From 0c4283fa691bec5a7f51d1ed3f11dcd30e438d1b Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 6 May 2024 14:17:23 -0700 Subject: [PATCH] Featured Media tab wrired up with lazy loading pagination, added media/weight category translation labels --- .../public/locales/en/EntityGeneral.json | 24 +- .../src/components/entities/EntityTypes.tsx | 97 +++--- .../view/pages/explore/tabs/FeaturedTab.tsx | 288 ++++++++++-------- 3 files changed, 244 insertions(+), 165 deletions(-) diff --git a/packages/fakeyou.com/public/locales/en/EntityGeneral.json b/packages/fakeyou.com/public/locales/en/EntityGeneral.json index 1800d1ac4b..a4fa438da5 100644 --- a/packages/fakeyou.com/public/locales/en/EntityGeneral.json +++ b/packages/fakeyou.com/public/locales/en/EntityGeneral.json @@ -1,4 +1,24 @@ { + "MediaCategories": { + "all": "All", + "audio": "Audio", + "image": "Image", + "video": "Video", + "engine_asset": "3D Asset" + }, + "MediaCategoriesPlural": { + "all": "All", + "audio": "Audio", + "image": "Images", + "video": "Videos", + "engine_asset": "3D Assets" + }, + "WeightsCategories": { + "all": "All", + "faceAnimation": "Face Animator", + "tts": "Text to speech", + "voiceConversion": "Voice conversion" + }, "WeightTypes": { "all": "All weights", "hifigan_tt2": "HiFi-GAN TT2", @@ -20,5 +40,5 @@ "glb": "GLB", "gltf": "gltf", "fbx": "FBX" - }, -} \ No newline at end of file + } +} diff --git a/packages/fakeyou.com/src/components/entities/EntityTypes.tsx b/packages/fakeyou.com/src/components/entities/EntityTypes.tsx index 597df501a1..ae3d2b280e 100644 --- a/packages/fakeyou.com/src/components/entities/EntityTypes.tsx +++ b/packages/fakeyou.com/src/components/entities/EntityTypes.tsx @@ -1,6 +1,6 @@ import { enumToKeyArr } from "resources"; import { UploadMediaResponse } from "@storyteller/components/src/api/media_files/UploadMedia"; -import { UploadEngineAssetResponse }from "@storyteller/components/src/api/media_files/UploadEngineAsset"; +import { UploadEngineAssetResponse } from "@storyteller/components/src/api/media_files/UploadEngineAsset"; // 1 @@ -10,7 +10,7 @@ import { UploadEngineAssetResponse }from "@storyteller/components/src/api/media_ export enum EntityType { unknown, media, - weights + weights, } // 2 @@ -23,14 +23,14 @@ export enum MediaFilters { audio, image, video, - engine_asset + engine_asset, } export enum WeightsCategories { all, faceAnimation, tts, - voiceConversion + voiceConversion, } // 3 @@ -49,7 +49,7 @@ export enum WeightsFilters { so_vits_svc, tt2, loRA, - vall_e + vall_e, } // media file types @@ -60,17 +60,17 @@ export enum EngineTypes { glb, gltf, obj, - ron + ron, } export enum AudioTypes { mp3, - wav + wav, } export enum ImageTypes { jpg, - png + png, } export enum VideoTypes { @@ -128,15 +128,19 @@ export type JobSelection = WeightCategoriesProp | WeightFilterProp; export type UploaderResponse = UploadEngineAssetResponse | UploadMediaResponse; export const ListEntityFilters = (mode?: EntityInputMode) => { - // this an object of all types // in most cases it is better to have granular caterogization of types // but because any entity can/could(?) be bookmarked all types are agregated here // this is condensed because typescript didn't like when I broke it apart. // Will clean up soon, I promise - const bookmarkFilters = Object.keys({ ...MediaFilters, ...WeightsFilters }).filter(val => isNaN(Number(val))).reduce((obj,current) => ({ ...obj, [current]: current }),{}); + const bookmarkFilters = Object.keys({ ...MediaFilters, ...WeightsFilters }) + .filter(val => isNaN(Number(val))) + .reduce((obj, current) => ({ ...obj, [current]: current }), {}); - const selectedFilters = mode !== undefined ? [bookmarkFilters,MediaFilters,WeightsFilters,WeightsFilters][mode] : EntityInputMode; + const selectedFilters = + mode !== undefined + ? [bookmarkFilters, MediaFilters, WeightsFilters, WeightsFilters][mode] + : EntityInputMode; return Object.values(selectedFilters).filter(val => isNaN(Number(val))); }; @@ -145,36 +149,59 @@ export const ListEntityFilters = (mode?: EntityInputMode) => { // note the use t() to translate labels // this allows easily maintained translations for inputs -export const EntityFilterOptions = (mode?: EntityInputMode, t = (v:string) => v) => { - return ListEntityFilters(mode).map((value) => { - if (typeof value === "string") return { value, label: t(value) } - return { label: "all", value: "all" }; +export const EntityFilterOptions = ( + mode?: EntityInputMode, + t = (v: string) => v +) => { + const translationNamespaces = [ + "BookmarksCategories", + "MediaCategoriesPlural", + "WeightsCategories", + "WeightsCategories", + ]; + const selectedNamespace = translationNamespaces[mode || -1]; + return ListEntityFilters(mode).map(value => { + if (typeof value === "string") + return { + value, + label: + selectedNamespace !== undefined + ? t(`${selectedNamespace}.${value}`) + : value, + }; + return { label: t(`${selectedNamespace}.all`), value: "all" }; }); }; -export const getMediaTypesByCategory = (mediaCategory: MediaFilters) => enumToKeyArr([ - { ...AudioTypes, ...ImageTypes, ...VideoTypes, ...EngineTypes }, - AudioTypes, - ImageTypes, - VideoTypes, - EngineTypes -][mediaCategory]); +export const getMediaTypesByCategory = (mediaCategory: MediaFilters) => + enumToKeyArr( + [ + { ...AudioTypes, ...ImageTypes, ...VideoTypes, ...EngineTypes }, + AudioTypes, + ImageTypes, + VideoTypes, + EngineTypes, + ][mediaCategory] + ); -export const isSelectedType = (mediaCategory: MediaFilters, fileExtension: string) => - getMediaTypesByCategory(mediaCategory).includes(fileExtension); +export const isSelectedType = ( + mediaCategory: MediaFilters, + fileExtension: string +) => getMediaTypesByCategory(mediaCategory).includes(fileExtension); -export const mediaCategoryfromString = (categoryStr: string) => enumToKeyArr(MediaFilters).indexOf(categoryStr); +export const mediaCategoryfromString = (categoryStr: string) => + enumToKeyArr(MediaFilters).indexOf(categoryStr); //renaming to getMediaCatgoryByType export const getMediaCategory = (fileExtension: string) => { - isSelectedType(MediaFilters.image,fileExtension); - if (isSelectedType(MediaFilters.engine_asset, fileExtension)) return MediaFilters.engine_asset; - if (isSelectedType(MediaFilters.audio, fileExtension)) return MediaFilters.audio; - if (isSelectedType(MediaFilters.image, fileExtension)) return MediaFilters.image; - if (isSelectedType(MediaFilters.video, fileExtension)) return MediaFilters.video; + isSelectedType(MediaFilters.image, fileExtension); + if (isSelectedType(MediaFilters.engine_asset, fileExtension)) + return MediaFilters.engine_asset; + if (isSelectedType(MediaFilters.audio, fileExtension)) + return MediaFilters.audio; + if (isSelectedType(MediaFilters.image, fileExtension)) + return MediaFilters.image; + if (isSelectedType(MediaFilters.video, fileExtension)) + return MediaFilters.video; return MediaFilters.all; // will change to "unknown" eventually -} - - - - +}; 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 index 99b4301b73..8dfa7e3a48 100644 --- a/packages/fakeyou.com/src/v2/view/pages/explore/tabs/FeaturedTab.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/explore/tabs/FeaturedTab.tsx @@ -1,152 +1,184 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useRef, useState } from "react"; import { useLocation } from "react-router-dom"; import MasonryGrid from "components/common/MasonryGrid/MasonryGrid"; import MediaCards from "components/common/Card/MediaCards"; -import { Link } from "react-router-dom"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faChevronRight } from "@fortawesome/pro-solid-svg-icons"; -import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia"; -import { useBookmarks, useRatings } from "hooks"; -import { ListFeaturedMediaFiles } from "@storyteller/components/src/api/media_files/ListFeaturedMediaFiles"; -import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes"; +import { Button, TempSelect as Select } 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 { SegmentButtons } from "components/common"; -import "./FeaturedTab.scss"; +import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia"; +import { + useBookmarks, + useLazyLists, + useLocalize, + useOnScreen, + useRatings, +} from "hooks"; +import InfiniteScroll from "react-infinite-scroll-component"; +import prepFilter from "resources/prepFilter"; +import { ListFeaturedMediaFiles } from "@storyteller/components/src/api/media_files/ListFeaturedMediaFiles"; +import { + EntityInputMode, + EntityFilterOptions, +} from "components/entities/EntityTypes"; -export default function FeaturedTab() { - const { pathname: source } = useLocation(); +export default function MediaTab() { + const { pathname: source, search } = useLocation(); + const urlQueries = new URLSearchParams(search); const bookmarks = useBookmarks(); const ratings = useRatings(); + const toTopBtnRef = useRef(null); + const onScreen = useOnScreen(toTopBtnRef, "0px"); + const { t } = useLocalize("EntityGeneral"); + const gridContainerRef = useRef(null); + const [mediaType, mediaTypeSet] = useState( + urlQueries.get("filter_media_type") || "all" + ); + const [showMasonryGrid, setShowMasonryGrid] = useState(true); const [list, listSet] = useState([]); - const [listType, - // listTypeSet - ] = useState("media"); - // const [showMasonryGrid, setShowMasonryGrid] = useState(true); - const [status, statusSet] = useState(FetchStatus.ready); - const isLoading = - status === FetchStatus.ready || status === FetchStatus.in_progress; + const media = useLazyLists({ + addQueries: { + page_size: urlQueries.get("page_size") || "24", + ...prepFilter(mediaType, "filter_media_type"), + }, + addSetters: { mediaTypeSet }, + fetcher: ListFeaturedMediaFiles, + list, + listSet, + onInputChange: () => setShowMasonryGrid(false), + onSuccess: res => { + ratings.gather({ res, expand: true, key: "token" }); // expand rather than replace for lazy loading + bookmarks.gather({ res, expand: true, key: "token" }); // expand rather than replace for lazy loading + setShowMasonryGrid(true); + }, + requestList: true, + }); - // const options = [{ label: "Media", value: "media" },{ label: "Weights", value: "weight" }]; - // const onChange = ({ target }: any) => { - // listTypeSet(target.value === "weight" ? target.value : "media"); - // // clear list - // // statusSet(FetchStatus.ready) - // }; + const filterOptions = EntityFilterOptions(EntityInputMode.media, t); - useEffect(() => { - if (status === FetchStatus.ready) { - statusSet(FetchStatus.in_progress); - if (listType === "media") { - ListFeaturedMediaFiles("", {}).then((res: any) => { - console.log("🏮", res); - statusSet(FetchStatus.success); - if (res.results) { - listSet(res.results); - } - }); - } - // else if (listType === "weight") {} - } - }, [listType, status]); + const sortOptions = [ + { value: false, label: "Newest" }, + { value: true, label: "Oldest" }, + // { value: "mostliked", label: "Most Liked" }, + ]; return ( -
-
-
-

Featured

- { - // // switch between media/weights control - } - - View media - - + <> +
+
+
- - {isLoading && !list.length ? ( + {media.urlCursor ? ( +
+
+ ) : null} +
+ {media.urlCursor && !onScreen ? ( +
- {/*
-
-

- Featured Image Weights -

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

No more results.

+ } + className="overflow-hidden" + > + {showMasonryGrid && ( + <> + {media.list.length === 0 && media.status === 3 ? ( +
+ No media created yet. +
+ ) : ( + console.log("Layout complete!")} + > + {media.list.map((data: any, key: number) => { + let props = { + bookmarks, + data, + source, + ratings, + type: "media", + showCreator: true, + }; + + return ( +
+ +
+ ); + })} +
+ )} + + )} + )} -
*/} -
+ + ); }