diff --git a/packages/components/src/api/bookmarks/GetBookmarksByUser.ts b/packages/components/src/api/bookmarks/GetBookmarksByUser.ts new file mode 100644 index 0000000000..21bce70f12 --- /dev/null +++ b/packages/components/src/api/bookmarks/GetBookmarksByUser.ts @@ -0,0 +1,19 @@ +import MakeRequest from "../MakeRequest"; +import { Pagination } from "../_common/SharedFetchTypes"; + +export interface UserBookmarksListRequest {} + +export interface UserBookmarksListResponse { + // pagination: Pagination, + success: boolean, + // weights: any +} + +export interface UserBookmarksListQueries { + page_index: number, +} + +export const GetBookmarksByUser = MakeRequest({ + method: "GET", + routingFunction: (userToken: string) => `/v1/user_bookmarks/list/user/${userToken}`, +}); \ No newline at end of file diff --git a/packages/components/src/api/media_files/ListMediaFiles.ts b/packages/components/src/api/media_files/ListMediaFiles.ts new file mode 100644 index 0000000000..4b9e262a7b --- /dev/null +++ b/packages/components/src/api/media_files/ListMediaFiles.ts @@ -0,0 +1,26 @@ +import MakeRequest from "../MakeRequest"; +import { MediaFile } from "./GetMedia"; +// import { MediaFileType } from "../_common/enums/MediaFileType"; +import { Pagination } from "../_common/SharedFetchTypes"; + +export interface ListMediaFilesRequest {} + +export interface ListMediaFilesResponse { + pagination: Pagination; + success: boolean; + results: MediaFile[]; +} + +export interface ListMediaFilesParams { + page_index: number; +} + +export const ListMediaFiles = MakeRequest< + string, + ListMediaFilesRequest, + ListMediaFilesResponse, + ListMediaFilesParams +>({ + method: "GET", + routingFunction: () => `/v1/media_files/list`, +}); diff --git a/packages/components/src/api/weights/GetWeightsByUser.ts b/packages/components/src/api/weights/GetWeightsByUser.ts new file mode 100644 index 0000000000..27a01af151 --- /dev/null +++ b/packages/components/src/api/weights/GetWeightsByUser.ts @@ -0,0 +1,19 @@ +import MakeRequest from "../MakeRequest"; +import { Pagination } from "../_common/SharedFetchTypes"; + +export interface UserWeightListRequest {} + +export interface UserWeightListResponse { + // pagination: Pagination, + success: boolean, + weights: any +} + +export interface UserWeightListQueries { + page_index: number, +} + +export const GetWeightsByUser = MakeRequest({ + method: "GET", + routingFunction: (userToken: string) => `/v1/weights/by_user/${userToken}`, +}); \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/Badge/Badge.scss b/packages/fakeyou.com/src/components/common/Badge/Badge.scss index 81c7b48b3d..dac8b23f8a 100644 --- a/packages/fakeyou.com/src/components/common/Badge/Badge.scss +++ b/packages/fakeyou.com/src/components/common/Badge/Badge.scss @@ -18,6 +18,12 @@ &.shadow { box-shadow: 0 0 12px rgba($bg, 0.4); } + + &.badge-small { + font-size: 11px; + padding: 1px 6px; + border-radius: 0.3rem; + } } @each $color, $value in $badge-colors { diff --git a/packages/fakeyou.com/src/components/common/Badge/Badge.tsx b/packages/fakeyou.com/src/components/common/Badge/Badge.tsx index 7458e7a3b5..a9fdf1d1fe 100644 --- a/packages/fakeyou.com/src/components/common/Badge/Badge.tsx +++ b/packages/fakeyou.com/src/components/common/Badge/Badge.tsx @@ -6,6 +6,7 @@ interface BadgeProps { color: string; overlay?: boolean; className?: string; + small?: boolean; } export default function Badge({ @@ -13,10 +14,11 @@ export default function Badge({ color, overlay = false, className = "", + small = false, }: BadgeProps) { const badgeClass = `fy-badge badge-${color} ${ overlay ? "shadow" : "" - } mb-0 ${className}`.trim(); + } mb-0 ${className} ${small ? "badge-small" : ""}`.trim(); return {label}; } diff --git a/packages/fakeyou.com/src/components/common/Button/Button.scss b/packages/fakeyou.com/src/components/common/Button/Button.scss index 6f1a478b75..60d0a00c0c 100644 --- a/packages/fakeyou.com/src/components/common/Button/Button.scss +++ b/packages/fakeyou.com/src/components/common/Button/Button.scss @@ -108,6 +108,18 @@ } } +.card .button-link { + padding: 4px 8px; + font-size: 13px !important; + color: $white !important; + background-color: $primary; + border-radius: 0.5rem; + + &:hover { + background-color: lighten($primary, 5%); + } +} + /* Disabled styles */ .btn-primary:disabled, .btn-secondary:disabled, diff --git a/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx b/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx index 69fbc6d1e5..7de27ff126 100644 --- a/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx +++ b/packages/fakeyou.com/src/components/common/Card/AudioCard/AudioCard.tsx @@ -6,17 +6,26 @@ import useTimeAgo from "hooks/useTimeAgo"; import Badge from "components/common/Badge"; import LikeButton from "components/common/LikeButton"; import FavoriteButton from "components/common/FavoriteButton"; +import CreatorName from "../CreatorName"; +import { faArrowRight } from "@fortawesome/pro-solid-svg-icons"; import Button from "components/common/Button"; interface AudioCardProps { data: any; type: "media" | "weights"; + showCreator?: boolean; + showCover?: boolean; } -export default function AudioCard({ data, type }: AudioCardProps) { +export default function AudioCard({ + data, + type, + showCreator, + showCover, +}: AudioCardProps) { const history = useHistory(); - // console.log("🐙",data); + // console.log("🐙",data, data.public_bucket_path); const handleCardClick = () => { if (type === "media") { @@ -30,11 +39,11 @@ export default function AudioCard({ data, type }: AudioCardProps) { event.stopPropagation(); }; - const handleUseModel = () => { - if (type === "weights") { - history.push(`/weight/${data.token}`); - } - }; + // const handleUseModel = () => { + // if (type === "weights") { + // history.push(`/weights/${data.token}`); + // } + // }; const timeAgo = useTimeAgo(data.created_at); @@ -48,7 +57,7 @@ export default function AudioCard({ data, type }: AudioCardProps) { <>
-
+
@@ -63,39 +72,96 @@ export default function AudioCard({ data, type }: AudioCardProps) {
-
- +
+ +
+ {showCreator && ( +
+ +
+ )} + +
+ +
)} {type === "weights" && ( <> -
-
-
- +
+ {showCover && ( +
+ {/* replace source image with api data */} + Cover
-
- +
+
+ +
+
-
-
-
-
- {data.weight_name} -
-

{timeAgo}

-
-
-
+ +
+ +
+ {showCreator && ( +
+ +
+ )} + +
+ +
+ +
)} diff --git a/packages/fakeyou.com/src/components/common/Card/Card.scss b/packages/fakeyou.com/src/components/common/Card/Card.scss index d5c08790b7..a9b3262b73 100644 --- a/packages/fakeyou.com/src/components/common/Card/Card.scss +++ b/packages/fakeyou.com/src/components/common/Card/Card.scss @@ -82,4 +82,33 @@ color: rgba($white, 0.8); } } + + .cover-img { + height: 110px; + width: 110px; + min-width: 110px; + overflow: hidden; + border-radius: 0.5rem; + border: 2px solid rgba($white, 0.3); + margin-right: 12px; + position: relative; + + img { + width: 100%; + object-fit: cover; + pointer-events: none; + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); + } + } + + h6 { + //2 lines of text then ellipsis + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } } diff --git a/packages/fakeyou.com/src/components/common/Card/CreatorName.tsx b/packages/fakeyou.com/src/components/common/Card/CreatorName.tsx new file mode 100644 index 0000000000..bfae8ee190 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Card/CreatorName.tsx @@ -0,0 +1,45 @@ +import { Gravatar } from "@storyteller/components/src/elements/Gravatar"; +import React from "react"; +import { Link } from "react-router-dom"; + +interface CreatorNameProps { + displayName: string; + username: string; + gravatarHash: string; + avatarIndex: number; + backgroundIndex: number; + className?: string; +} + +const handleInnerClick = (event: any) => { + event.stopPropagation(); +}; + +export default function CreatorName({ + displayName, + gravatarHash, + avatarIndex, + backgroundIndex, + username, + className, +}: CreatorNameProps) { + return ( +
+ + + {displayName} + +
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx b/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx index fa855ce3be..92ee2206cf 100644 --- a/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx +++ b/packages/fakeyou.com/src/components/common/Card/ImageCard/ImageCard.tsx @@ -4,15 +4,18 @@ import Card from "../Card"; import useTimeAgo from "hooks/useTimeAgo"; import Badge from "components/common/Badge"; import LikeButton from "components/common/LikeButton"; -import Button from "components/common/Button"; import FavoriteButton from "components/common/FavoriteButton"; +import CreatorName from "../CreatorName"; +import Button from "components/common/Button"; +import { faArrowRight } from "@fortawesome/pro-solid-svg-icons"; interface ImageCardProps { data: any; type: "media" | "weights"; + showCreator?: boolean; } -export default function ImageCard({ data, type }: ImageCardProps) { +export default function ImageCard({ data, type, showCreator }: ImageCardProps) { const history = useHistory(); const handleCardClick = () => { @@ -55,12 +58,32 @@ export default function ImageCard({ data, type }: ImageCardProps) {

{timeAgo}

-
- + +
+ +
+ {showCreator && ( +
+ +
+ )} + +
+ +
@@ -76,18 +99,18 @@ export default function ImageCard({ data, type }: ImageCardProps) { />
-
-
- -
+
@@ -96,11 +119,39 @@ export default function ImageCard({ data, type }: ImageCardProps) {
{data.weight_name}
-

{timeAgo}

+

{timeAgo}

-
-
+ +
+ +
+ {showCreator && ( +
+ +
+ )} + +
+
+
diff --git a/packages/fakeyou.com/src/components/common/Card/VideoCard/VideoCard.tsx b/packages/fakeyou.com/src/components/common/Card/VideoCard/VideoCard.tsx index b48ab49d0f..67ab65b1d2 100644 --- a/packages/fakeyou.com/src/components/common/Card/VideoCard/VideoCard.tsx +++ b/packages/fakeyou.com/src/components/common/Card/VideoCard/VideoCard.tsx @@ -3,16 +3,19 @@ import { useHistory } from "react-router-dom"; import Card from "../Card"; import useTimeAgo from "hooks/useTimeAgo"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faPlayCircle } from "@fortawesome/pro-solid-svg-icons"; +import { faArrowRight, faPlayCircle } from "@fortawesome/pro-solid-svg-icons"; import Badge from "components/common/Badge"; import LikeButton from "components/common/LikeButton"; +import Button from "components/common/Button"; +import CreatorName from "../CreatorName"; interface VideoCardProps { data: any; type: "media" | "weights"; + showCreator?: boolean; } -export default function VideoCard({ data, type }: VideoCardProps) { +export default function VideoCard({ data, type, showCreator }: VideoCardProps) { const history = useHistory(); const handleCardClick = () => { @@ -53,12 +56,30 @@ export default function VideoCard({ data, type }: VideoCardProps) { {data.weight_name}

{timeAgo}

-
- +
+
+ {showCreator && ( +
+ +
+ )} + +
+ +
@@ -80,6 +101,14 @@ export default function VideoCard({ data, type }: VideoCardProps) {
+
diff --git a/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.scss b/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.scss index 4994731213..06fbe59fa1 100644 --- a/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.scss +++ b/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.scss @@ -29,7 +29,7 @@ background: rgba(lighten($secondary, 10%), 0.9); } - &:hover .favorite-number { + &:hover .favorite-text { opacity: 1; } @@ -69,7 +69,7 @@ color: map-get($badge-colors, "yellow"); } -.favorite-number { +.favorite-text { color: $white; font-size: 14px; font-weight: 400 !important; diff --git a/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.tsx b/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.tsx index 1a16470ada..adcbbbebf7 100644 --- a/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.tsx +++ b/packages/fakeyou.com/src/components/common/FavoriteButton/FavoriteButton.tsx @@ -5,12 +5,12 @@ import { faBookmark as faBookmarkOutline } from "@fortawesome/pro-regular-svg-ic import Tippy from "@tippyjs/react"; import "tippy.js/dist/tippy.css"; import "./FavoriteButton.scss"; -import useShortenNumber from "hooks/useShortenNumber"; +// import useShortenNumber from "hooks/useShortenNumber"; interface FavoriteButtonProps { initialToggled?: boolean; onToggle: (toggled: boolean) => Promise; - favoriteCount: number; + favoriteCount?: number; overlay?: boolean; large?: boolean; } @@ -41,7 +41,7 @@ export default function FavoriteButton({ const buttonShadow = overlay ? "shadow" : ""; const iconClass = isToggled ? "icon-toggled" : "icon-default"; const toolTip = isToggled ? "Unbookmark" : "Bookmark"; - let favoriteCountShort = useShortenNumber(favoriteCount); + // let favoriteCountShort = useShortenNumber(favoriteCount || 0); return (
@@ -52,6 +52,7 @@ export default function FavoriteButton({ trigger="mouseenter" delay={[500, 0]} offset={[0, 12]} + placement="bottom" >
diff --git a/packages/fakeyou.com/src/components/common/Input/Input.tsx b/packages/fakeyou.com/src/components/common/Input/Input.tsx index 4697f07970..edfaef74c0 100644 --- a/packages/fakeyou.com/src/components/common/Input/Input.tsx +++ b/packages/fakeyou.com/src/components/common/Input/Input.tsx @@ -6,11 +6,18 @@ interface InputProps extends React.InputHTMLAttributes { icon?: IconDefinition; label?: string; textArea?: boolean; + className?: string; } -export default function Input({ label, icon, textArea, ...rest }: InputProps) { +export default function Input({ + label, + icon, + textArea, + className, + ...rest +}: InputProps) { return ( -
+
{label && }
diff --git a/packages/fakeyou.com/src/components/common/MasonryGrid/MasonryGrid.tsx b/packages/fakeyou.com/src/components/common/MasonryGrid/MasonryGrid.tsx index df2b4e27a7..0e2fb19312 100644 --- a/packages/fakeyou.com/src/components/common/MasonryGrid/MasonryGrid.tsx +++ b/packages/fakeyou.com/src/components/common/MasonryGrid/MasonryGrid.tsx @@ -27,6 +27,7 @@ export default function MasonryGrid({ masonryInstance = new Masonry(gridRef.current, { itemSelector: ".grid-item", percentPosition: true, + transitionDuration: 0, }); //Needs this or images will overflow masonry card. It checks if image is loaded then after it is loaded, it resizes the card. diff --git a/packages/fakeyou.com/src/components/common/SearchBar/SearchBar.scss b/packages/fakeyou.com/src/components/common/SearchBar/SearchBar.scss new file mode 100644 index 0000000000..bf9c42fc6d --- /dev/null +++ b/packages/fakeyou.com/src/components/common/SearchBar/SearchBar.scss @@ -0,0 +1,101 @@ +@import "scss/colours.scss"; + +.search-bar-container { + display: flex; + justify-content: center; + width: 580px; +} + +.search-field-group { + position: relative; + width: 100%; +} + +.search-field { + .form-control { + padding-top: 7.25px !important; + padding-bottom: 7.25px !important; + border-radius: 0.5rem 0 0 0.5rem !important; + position: relative; + z-index: 3; + + &::placeholder { + color: rgba($white, 0.3) !important; + } + } +} + +.search-results-dropdown { + position: absolute; + border-color: lighten($panel-inner, 6%) !important; + border: 1px solid; + top: 46px; + width: 100%; + z-index: 4; + border-radius: 0.5rem; + overflow: hidden; + + .search-results-dropdown-item { + background-color: $panel-inner; + border-bottom: 1px solid lighten($panel-inner, 6%); + transition: 0.05s; + + &:hover { + background-color: lighten($panel-inner, 6%); + cursor: pointer; + } + + &:last-child { + border-bottom: none; + } + } + + .view-more { + display: flex; + justify-content: center; + color: $primary; + font-size: 15px; + font-weight: 500; + } +} + +.search-bar-button { + border-radius: 0 0.5rem 0.5rem 0 !important; + border-color: lighten($secondary, 5%) !important; + background-color: lighten($secondary, 5%) !important; + + &:hover { + border-color: lighten($secondary, 10%) !important; + background-color: lighten($secondary, 10%) !important; + } +} + +.no-results { + text-align: center; + color: rgba($white, 0.5); + font-size: 15px; + + &:hover { + background-color: $panel-inner !important; + cursor: default !important; + } +} + +.loading-results { + &:hover { + background-color: $panel-inner !important; + cursor: default !important; + } +} + +@media (max-width: 992px) { + .search-bar-container { + width: 100%; + } + + .search-field { + .form-control { + border-radius: 0.5rem !important; + } + } +} diff --git a/packages/fakeyou.com/src/components/common/SearchBar/SearchBar.tsx b/packages/fakeyou.com/src/components/common/SearchBar/SearchBar.tsx new file mode 100644 index 0000000000..8dc0ee14c2 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/SearchBar/SearchBar.tsx @@ -0,0 +1,125 @@ +import React, { useCallback, useEffect, useState } from "react"; +import Button from "../Button"; +import { faSearch } from "@fortawesome/pro-solid-svg-icons"; +import { + SearchTtsModels, + TtsModel, +} from "@storyteller/components/src/api/tts/SearchTtsModels"; +import SearchResultsDropdown from "./SearchResultsDropdown"; +import SearchField from "./SearchField"; +import "./SearchBar.scss"; +import { useHistory, useLocation } from "react-router-dom"; +import { useSearch } from "context/SearchContext"; + +interface SearchBarProps { + autoFocus?: boolean; + onBlur?: () => void; + onFocus?: () => void; + isFocused?: boolean; +} + +export default function SearchBar({ + autoFocus, + onBlur, + onFocus, + isFocused, +}: SearchBarProps) { + let history = useHistory(); + let location = useLocation(); + + const { searchTerm, setSearchTerm } = useSearch(); + const [foundTtsModels, setFoundTtsModels] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const isOnSearchPage = location.pathname.startsWith("/search"); + + const maybeSearch = useCallback( + async (value: string) => { + setSearchTerm(value); + }, + [setSearchTerm] + ); + + const doSearch = useCallback( + async (value: string) => { + let request = { + search_term: value, + }; + + setIsLoading(true); + + let response = await SearchTtsModels(request); + + if (response.success) { + let models = [...response.models]; + setFoundTtsModels(models); + } else { + setFoundTtsModels([]); + } + + setIsLoading(false); + }, + [setFoundTtsModels] + ); + + useEffect(() => { + if (isOnSearchPage) { + const query = new URLSearchParams(location.search).get("query"); + if (query) { + setSearchTerm(query); + } + } + }, [isOnSearchPage, location.search, setSearchTerm]); + + useEffect(() => { + if (isOnSearchPage) { + history.push(`/search/weights?query=${encodeURIComponent(searchTerm)}`); + } else { + doSearch(searchTerm); + } + }, [searchTerm, history, location.pathname, doSearch, isOnSearchPage]); + + const handleSearchButtonClick = useCallback(() => { + if (searchTerm !== "") { + history.push(`/search/weights?query=${encodeURIComponent(searchTerm)}`); + } + }, [searchTerm, history]); + + const handleKeyPress = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleSearchButtonClick(); + } + }, + [handleSearchButtonClick] + ); + + return ( +
+
+ + {isFocused && !isOnSearchPage && ( + + )} +
+ +
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/SearchBar/SearchField.tsx b/packages/fakeyou.com/src/components/common/SearchBar/SearchField.tsx new file mode 100644 index 0000000000..832b01f30b --- /dev/null +++ b/packages/fakeyou.com/src/components/common/SearchBar/SearchField.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import Input from "../Input"; + +interface SearchFieldProps { + value: string; + onChange: (value: string) => void; + onFocus?: () => void; + onBlur?: () => void; + autoFocus?: boolean; + onKeyPress?: (event: React.KeyboardEvent) => void; +} + +export default function SearchField({ + value, + onChange, + onFocus, + onBlur, + onKeyPress, + autoFocus = false, +}: SearchFieldProps) { + return ( + onChange(e.target.value)} + placeholder="Search for a model weight" + className="search-field" + onFocus={onFocus} + onBlur={onBlur} + autoFocus={autoFocus} + onKeyPress={onKeyPress} + /> + ); +} diff --git a/packages/fakeyou.com/src/components/common/SearchBar/SearchResultsDropdown.tsx b/packages/fakeyou.com/src/components/common/SearchBar/SearchResultsDropdown.tsx new file mode 100644 index 0000000000..93bc44cf6f --- /dev/null +++ b/packages/fakeyou.com/src/components/common/SearchBar/SearchResultsDropdown.tsx @@ -0,0 +1,99 @@ +import { TtsModel } from "@storyteller/components/src/api/tts/SearchTtsModels"; +import React from "react"; +import { Link, useHistory } from "react-router-dom"; +import Badge from "../Badge"; + +interface SearchResultsDropdownProps { + data: TtsModel[]; + isNoResults?: boolean; + isLoading?: boolean; + searchTerm?: string; +} + +export default function SearchResultsDropdown({ + data, + isNoResults, + isLoading, + searchTerm, +}: SearchResultsDropdownProps) { + const history = useHistory(); + + const handleResultClick = (item: TtsModel, event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + history.push(`/weight/${item.model_token}`); + }; + + const handleInnerClick = (event: any) => { + event.stopPropagation(); + }; + + const handleUrlPush = (event: React.MouseEvent) => { + event.stopPropagation(); + if (searchTerm) { + history.push(`/search/weights?query=${encodeURIComponent(searchTerm)}`); + } + }; + + return ( + <> + {data.length !== 0 && ( +
+ {data.map(item => { + // let modelPageLink = `/weight/${item.model_token}`; + + return ( +
handleResultClick(item, event)} + > +
{item.title}
+
+

+ by{" "} + + {item.creator_display_name} + +

+ +
+
+ ); + })} +
+ View more results +
+
+ )} + {data.length === 0 && !isLoading && isNoResults && ( +
+
+ No results found +
+
+ )} + {isLoading && isNoResults && ( +
+
+
+
+ Loading... +
+
+
+
+ )} + + ); +} diff --git a/packages/fakeyou.com/src/components/common/SearchBar/index.ts b/packages/fakeyou.com/src/components/common/SearchBar/index.ts new file mode 100644 index 0000000000..f3cedd3e32 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/SearchBar/index.ts @@ -0,0 +1 @@ +export { default } from "./SearchBar"; diff --git a/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.scss b/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.scss index 2dfcfa6570..3e5e88ed60 100644 --- a/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.scss +++ b/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.scss @@ -1,9 +1,5 @@ @import "scss/colours.scss"; -.mobile-menu-container { - padding-bottom: 4rem; -} - .mobile-menu { z-index: 100; position: fixed; diff --git a/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.tsx b/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.tsx index 05c166a4fc..099804e831 100644 --- a/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.tsx +++ b/packages/fakeyou.com/src/components/layout/MobileMenu/MobileMenu.tsx @@ -27,7 +27,11 @@ export default function MobileMenu(props: MobileMenuProps) { let signupOrProfileButton = ( <> -
+
+ ); } diff --git a/packages/fakeyou.com/src/components/layout/TopNav/TopNav.tsx b/packages/fakeyou.com/src/components/layout/TopNav/TopNav.tsx new file mode 100644 index 0000000000..0dc99f00a8 --- /dev/null +++ b/packages/fakeyou.com/src/components/layout/TopNav/TopNav.tsx @@ -0,0 +1,258 @@ +import { + faBars, + faSearch, + faSignOutAlt, + faUser, + faXmark, +} from "@fortawesome/pro-solid-svg-icons"; +import { Button } from "components/common"; +import SearchBar from "components/common/SearchBar"; +import React, { useEffect, useState } from "react"; +import { Link, useHistory } from "react-router-dom"; +import { WebUrl } from "common/WebUrl"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { Logout } from "@storyteller/components/src/api/session/Logout"; + +interface TopNavProps { + sessionWrapper: SessionWrapper; + logoutHandler: () => void; + querySessionCallback: () => void; + querySessionSubscriptionsCallback: () => void; +} + +export default function TopNav({ + sessionWrapper, + querySessionCallback, + querySessionSubscriptionsCallback, +}: TopNavProps) { + let history = useHistory(); + const [isMobileSearchBarVisible, setIsMobileSearchBarVisible] = + useState(false); + const [isFocused, setIsFocused] = useState(false); + const wrapper = document.getElementById("wrapper"); + const [menuButtonIcon, setMenuButtonIcon] = useState(faBars); + + const handleMenuButtonClick = () => { + if (window.innerWidth < 1200) { + if (wrapper) { + wrapper.classList.toggle("toggled"); + if (menuButtonIcon === faBars) { + setMenuButtonIcon(faXmark); + } else { + setMenuButtonIcon(faBars); + } + } + } + }; + + const handleSearchButtonClick = () => { + setIsMobileSearchBarVisible(true); + setMenuButtonIcon(faBars); + if (window.innerWidth < 1200) { + if (wrapper) { + wrapper.classList.remove("toggled"); + } + } + }; + + const onFocusHandler = () => { + setIsFocused(true); + }; + + const onBlurHandler = () => { + // Search field blur/Unfocusing hack: needs a little bit of delay for the result click event to register + setTimeout(() => { + setIsFocused(false); + + if (isMobileSearchBarVisible) { + setIsMobileSearchBarVisible(false); + } + }, 100); + }; + + useEffect(() => { + const handleMenuToggle = (event: any) => { + setMenuButtonIcon(event.detail.isOpen ? faXmark : faBars); + }; + + window.addEventListener("menuToggle", handleMenuToggle); + + return () => { + window.removeEventListener("menuToggle", handleMenuToggle); + }; + }, []); + + const logoutHandler = async () => { + await Logout(); + querySessionCallback(); + querySessionSubscriptionsCallback(); + // PosthogClient.reset(); + // Analytics.accountLogout(); + history.push("/"); + }; + + const loggedIn = sessionWrapper.isLoggedIn(); + + let userOrLoginButton = ( + <> +
+
+
+ + {/* Mobile Searchbar */} + {isMobileSearchBarVisible && ( +
+
+ + +
+
+ )} +
+ ); +} diff --git a/packages/fakeyou.com/src/context/SearchContext.tsx b/packages/fakeyou.com/src/context/SearchContext.tsx new file mode 100644 index 0000000000..c4cdd9a787 --- /dev/null +++ b/packages/fakeyou.com/src/context/SearchContext.tsx @@ -0,0 +1,23 @@ +import React, { createContext, useContext, useState } from "react"; + +interface SearchContextType { + searchTerm: string; + setSearchTerm: (value: string) => void; +} + +const SearchContext = createContext({ + searchTerm: "", + setSearchTerm: () => {}, +}); + +export const useSearch = () => useContext(SearchContext); + +export const SearchProvider: React.FC = ({ children }) => { + const [searchTerm, setSearchTerm] = useState(""); + + return ( + + {children} + + ); +}; diff --git a/packages/fakeyou.com/src/context/index.ts b/packages/fakeyou.com/src/context/index.ts index 104d13cde7..cfd97debb2 100644 --- a/packages/fakeyou.com/src/context/index.ts +++ b/packages/fakeyou.com/src/context/index.ts @@ -1,4 +1,4 @@ import InferenceJobsContext from "./InferenceJobsContext"; import SessionContext from "./SessionContext"; -export { InferenceJobsContext, SessionContext }; \ No newline at end of file +export { InferenceJobsContext, SessionContext }; diff --git a/packages/fakeyou.com/src/hooks/useListContent/useListContent.tsx b/packages/fakeyou.com/src/hooks/useListContent/useListContent.tsx index 3e65f234d5..59ce127c1e 100644 --- a/packages/fakeyou.com/src/hooks/useListContent/useListContent.tsx +++ b/packages/fakeyou.com/src/hooks/useListContent/useListContent.tsx @@ -1,15 +1,25 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState } from "react"; import { useSession } from "hooks"; interface Props { + addQueries?: any; fetcher: any; list: any; listSet: any; pagePreset?: number; requestList?: boolean; + isInfiniteScroll?: boolean; } -export default function useListContent({ fetcher, list, listSet, pagePreset = 0, requestList = false }: Props) { +export default function useListContent({ + addQueries, + fetcher, + list, + listSet, + pagePreset = 0, + requestList = false, + isInfiniteScroll = false, +}: Props) { const { user } = useSession(); const [filter, filterSet] = useState("all"); const [page, pageSet] = useState(pagePreset); @@ -19,12 +29,17 @@ export default function useListContent({ fetcher, list, listSet, pagePreset = 0, const pageChange = (page: number) => { pageSet(page); + if (!isInfiniteScroll || page === 1) { + listSet([]); // Reset list if not in infinite scroll mode or starting from first page + } statusSet(1); }; - const onChange = ({ target }: { target: { name: string, value: any} }) => { - const todo: { [ key: string]: (x: any) => void } = { filterSet, sortSet }; + const onChange = ({ target }: { target: { name: string; value: any } }) => { + const todo: { [key: string]: (x: any) => void } = { filterSet, sortSet }; todo[target.name + "Set"](target.value); + pageSet(1); // Reset to first page on filter/sort change + listSet([]); // Reset list on filter/sort change statusSet(1); }; @@ -32,21 +47,40 @@ export default function useListContent({ fetcher, list, listSet, pagePreset = 0, if (user && user.username) { if (status === 1) { statusSet(2); - fetcher(user.username, {}, { - page_index: page, - ...filter !== "all" ? { filter_media_type: filter } : {}, - ...sort !== "newest" ? { sort_ascending: true } : {}, - }).then((res: any) => { + fetcher( + user.username, + {}, + { + page_index: page, + ...addQueries, // eventually we should provide a way to type this ... or not. It works + ...(filter !== "all" ? { filter_media_type: filter } : {}), + ...(sort !== "newest" ? { sort_ascending: true } : {}), + } + ).then((res: any) => { statusSet(3); - console.log("🧶",res); + console.log("🧶", res); if (res.results && res.pagination) { - pageCountSet(res.pagination.total_page_count) - listSet(res.results); + pageCountSet(res.pagination.total_page_count); + if (isInfiniteScroll && page !== 1) { + listSet((prevList: any) => [...prevList, ...res.results]); // Append to list for infinite scroll + } else { + listSet(res.results); // Replace list for regular pagination + } } }); } } - }, [fetcher, filter, listSet, page, user, sort, status]); + }, [ + addQueries, + fetcher, + filter, + listSet, + page, + user, + sort, + status, + isInfiniteScroll, + ]); return { filter, @@ -61,6 +95,6 @@ export default function useListContent({ fetcher, list, listSet, pagePreset = 0, sort, sortSet, status, - statusSet + statusSet, }; -}; \ No newline at end of file +} diff --git a/packages/fakeyou.com/src/scss/nav.scss b/packages/fakeyou.com/src/scss/nav.scss index 988c6a3228..f42b1bf41a 100644 --- a/packages/fakeyou.com/src/scss/nav.scss +++ b/packages/fakeyou.com/src/scss/nav.scss @@ -10,22 +10,23 @@ transition: all 0.2s ease; } -#wrapper.toggled { - padding-left: 260px; -} +$topbar-height: 66px; #sidebar-wrapper { z-index: 1000; position: fixed; left: 260px; width: 0; - height: 100%; + height: calc(100% - $topbar-height); margin-left: -260px; overflow-x: hidden; -webkit-transition: all 0.2s ease; -moz-transition: all 0.2s ease; -o-transition: all 0.2s ease; transition: all 0.2s ease; + margin-top: $topbar-height; + display: grid; + grid-template-rows: 1fr auto; &::-webkit-scrollbar { width: 0; @@ -47,43 +48,36 @@ #wrapper.toggled #sidebar-wrapper { width: 260px; + margin-left: 0; } #page-content-wrapper { width: 100%; position: relative; -} - -#wrapper.toggled #page-content-wrapper { - position: absolute; - margin-right: -260px; + padding-left: 260px; + padding-top: $topbar-height; } /* Sidebar Styles */ .sidebar-nav { - position: absolute; - top: 0; width: 260px; margin: 0; padding: 0; list-style: none; - min-height: 100vh; + height: 100%; background: $panel; border-color: rgba(255, 255, 255, 0.0705882353) !important; border-right: 1px solid; + padding-top: 1rem; } -// .sidebar-nav li { -// text-indent: 2rem; -// line-height: 38px; -// } - .sidebar-nav li a { display: block; text-decoration: none; color: rgba($white, 0.7); font-weight: 500; + font-size: 15px; margin: 0rem 1rem; padding: 0.5rem 1rem; border-radius: 0.5rem; @@ -137,12 +131,12 @@ right: 0; bottom: 0; left: 0; - background-color: rgba(0, 0, 0, 0.5); /* Semi-black background */ - z-index: 900; /* Appear on top of the content */ + background-color: rgba(0, 0, 0, 0.5); + z-index: 900; } .toggled #page-content-wrapper { position: relative; - z-index: 2; /* Ensure content is above the overlay */ + z-index: 2; } .toggled #overlay { display: block; @@ -153,16 +147,91 @@ } .sidebar { - transform: translateX( - -100% - ); /* move the sidebar out of the screen to the left */ - transition: transform 0.3s ease-out; /* transition effect */ - /* other necessary styles for the sidebar */ + transform: translateX(-100%); + transition: transform 0.3s ease-out; } /* Visible state for the sidebar */ .sidebar.visible { - transform: translateX(0); /* sidebar occupies its regular position */ + transform: translateX(0); +} + +#topbar-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: $topbar-height; + background: $panel; + border-bottom: 1px solid rgba(255, 255, 255, 0.0705882353) !important; + z-index: 1000; +} + +.topbar-nav { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: center; + height: 100%; + padding: 0 1rem; + justify-content: space-between; + align-items: center; + + .topbar-nav-right { + display: flex; + justify-content: flex-end; + } + + .topbar-nav-right { + display: flex; + justify-content: flex-end; + } + + .topbar-nav-center { + grid-column: 2; + } +} + +.topbar-mobile-search-bar-container { + position: absolute; + width: 100%; + z-index: 1000; + top: 0; + height: $topbar-height; + background-color: $panel; + + .topbar-mobile-search-bar { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 100%; + padding: 0 1rem; + + .close-search-button { + position: absolute; + top: 0; + right: 0; + margin: 0 1.8rem; + color: $white; + height: 100%; + font-size: 1.5rem; + cursor: pointer; + z-index: 4; + background: none; + border: none; + padding: 0; + } + } +} + +.mobile-fixed-bottom { + position: sticky; + bottom: 0; + padding: 1.5rem 0rem; + width: 100%; + background-color: $panel-inner; + border-top: 1px solid lighten($panel-inner, 5%) !important; + font-size: 13px; } @media (min-width: 992px) { @@ -191,3 +260,18 @@ margin-right: 0; } } + +@media (max-width: 992px) { + #wrapper.toggled #sidebar-wrapper { + width: 100%; + } + + #sidebar-wrapper { + left: 100%; + margin-left: 0; + } + + .sidebar-nav { + width: 100%; + } +} diff --git a/packages/fakeyou.com/src/scss/site-refresh.scss b/packages/fakeyou.com/src/scss/site-refresh.scss index 5858328eea..ac0839aba3 100644 --- a/packages/fakeyou.com/src/scss/site-refresh.scss +++ b/packages/fakeyou.com/src/scss/site-refresh.scss @@ -18,13 +18,17 @@ styles with important tags: } ::-webkit-scrollbar-track { - background-color: rgba($bg, 1) !important; + background-color: $panel !important; } ::-webkit-scrollbar { width: 10px; } + body { + overflow-y: scroll; + } + p { font-weight: 400; margin-bottom: 0; diff --git a/packages/fakeyou.com/src/v2/view/PageContainer.tsx b/packages/fakeyou.com/src/v2/view/PageContainer.tsx index b96d95e7d7..13fffd263d 100644 --- a/packages/fakeyou.com/src/v2/view/PageContainer.tsx +++ b/packages/fakeyou.com/src/v2/view/PageContainer.tsx @@ -84,9 +84,9 @@ import { GenerateSpeechPage } from "./pages/generate_speech/GenerateSpeechPage"; import VcModelViewPage from "./pages/vc/vc_model_view/VcModelViewPage"; import VcModelEditPage from "./pages/vc/vc_model_edit/VcModelEditPage"; import VcModelDeletePage from "./pages/vc/vc_model_delete/VcModelDeletePage"; +import TopNav from "components/layout/TopNav/TopNav"; import SideNav from "components/layout/SideNav/SideNav"; import MobileMenu from "components/layout/MobileMenu/MobileMenu"; -import { TopNav } from "./nav/TopNav"; import TtsModelSearchPage from "./pages/tts/tts_model_search/TtsModelSearchPage"; import MediaPage from "./pages/media/MediaPage"; import { VoiceDesignerFormPage } from "./pages/voice_designer/VoiceDesignerFormPage"; @@ -99,6 +99,9 @@ import { NewProfilePage } from "./pages/profile/profile_view/NewProfilePage"; import { ProfilePage } from "./pages/profile/profile_view/ProfilePage"; import { ModerationJobControlPage } from "./pages/moderation/job_control/ModerationJobControlPage"; import WeightPage from "./pages/weight/WeightPage"; +import ExplorePage from "./pages/explore/ExplorePage"; +import SearchPage from "./search/SearchPage"; +import { SearchProvider } from "context/SearchContext"; interface Props { sessionWrapper: SessionWrapper; @@ -207,651 +210,671 @@ class PageContainer extends React.Component< logout = () => {}; public render() { - const { location } = this.props; - - const shouldRenderNavs = location.pathname === "/"; - return ( -
-
- + +
+
- {shouldRenderNavs && ( + - )} -
- - - - - +
+ + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - {/* New Profile Page */} - - - + {/* New Profile Page */} + + + - - {}} - sessionWrapper={this.props.sessionWrapper} - /> - + + {}} + sessionWrapper={this.props.sessionWrapper} + /> + - - - + + + - - {}} - sessionWrapper={this.props.sessionWrapper} - /> - - - {}} - sessionWrapper={this.props.sessionWrapper} - /> - - - {}} - sessionWrapper={this.props.sessionWrapper} - /> - + + {}} + sessionWrapper={this.props.sessionWrapper} + /> + + + {}} + sessionWrapper={this.props.sessionWrapper} + /> + + + {}} + sessionWrapper={this.props.sessionWrapper} + /> + - - - + + + - {/* + + + + + {/* */} - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - {/* Route for initial voice creation */} - - - + + + - {/* Route for editing the dataset details */} - - - + {/* Route for initial voice creation */} + + + - {/* Route for handling dataset token for uploading samples */} - - - + {/* Route for editing the dataset details */} + + + - - - + {/* Route for handling dataset token for uploading samples */} + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + + + + + +
-
+
); } } diff --git a/packages/fakeyou.com/src/v2/view/pages/explore/ExplorePage.tsx b/packages/fakeyou.com/src/v2/view/pages/explore/ExplorePage.tsx new file mode 100644 index 0000000000..31165a4638 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/explore/ExplorePage.tsx @@ -0,0 +1,58 @@ +import { Panel } from "components/common"; +import Container from "components/common/Container"; +import Tabs from "components/common/Tabs"; +import PageHeader from "components/layout/PageHeader"; +import React from "react"; +import { + faFire, + faLayerGroup, + faPhotoFilmMusic, +} from "@fortawesome/pro-solid-svg-icons"; +import FeaturedTab from "./tabs/FeaturedTab"; +import WeightsTab from "./tabs/WeightsTab"; +import MediaTab from "./tabs/MediaTab"; +import { Redirect, useLocation } from "react-router-dom"; + +interface ExplorePageProps {} + +export default function ExplorePage(props: ExplorePageProps) { + const { pathname } = useLocation(); + + if (pathname === `/explore` || pathname === `/explore/`) { + return ; + } + + const tabs = [ + { + label: "Featured", + icon: faFire, + content: , + to: "/explore/featured", + padding: true, + }, + { + label: "Weights", + icon: faLayerGroup, + content: , + to: "/explore/weights", + padding: true, + }, + { + label: "Media", + icon: faPhotoFilmMusic, + content: , + to: "/explore/media", + padding: true, + }, + ]; + + return ( + + + + + + + + ); +} diff --git a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/FeaturedTab.tsx b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/FeaturedTab.tsx new file mode 100644 index 0000000000..8fd3efb6e7 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/FeaturedTab.tsx @@ -0,0 +1,104 @@ +import React, { useRef, useState } from "react"; +import MasonryGrid from "components/common/MasonryGrid/MasonryGrid"; +import mockWeightsData from "./mockWeightsData"; +import AudioCard from "components/common/Card/AudioCard"; +import SkeletonCard from "components/common/Card/SkeletonCard"; +import { Link } from "react-router-dom"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronRight } from "@fortawesome/pro-solid-svg-icons"; +import ImageCard from "components/common/Card/ImageCard"; + +export default function FeaturedTab() { + const gridContainerRef = useRef(null); + const [data] = useState(mockWeightsData); + const [isLoading] = useState(false); + + return ( +
+
+
+

+ Featured Voice Weights +

+ + View all + + +
+ + {isLoading ? ( +
+ {Array.from({ length: 6 }).map((_, index) => ( + + ))} +
+ ) : ( + console.log("Layout complete!")} + > + {data.map((data, index) => { + let card = ( + + ); + return ( +
+ {card} +
+ ); + })} +
+ )} +
+
+
+

+ Featured Image Weights +

+ + View all + + +
+ + {isLoading ? ( +
+ {Array.from({ length: 6 }).map((_, index) => ( + + ))} +
+ ) : ( + console.log("Layout complete!")} + > + {data.map((data, index) => { + let card = ( + + ); + return ( +
+ {card} +
+ ); + })} +
+ )} +
+
+ ); +} diff --git a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx new file mode 100644 index 0000000000..0745b8dd78 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/MediaTab.tsx @@ -0,0 +1,155 @@ +import React, { useCallback, useRef, useState } from "react"; +import MasonryGrid from "components/common/MasonryGrid/MasonryGrid"; +import AudioCard from "components/common/Card/AudioCard"; +import ImageCard from "components/common/Card/ImageCard"; +import VideoCard from "components/common/Card/VideoCard"; +import { TempSelect } from "components/common"; +import { + faArrowDownWideShort, + faFilter, +} from "@fortawesome/pro-solid-svg-icons"; +import AudioPlayerProvider from "components/common/AudioPlayer/AudioPlayerContext"; +import SkeletonCard from "components/common/Card/SkeletonCard"; +import { ListMediaFiles } from "@storyteller/components/src/api/media_files/ListMediaFiles"; +import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia"; +import { useListContent } from "hooks"; +import InfiniteScroll from "react-infinite-scroll-component"; + +export default function MediaTab() { + const gridContainerRef = useRef(null); + const [isLoading] = useState(false); + + const [list, listSet] = useState([]); + const media = useListContent({ + fetcher: ListMediaFiles, + list, + listSet, + pagePreset: 1, + requestList: true, + isInfiniteScroll: true, + }); + + // Fetch more data for infinite scroll + const fetchMoreData = useCallback(() => { + if (media.page < media.pageCount) { + media.pageChange(media.page + 1); + } + }, [media]); + + const filterOptions = [ + { value: "all", label: "All Media" }, + { value: "images", label: "Images" }, + { value: "audio", label: "Audio" }, + { value: "video", label: "Video" }, + ]; + + const sortOptions = [ + { value: "newest", label: "Newest" }, + { value: "oldest", label: "Oldest" }, + { value: "mostliked", label: "Most Liked" }, + ]; + + return ( + <> +
+
+ + +
+
+ + {isLoading ? ( +
+ {Array.from({ length: 6 }).map((_, index) => ( + + ))} +
+ ) : ( + +
+ Loading... +
+
+ } + endMessage={ +

No more results.

+ } + className="overflow-hidden" + > + console.log("Layout complete!")} + > + {media.list.map((data: MediaFile, index: number) => { + let card; + switch (data.media_type) { + case "audio": + card = ( + + ); + break; + case "image": + card = ( + + ); + break; + case "video": + card = ( + + ); + break; + default: + card =
Unsupported media type
; + } + return ( +
+ {card} +
+ ); + })} +
+ + )} + + + ); +} diff --git a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx new file mode 100644 index 0000000000..914d3bba73 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/WeightsTab.tsx @@ -0,0 +1,149 @@ +import React, { useRef, useState } from "react"; +import MasonryGrid from "components/common/MasonryGrid/MasonryGrid"; +import mockWeightsData from "./mockWeightsData"; +import AudioCard from "components/common/Card/AudioCard"; +import ImageCard from "components/common/Card/ImageCard"; +import VideoCard from "components/common/Card/VideoCard"; +import Select from "components/common/Select"; +import { + faArrowDownWideShort, + faFilter, +} from "@fortawesome/pro-solid-svg-icons"; +import SkeletonCard from "components/common/Card/SkeletonCard"; + +export default function WeightsTab() { + const gridContainerRef = useRef(null); + const [data] = useState(mockWeightsData); + const [isLoading] = useState(false); + const [selectedFilter, setSelectedFilter] = useState("all"); + + const filterOptions = [ + { value: "all", label: "All Weights" }, + { value: "tts", label: "Text to Speech" }, + { value: "vc", label: "Voice to Voice" }, + { value: "sd", label: "Image Generation" }, + ]; + + const sortOptions = [ + { value: "newest", label: "Newest" }, + { value: "oldest", label: "Oldest" }, + { value: "mostliked", label: "Most Bookmarked" }, + ]; + + const modelTtsOptions = [ + { value: "all", label: "All Types" }, + { value: "tt2", label: "Tacotron 2" }, + ]; + + const modelVcOptions = [ + { value: "all", label: "All Types" }, + { value: "rvc", label: "RVCv2" }, + { value: "svc", label: "SoVitsSvc" }, + ]; + + const modelSdOptions = [ + { value: "all", label: "All Types" }, + { value: "lora", label: "LoRA" }, + { value: "SD15", label: "SD 1.5" }, + { value: "SDXL", label: "SD XL" }, + ]; + + const handleFilterChange = (option: any) => { + const selectedOption = option as { value: string; label: string }; + setSelectedFilter(selectedOption.value); + }; + + return ( + <> +
+
+ + + {selectedFilter === "tts" && ( + + )} + {selectedFilter === "vc" && ( + +