Merge branch 'main' of github.com:storytold/GottaGoFast into main

This commit is contained in:
Michael Chung committed 2024-07-09 16:10:27 -04:00
commit 5e9a620204
14 files changed
+990 -569

No files matched your search

+3 -1
View File
@@ -75,7 +75,9 @@ export const LoadingBar = ({
<div className="flex items-center gap-2.5">
{hasSpinner && <LoadingSpinner className="h-5" />}
{progressData.label && (
<label className="text-lg">{progressData.label}</label>
<label className="text-lg font-medium">
{progressData.label}
</label>
)}
</div>
</div>
@@ -149,7 +149,7 @@ export const UploadFiles3D = ({
<canvas className="h-full w-full" ref={canvasCallbackRef} />
{!assetFile.value && (
<H6 className="absolute left-0 top-1/2 -mt-5 w-full text-center">
File Preivew
File Preview
</H6>
)}
{previewStatus.type.includes("Error") && (
@@ -108,7 +108,7 @@ export const UploadFilesImages = ({
<div className="relative m-auto aspect-square w-full overflow-hidden rounded-lg bg-brand-secondary">
{!assetFile.value && (
<H6 className="absolute left-0 top-1/2 -mt-5 w-full text-center">
File Preivew
File Preview
</H6>
)}
{assetFile.value && (
@@ -26,8 +26,6 @@ import {
import { TestFeaturesButtons } from "./TestFeaturesButtons";
import { Help } from "./Help";
import { LoadUserScenes } from "./LoadUserScenes";
import { NewSceneFromTemplate } from "./NewSceneFromTemplate";
import { getCurrentLocationWithoutParams, isNumberString } from "~/utilities";
import { SceneGenereationMetaData as SceneGenerationMetaData } from "~/pages/PageEnigma/models/sceneGenerationMetadata";
import {
@@ -134,11 +132,6 @@ export const ControlsTopButtons = () => {
}
}, [sceneTitleInput, editorEngine, getSceneGenereationMetaData]);
const handleButtonNewFromTemplate = () => {
handleResetScene();
handleButtonLoadScene();
};
const handleButtonLoadScene = () => {
handleResetScene();
editorEngine?.loadScene(sceneTokenSelected).catch((err) => {
@@ -196,26 +189,6 @@ export const ControlsTopButtons = () => {
showWizard.value = "new_scene";
},
},
{
label: "New scene from Featured",
description: "Ctrl+Shift+N",
dialogProps: {
title: "Create a New Scene from a Featured Scene",
content: (
<NewSceneFromTemplate onSceneSelect={handleSceneSelection} />
),
confirmButtonProps: {
label: "Create",
disabled: sceneTokenSelected === "",
onClick: handleButtonNewFromTemplate,
},
closeButtonProps: {
label: "Cancel",
},
showClose: true,
className: "max-w-5xl",
},
},
{
label: "Load my scene",
description: "Ctrl+O",
@@ -33,8 +33,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
const isSelected = outlinerState.selectedItem.value?.id === item.id;
const editorEngine = useContext(EngineContext)
const editorEngine = useContext(EngineContext);
// Delete object logic here
const handleDeleteKeyPress = (event: React.KeyboardEvent<HTMLDivElement>) => {
@@ -53,8 +52,9 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
<div
role="button"
className={twMerge(
"flex cursor-pointer justify-between px-4 py-[7px] text-[13px] font-normal text-white/80 outline-none transition-all duration-100 hover:bg-action/35 focus:outline-none",
isSelected && "bg-action/90 font-medium text-white hover:bg-action/90",
"flex cursor-pointer justify-between px-4 py-[7px] text-[13px] font-normal text-white/80 outline-none transition-all duration-100 hover:bg-action/50 focus:outline-none",
isSelected &&
"bg-brand-primary/80 font-medium text-white hover:bg-brand-primary/80",
)}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
@@ -73,7 +73,10 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
<button
onClick={(e) => {
e.stopPropagation();
toggleLock(item.id, editorEngine?.lockUnlockObject.bind(editorEngine));
toggleLock(
item.id,
editorEngine?.lockUnlockObject.bind(editorEngine),
);
}}
style={{
opacity: hovered || item.locked ? 1 : 0,
@@ -82,14 +85,19 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
<div className="w-3">
<FontAwesomeIcon
icon={item.locked ? faLock : faLockOpen}
className="opacity-60 transition-opacity duration-100 hover:opacity-100"
className="opacity-80 transition-opacity duration-100 hover:opacity-100"
/>
</div>
</button>
<button
onClick={(e) => {
e.stopPropagation();
toggleVisibility(item.id, editorEngine?.sceneManager?.hideObject.bind(editorEngine.sceneManager));
toggleVisibility(
item.id,
editorEngine?.sceneManager?.hideObject.bind(
editorEngine.sceneManager,
),
);
}}
style={{
opacity: hovered || !item.visible ? 1 : 0,
@@ -99,7 +107,7 @@ const OutlinerItem = ({ item }: { item: SceneObject }) => {
<FontAwesomeIcon
icon={item.visible ? faEye : faEyeSlash}
className={twMerge(
"opacity-60 transition-opacity duration-100 hover:opacity-100",
"opacity-80 transition-opacity duration-100 hover:opacity-100",
item.locked && "text-white/90",
)}
/>
@@ -80,7 +80,10 @@ export const SidePanelMenu = ({
<div>
<FontAwesomeIcon icon={tab.icon} size="lg" />
</div>
<div className="-mb-1 mt-1" style={{ fontSize: 11 }}>
<div
className="-mb-1 mt-1 leading-[14px]"
style={{ fontSize: 11 }}
>
{tab.title}
</div>
</button>
@@ -1,42 +1,79 @@
import { useCallback, useEffect, useState } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import { MediaInfo, MediaItem } from "~/pages/PageEnigma/models";
import {
AssetFilterOption,
AssetType,
FeatureFlags,
FilterEngineCategories,
MediaFileAnimationType,
ToastTypes,
} from "~/enums";
import { animationItems } from "~/pages/PageEnigma/signals";
import { addToast } from "~/signals";
import { Button, FilterButtons, Pagination, UploadModal } from "~/components";
import {
Button,
FilterButtons,
Pagination,
SearchFilter,
UploadModal,
} from "~/components";
import {
TabTitle,
ItemElements,
} from "~/pages/PageEnigma/comps/SidePanelTabs/sharedComps";
import { MediaFilesApi } from "~/Classes/ApiManager";
import { FetchStatus } from "~/pages/PageEnigma/enums";
import { BucketConfig } from "~/api/BucketConfig";
import {
fetchFeaturedMediaItems,
fetchFeaturedMediaItemsSearchResults,
FetchMediaItemStates,
fetchUserMediaItems,
fetchUserMediaItemsSearchResults,
isAnyStatusFetching,
} from "../utilities";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
export const AnimationTab = () => {
useSignals();
const showSearchObjectComponent = usePosthogFeatureFlag(
FeatureFlags.SHOW_SEARCH_OBJECTS,
);
const [open, setOpen] = useState(false);
const [searchTermFeatured, setSearchTermFeatured] = useState("");
const [searchTermUser, setSearchTermUser] = useState("");
const [
{ mediaItems: userAnimations, status: userFetchStatus },
setUserFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredAnimations, status: featuredFetchStatus },
setFeaturedFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSearchResults, status: featuredSearchFetchStatus },
setFeaturedSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: userSearchResults, status: userSearchFetchStatus },
setUserSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [userAnimations, setUserAnimations] = useState<MediaItem[] | null>();
const [featuredAnimations, setFeaturedAnimations] = useState<
MediaItem[] | undefined
>(undefined);
const [selectedFilter, setSelectedFilter] = useState(
AssetFilterOption.FEATURED,
);
const filteredAnimations =
const displayedItems =
selectedFilter === AssetFilterOption.FEATURED
? [...(featuredAnimations || []), ...animationItems.value]
: userAnimations ?? [];
@@ -44,104 +81,88 @@ export const AnimationTab = () => {
const [currentPage, setCurrentPage] = useState<number>(0);
const pageSize = 21;
const totalPages = Math.ceil(filteredAnimations.length / pageSize);
const totalPages = Math.ceil(displayedItems.length / pageSize);
const [fetchStatuses, setFetchStatuses] = useState({
userObjectsFetch: FetchStatus.READY,
featuredObjectsFetch: FetchStatus.READY,
});
const isFetching =
fetchStatuses.userObjectsFetch === FetchStatus.READY ||
fetchStatuses.userObjectsFetch === FetchStatus.IN_PROGRESS ||
fetchStatuses.featuredObjectsFetch === FetchStatus.READY ||
fetchStatuses.featuredObjectsFetch === FetchStatus.IN_PROGRESS;
const isFetching = isAnyStatusFetching([
userFetchStatus,
featuredFetchStatus,
featuredSearchFetchStatus,
userSearchFetchStatus,
]);
const responseMapping = (data: MediaInfo[]) => {
return data.map((item) => {
const bucketConfig = new BucketConfig();
const itemThumb = bucketConfig.getCdnUrl(
item.cover_image.maybe_cover_image_public_bucket_path ?? "",
600,
100,
);
return {
colorIndex: item.cover_image.default_cover.color_index,
imageIndex: item.cover_image.default_cover.image_index,
media_id: item.token,
name: item.maybe_title ?? "Unknown",
type: AssetType.ANIMATION,
media_type: item.media_type,
length: ((item.maybe_duration_millis ?? 1000) / 1000) * 60,
version: 1,
...(item.cover_image.maybe_cover_image_public_bucket_path
? {
thumbnail: itemThumb,
}
: {}),
};
});
};
const fetchUserAnimations = useCallback(
() =>
fetchUserMediaItems({
filterEngineCategories: [FilterEngineCategories.ANIMATION],
setState: (newState: FetchMediaItemStates) => {
setUserFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching User Set Objects",
}),
[],
);
const fetchUserAnimations = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.IN_PROGRESS,
}));
const mediaFilesApi = new MediaFilesApi();
const fetchFeaturedAnimations = useCallback(
() =>
fetchFeaturedMediaItems({
filterEngineCategories: [FilterEngineCategories.ANIMATION],
setState: (newState: FetchMediaItemStates) => {
setFeaturedFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching Featured Set Objects",
}),
[],
);
const response = await mediaFilesApi.ListUserMediaFiles({
page_size: 100,
filter_engine_categories: [FilterEngineCategories.ANIMATION],
});
if (response.success && response.data) {
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.SUCCESS,
}));
const newAnimations = responseMapping(response.data);
setUserAnimations(newAnimations);
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching User Animations",
const filterAnimationItems = (searchTerm: string) =>
animationItems.value.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()),
);
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.ERROR,
}));
}, []);
const fetchFeaturedAnimations = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.IN_PROGRESS,
}));
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.ListFeaturedMediaFiles({
filter_engine_categories: [FilterEngineCategories.ANIMATION],
const fetchFeaturedSearchResults = useCallback(async () => {
const filteredAnimationItems = filterAnimationItems(searchTermFeatured);
fetchFeaturedMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.ANIMATION],
setState: (newState: FetchMediaItemStates) => {
setFeaturedSearchFetch(() => ({
status: newState.status,
mediaItems: newState.mediaItems
? [...filteredAnimationItems, ...newState.mediaItems]
: filteredAnimationItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching Featured Set Objects Search Results",
searchTerm: searchTermFeatured,
});
}, [searchTermFeatured]);
if (response.success && response.data) {
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.SUCCESS,
}));
const newAnimations = responseMapping(response.data);
setFeaturedAnimations(newAnimations);
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching Featured Animations",
);
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.ERROR,
}));
}, []);
const fetchUserSearchResults = useCallback(async () => {
fetchUserMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.ANIMATION],
setState: (newState: FetchMediaItemStates) => {
setUserSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching User Set Objects Search Results",
searchTerm: searchTermUser,
});
}, [searchTermUser]);
useEffect(() => {
if (!userAnimations) {
@@ -157,6 +178,22 @@ export const AnimationTab = () => {
fetchFeaturedAnimations,
]);
useEffect(() => {
if (selectedFilter === AssetFilterOption.FEATURED) {
setCurrentPage(0);
fetchFeaturedSearchResults();
} else if (selectedFilter === AssetFilterOption.MINE) {
setCurrentPage(0);
fetchUserSearchResults();
}
}, [
searchTermFeatured,
searchTermUser,
fetchFeaturedSearchResults,
fetchUserSearchResults,
selectedFilter,
]);
return (
<>
<TabTitle title="Animation" />
@@ -169,7 +206,7 @@ export const AnimationTab = () => {
}}
/>
<div className="w-full px-4">
<div className="flex w-full flex-col gap-3 px-4">
<Button
icon={faCirclePlus}
variant="action"
@@ -178,6 +215,26 @@ export const AnimationTab = () => {
>
Upload Animation
</Button>
{showSearchObjectComponent && (
<SearchFilter
searchTerm={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
: searchTermUser
}
onSearchChange={
selectedFilter === AssetFilterOption.FEATURED
? setSearchTermFeatured
: setSearchTermUser
}
key={selectedFilter}
placeholder={
selectedFilter === AssetFilterOption.FEATURED
? "Search featured animations"
: "Search my animations"
}
/>
)}
</div>
<div className="w-full grow overflow-y-auto px-4 pb-4">
<ItemElements
@@ -185,7 +242,15 @@ export const AnimationTab = () => {
debug="animations tab"
currentPage={currentPage}
pageSize={pageSize}
items={filteredAnimations}
items={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
? featuredSearchResults ?? []
: displayedItems
: searchTermUser
? userSearchResults ?? []
: displayedItems
}
/>
</div>
{totalPages > 1 && (
@@ -1,37 +1,74 @@
import { useCallback, useEffect, useState } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import {
AssetFilterOption,
AssetType,
FeatureFlags,
FilterEngineCategories,
ToastTypes,
} from "~/enums";
import { FetchStatus } from "~/pages/PageEnigma/enums";
import { MediaInfo, MediaItem } from "~/pages/PageEnigma/models";
import { characterItems as demoCharacterItems } from "~/pages/PageEnigma/signals";
import { addToast } from "~/signals";
import { BucketConfig } from "~/api/BucketConfig";
import { Button, FileWrapper, FilterButtons, Pagination } from "~/components";
import {
Button,
FileWrapper,
FilterButtons,
Pagination,
SearchFilter,
} from "~/components";
import {
ItemElements,
TabTitle,
} from "~/pages/PageEnigma/comps/SidePanelTabs/sharedComps";
import { MediaFilesApi } from "~/Classes/ApiManager";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
import {
fetchFeaturedMediaItems,
fetchFeaturedMediaItemsSearchResults,
FetchMediaItemStates,
fetchUserMediaItems,
fetchUserMediaItemsSearchResults,
isAnyStatusFetching,
} from "../utilities";
export const CharactersTab = () => {
useSignals();
const [userCharacters, setUserCharacters] = useState<MediaItem[] | undefined>(
undefined,
const showSearchObjectComponent = usePosthogFeatureFlag(
FeatureFlags.SHOW_SEARCH_OBJECTS,
);
const [featuredCharacters, setFeaturedCharacters] = useState<
MediaItem[] | undefined
>(undefined);
const [searchTermFeatured, setSearchTermFeatured] = useState("");
const [searchTermUser, setSearchTermUser] = useState("");
const [
{ mediaItems: userCharacters, status: userFetchStatus },
setUserFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredCharacters, status: featuredFetchStatus },
setFeaturedFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSearchResults, status: featuredSearchFetchStatus },
setFeaturedSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: userSearchResults, status: userSearchFetchStatus },
setUserSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [selectedFilter, setSelectedFilter] = useState(
AssetFilterOption.FEATURED,
);
@@ -39,7 +76,7 @@ export const CharactersTab = () => {
...demoCharacterItems.value,
...(featuredCharacters ?? []),
];
const filteredCharacters =
const displayedItems =
selectedFilter === AssetFilterOption.FEATURED
? allFeaturedCharacters ?? []
: userCharacters ?? [];
@@ -47,104 +84,88 @@ export const CharactersTab = () => {
const [currentPage, setCurrentPage] = useState<number>(0);
const pageSize = 21;
const totalPages = Math.ceil(filteredCharacters.length / pageSize);
const totalPages = Math.ceil(displayedItems.length / pageSize);
const [fetchStatuses, setFetchStatuses] = useState({
userObjectsFetch: FetchStatus.READY,
featuredObjectsFetch: FetchStatus.READY,
});
const isFetching =
fetchStatuses.userObjectsFetch === FetchStatus.READY ||
fetchStatuses.userObjectsFetch === FetchStatus.IN_PROGRESS ||
fetchStatuses.featuredObjectsFetch === FetchStatus.READY ||
fetchStatuses.featuredObjectsFetch === FetchStatus.IN_PROGRESS;
const isFetching = isAnyStatusFetching([
userFetchStatus,
featuredFetchStatus,
featuredSearchFetchStatus,
userSearchFetchStatus,
]);
const responseMapping = (data: MediaInfo[]): MediaItem[] => {
return data.map((item) => {
const bucketConfig = new BucketConfig();
const itemThumb = bucketConfig.getCdnUrl(
item.cover_image.maybe_cover_image_public_bucket_path ?? "",
600,
100,
);
return {
colorIndex: item.cover_image.default_cover.color_index,
imageIndex: item.cover_image.default_cover.image_index,
media_id: item.token,
name: item.maybe_title ?? "Unknown",
type: AssetType.CHARACTER,
media_type: item.media_type,
// maybe_animation_type: item.maybe_animation_type || undefined,
version: 1,
...(item.cover_image.maybe_cover_image_public_bucket_path
? {
thumbnail: itemThumb,
}
: {}),
};
});
};
const fetchUserCharacters = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.IN_PROGRESS,
}));
const mediaFilesApi = new MediaFilesApi();
const fetchUserCharacters = useCallback(
() =>
fetchUserMediaItems({
filterEngineCategories: [FilterEngineCategories.CHARACTER],
setState: (newState: FetchMediaItemStates) => {
setUserFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching User Characters",
}),
[],
);
const response = await mediaFilesApi.ListUserMediaFiles({
page_size: 100,
filter_engine_categories: [FilterEngineCategories.CHARACTER],
});
const fetchFeaturedCharacters = useCallback(
() =>
fetchFeaturedMediaItems({
filterEngineCategories: [FilterEngineCategories.CHARACTER],
setState: (newState: FetchMediaItemStates) => {
setFeaturedFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching Featured Characters",
}),
[],
);
if (response.success && response.data) {
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.SUCCESS,
}));
const newCharacters = responseMapping(response.data);
setUserCharacters(newCharacters);
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching User Characters",
const filterCharacterItems = (searchTerm: string) =>
demoCharacterItems.value.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()),
);
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.ERROR,
}));
}, []);
const fetchFeaturedCharacters = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.IN_PROGRESS,
}));
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.ListFeaturedMediaFiles({
filter_engine_categories: [FilterEngineCategories.CHARACTER],
const fetchFeaturedSearchResults = useCallback(async () => {
const filteredCharacterItems = filterCharacterItems(searchTermFeatured);
fetchFeaturedMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.CHARACTER],
setState: (newState: FetchMediaItemStates) => {
setFeaturedSearchFetch(() => ({
status: newState.status,
mediaItems: newState.mediaItems
? [...filteredCharacterItems, ...newState.mediaItems]
: filteredCharacterItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching Featured Characters Search Results",
searchTerm: searchTermFeatured,
});
}, [searchTermFeatured]);
if (response.success && response.data) {
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.SUCCESS,
}));
const newCharacters = responseMapping(response.data);
setFeaturedCharacters(newCharacters);
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching Featured Characters",
);
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.ERROR,
}));
}, []);
const fetchUserSearchResults = useCallback(async () => {
fetchUserMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.CHARACTER],
setState: (newState: FetchMediaItemStates) => {
setUserSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching User Characters Search Results",
searchTerm: searchTermUser,
});
}, [searchTermUser]);
useEffect(() => {
if (!userCharacters) {
@@ -160,6 +181,22 @@ export const CharactersTab = () => {
fetchFeaturedCharacters,
]);
useEffect(() => {
if (selectedFilter === AssetFilterOption.FEATURED) {
setCurrentPage(0);
fetchFeaturedSearchResults();
} else if (selectedFilter === AssetFilterOption.MINE) {
setCurrentPage(0);
fetchUserSearchResults();
}
}, [
searchTermFeatured,
searchTermUser,
fetchFeaturedSearchResults,
fetchUserSearchResults,
selectedFilter,
]);
return (
<>
<FileWrapper
@@ -177,7 +214,7 @@ export const CharactersTab = () => {
}}
/>
</div>
<div className="w-full px-4">
<div className="flex w-full flex-col gap-3 px-4">
<Button
className="file-picker-button py-3"
htmlFor={parentId}
@@ -186,14 +223,42 @@ export const CharactersTab = () => {
>
Upload Character
</Button>
{showSearchObjectComponent && (
<SearchFilter
searchTerm={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
: searchTermUser
}
onSearchChange={
selectedFilter === AssetFilterOption.FEATURED
? setSearchTermFeatured
: setSearchTermUser
}
key={selectedFilter}
placeholder={
selectedFilter === AssetFilterOption.FEATURED
? "Search featured characters"
: "Search my characters"
}
/>
)}
</div>
<div className="w-full grow overflow-y-auto rounded px-4 pb-4">
<ItemElements
busy={isFetching}
debug="characters tab"
items={filteredCharacters}
currentPage={currentPage}
pageSize={pageSize}
items={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
? featuredSearchResults ?? []
: displayedItems
: searchTermUser
? userSearchResults ?? []
: displayedItems
}
/>
</div>
{totalPages > 1 && (
@@ -207,7 +272,7 @@ export const CharactersTab = () => {
/>
)}
</>
)} // End FileWrapper Render
)}
/>
</>
);
@@ -1,102 +1,160 @@
import { useCallback, useEffect, useState } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import { MediaInfo, MediaItem } from "~/pages/PageEnigma/models";
import {
AssetFilterOption,
AssetType,
FilterEngineCategories,
ToastTypes,
MediaFileAnimationType,
FeatureFlags,
} from "~/enums";
import {
Button,
FilterButtons,
Pagination,
SearchFilter,
UploadModal,
} from "~/components";
import {
ItemElements,
TabTitle,
} from "~/pages/PageEnigma/comps/SidePanelTabs/sharedComps";
import { MediaFilesApi } from "~/Classes/ApiManager";
import { addToast } from "~/signals";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
import {
fetchFeaturedMediaItems,
fetchFeaturedMediaItemsSearchResults,
FetchMediaItemStates,
fetchUserMediaItems,
fetchUserMediaItemsSearchResults,
isAnyStatusFetching,
} from "../utilities";
import { FetchStatus } from "~/pages/PageEnigma/enums";
export const ExpressionTab = () => {
useSignals();
const [open, setOpen] = useState(false);
const [userExpressions, setUserExpressions] = useState<MediaItem[] | null>();
const [featuredExpressions, setFeaturedExpressions] = useState<
MediaItem[] | undefined
>(undefined);
const showSearchObjectComponent = usePosthogFeatureFlag(
FeatureFlags.SHOW_SEARCH_OBJECTS,
);
const [open, setOpen] = useState(false);
const [searchTermFeatured, setSearchTermFeatured] = useState("");
const [searchTermUser, setSearchTermUser] = useState("");
const [
{ mediaItems: userExpressions, status: userFetchStatus },
setUserFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredExpressions, status: featuredFetchStatus },
setFeaturedFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSearchResults, status: featuredSearchFetchStatus },
setFeaturedSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: userSearchResults, status: userSearchFetchStatus },
setUserSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [selectedFilter, setSelectedFilter] = useState(
AssetFilterOption.FEATURED,
);
const filteredExpressions =
const displayedItems =
selectedFilter === AssetFilterOption.FEATURED
? featuredExpressions ?? []
: userExpressions ?? [];
const [currentPage, setCurrentPage] = useState<number>(0);
const pageSize = 21;
const totalPages = Math.ceil(filteredExpressions.length / pageSize);
const totalPages = Math.ceil(displayedItems.length / pageSize);
const responseMapping = (data: MediaInfo[], isMine: boolean) => {
return data.map((item, index: number) => {
return {
version: 1,
type: AssetType.EXPRESSION,
media_type: item.media_type,
media_id: item.token,
name: item.maybe_title,
publicBucketPath: item.public_bucket_path,
length: ((item.maybe_duration_millis ?? 1000) / 1000) * 60,
thumbnail: item.cover_image?.maybe_cover_image_public_bucket_path
? "https://cdn.fakeyou.com/cdn-cgi/image/width=600,quality=100" +
item.cover_image?.maybe_cover_image_public_bucket_path
: undefined,
isMine: isMine,
imageIndex: index,
} as MediaItem;
});
};
const isFetching = isAnyStatusFetching([
userFetchStatus,
featuredFetchStatus,
featuredSearchFetchStatus,
userSearchFetchStatus,
]);
const fetchUserExpressions = useCallback(async () => {
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.ListUserMediaFiles({
page_size: 100,
filter_engine_categories: [FilterEngineCategories.EXPRESSION],
});
if (response.success && response.data) {
const newExpressions = responseMapping(response.data, true);
setUserExpressions(newExpressions);
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching User Expressions",
);
}, []);
const fetchUserExpressions = useCallback(
() =>
fetchUserMediaItems({
filterEngineCategories: [FilterEngineCategories.EXPRESSION],
setState: (newState: FetchMediaItemStates) => {
setUserFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching User Expressions",
}),
[],
);
const fetchFeaturedExpressions = useCallback(async () => {
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.ListFeaturedMediaFiles({
filter_engine_categories: [FilterEngineCategories.EXPRESSION],
const fetchFeaturedExpressions = useCallback(
() =>
fetchFeaturedMediaItems({
filterEngineCategories: [FilterEngineCategories.EXPRESSION],
setState: (newState: FetchMediaItemStates) => {
setFeaturedFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching Featured Expressions",
}),
[],
);
const fetchFeaturedSearchResults = useCallback(async () => {
fetchFeaturedMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.EXPRESSION],
setState: (newState: FetchMediaItemStates) => {
setFeaturedSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching Featured Expressions Search Results",
searchTerm: searchTermFeatured,
});
if (response.success && response.data) {
const newExpressions = responseMapping(response.data, false);
setFeaturedExpressions(newExpressions);
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching Featured Expressions",
);
}, []);
}, [searchTermFeatured]);
const fetchUserSearchResults = useCallback(async () => {
fetchUserMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.EXPRESSION],
setState: (newState: FetchMediaItemStates) => {
setUserSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching User Expressions Search Results",
searchTerm: searchTermUser,
});
}, [searchTermUser]);
useEffect(() => {
if (!userExpressions) {
@@ -107,11 +165,27 @@ export const ExpressionTab = () => {
}
}, [
userExpressions,
featuredExpressions,
fetchUserExpressions,
featuredExpressions,
fetchFeaturedExpressions,
]);
useEffect(() => {
if (selectedFilter === AssetFilterOption.FEATURED) {
setCurrentPage(0);
fetchFeaturedSearchResults();
} else if (selectedFilter === AssetFilterOption.MINE) {
setCurrentPage(0);
fetchUserSearchResults();
}
}, [
searchTermFeatured,
searchTermUser,
fetchFeaturedSearchResults,
fetchUserSearchResults,
selectedFilter,
]);
return (
<>
<TabTitle title="Face Expression" />
@@ -122,42 +196,50 @@ export const ExpressionTab = () => {
setCurrentPage(0);
}}
/>
<div className="flex w-full flex-col gap-2.5 px-4">
<div className="flex w-full flex-col gap-3 px-4">
<Button
icon={faCirclePlus}
variant="action"
onClick={() => setOpen(true)}
className="w-full py-3 text-sm font-medium"
>
Upload Expression ( Developer Only )
Upload Expression (Dev Only)
</Button>
{/* <ButtonDialogue
buttonProps={{
variant: "secondary",
label:
"Watch a tutorial on how to create expressions using your iPhone",
className:
"justify-center border-2 border-white/30 hover:border-white/40 text-start p-3 gap-3.5 rounded-xl",
icon: faMobileNotch,
iconClassName: "text-3xl",
}}
dialogProps={{
className: "max-w-6xl",
}}
title={<>Video Tutorial: Creating your own expressions</>}
>
<video
className="aspect-video w-full rounded-lg"
controls
src="https://storage.googleapis.com/vocodes-public/media/t/p/w/q/6/tpwq6beqn95f1h1q9e88c42r0gdekg7p/storyteller_tpwq6beqn95f1h1q9e88c42r0gdekg7p.mp4"
></video>
</ButtonDialogue> */}
{showSearchObjectComponent && (
<SearchFilter
searchTerm={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
: searchTermUser
}
onSearchChange={
selectedFilter === AssetFilterOption.FEATURED
? setSearchTermFeatured
: setSearchTermUser
}
key={selectedFilter}
placeholder={
selectedFilter === AssetFilterOption.FEATURED
? "Search featured expressions"
: "Search my expressions"
}
/>
)}
</div>
<div className="h-full w-full overflow-y-auto px-4 pb-4">
<ItemElements
busy={isFetching}
currentPage={currentPage}
pageSize={pageSize}
items={filteredExpressions}
items={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
? featuredSearchResults ?? []
: displayedItems
: searchTermUser
? userSearchResults ?? []
: displayedItems
}
/>
</div>
{totalPages > 1 && (
@@ -1,34 +1,41 @@
import { useCallback, useEffect, useState } from "react";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import {
AssetFilterOption,
FeatureFlags,
FilterEngineCategories,
IMAGE_FILE_TYPE,
} from "~/enums";
import { FetchStatus } from "~/pages/PageEnigma/enums";
import {
Button,
FilterButtons,
Pagination,
SearchFilter,
UploadModalImages,
} from "~/components";
import {
TabTitle,
ItemElements,
} from "~/pages/PageEnigma/comps/SidePanelTabs/sharedComps";
import {
fetchFeaturedMediaItems,
fetchUserMediaItems,
FetchMediaItemStates,
isAnyStatusFetching,
fetchFeaturedMediaItemsSearchResults,
fetchUserMediaItemsSearchResults,
} from "../utilities";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
export const ImagePlanesTab = () => {
const showSearchObjectComponent = usePosthogFeatureFlag(
FeatureFlags.SHOW_SEARCH_OBJECTS,
);
const [openUploadModal, setOpenUploadModal] = useState(false);
const [searchTermFeatured, setSearchTermFeatured] = useState("");
const [searchTermUser, setSearchTermUser] = useState("");
const [{ mediaItems: userImages, status: userFetchStatus }, setUserFetch] =
useState<FetchMediaItemStates>({
@@ -36,15 +43,29 @@ export const ImagePlanesTab = () => {
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredImages, status: feateredFetchStatus },
{ mediaItems: featuredImages, status: featuredFetchStatus },
setFeaturedFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSearchResults, status: featuredSearchFetchStatus },
setFeaturedSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: userSearchResults, status: userSearchFetchStatus },
setUserSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [selectedFilter, setSelectedFilter] = useState(AssetFilterOption.MINE);
const diasplayedItems =
const displayedItems =
selectedFilter === AssetFilterOption.FEATURED
? featuredImages ?? []
: userImages ?? [];
@@ -52,11 +73,13 @@ export const ImagePlanesTab = () => {
const [currentPage, setCurrentPage] = useState<number>(0);
const pageSize = 21;
const totalPages = Math.ceil(diasplayedItems.length / pageSize);
const totalPages = Math.ceil(displayedItems.length / pageSize);
const isFetching = isAnyStatusFetching([
userFetchStatus,
feateredFetchStatus,
featuredFetchStatus,
featuredSearchFetchStatus,
userSearchFetchStatus,
]);
const fetchUserImages = useCallback(
@@ -93,6 +116,40 @@ export const ImagePlanesTab = () => {
[],
);
const fetchFeaturedSearchResults = useCallback(async () => {
fetchFeaturedMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.IMAGE_PLANE],
setState: (newState: FetchMediaItemStates) => {
setFeaturedSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching Featured Images Search Results",
searchTerm: searchTermFeatured,
});
}, [searchTermFeatured]);
const fetchUserSearchResults = useCallback(async () => {
fetchUserMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.IMAGE_PLANE],
setState: (newState: FetchMediaItemStates) => {
setUserSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching User Images Search Results",
searchTerm: searchTermUser,
});
}, [searchTermUser]);
useEffect(() => {
if (!userImages) {
fetchUserImages();
@@ -102,6 +159,22 @@ export const ImagePlanesTab = () => {
}
}, [userImages, fetchUserImages, featuredImages, fetchFeaturedImages]);
useEffect(() => {
if (selectedFilter === AssetFilterOption.FEATURED) {
setCurrentPage(0);
fetchFeaturedSearchResults();
} else if (selectedFilter === AssetFilterOption.MINE) {
setCurrentPage(0);
fetchUserSearchResults();
}
}, [
searchTermFeatured,
searchTermUser,
fetchFeaturedSearchResults,
fetchUserSearchResults,
selectedFilter,
]);
return (
<>
<TabTitle title="Image Panels" />
@@ -113,7 +186,7 @@ export const ImagePlanesTab = () => {
}}
/>
<div className="w-full px-4">
<div className="flex w-full flex-col gap-3 px-4">
<Button
icon={faCirclePlus}
variant="action"
@@ -122,13 +195,41 @@ export const ImagePlanesTab = () => {
>
Upload Image Panels
</Button>
{showSearchObjectComponent && (
<SearchFilter
searchTerm={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
: searchTermUser
}
onSearchChange={
selectedFilter === AssetFilterOption.FEATURED
? setSearchTermFeatured
: setSearchTermUser
}
key={selectedFilter}
placeholder={
selectedFilter === AssetFilterOption.FEATURED
? "Search featured image panels"
: "Search my image panels"
}
/>
)}
</div>
<div className="w-full grow overflow-y-auto px-4 pb-4">
<ItemElements
busy={isFetching}
currentPage={currentPage}
pageSize={pageSize}
items={diasplayedItems}
items={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
? featuredSearchResults ?? []
: displayedItems
: searchTermUser
? userSearchResults ?? []
: displayedItems
}
/>
</div>
{totalPages > 1 && (
@@ -1,22 +1,16 @@
import { useCallback, useEffect, useState } from "react";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
import { useSignals } from "@preact/signals-react/runtime";
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import {
AssetFilterOption,
AssetType,
FeatureFlags,
FilterEngineCategories,
ToastTypes,
} from "~/enums";
import { FetchStatus } from "~/pages/PageEnigma/enums";
import { MediaInfo, MediaItem } from "~/pages/PageEnigma/models";
import { shapeItems } from "~/pages/PageEnigma/signals";
import { BucketConfig } from "~/api/BucketConfig";
import {
TabTitle,
ItemElements,
@@ -28,8 +22,14 @@ import {
SearchFilter,
Pagination,
} from "~/components";
import { MediaFilesApi } from "~/Classes/ApiManager";
import { addToast } from "~/signals";
import {
fetchFeaturedMediaItems,
fetchFeaturedMediaItemsSearchResults,
FetchMediaItemStates,
fetchUserMediaItems,
fetchUserMediaItemsSearchResults,
isAnyStatusFetching,
} from "../utilities";
export const ObjectsTab = () => {
useSignals();
@@ -39,209 +39,128 @@ export const ObjectsTab = () => {
);
const [searchTermFeatured, setSearchTermFeatured] = useState("");
const [searchTermMine, setSearchTermMine] = useState("");
const [filteredSearchObjectsFeatured, setFilteredSearchObjectsFeatured] =
useState<MediaItem[]>([]);
const [filteredSearchObjectsMine, setFilteredSearchObjectsMine] = useState<
MediaItem[]
>([]);
const [userObjects, setUserObjects] = useState<MediaItem[] | undefined>(
undefined,
);
const [featuredObjects, setFeaturedObjects] = useState<
MediaItem[] | undefined
>(undefined);
const [searchTermUser, setSearchTermUser] = useState("");
const [{ mediaItems: userObjects, status: userFetchStatus }, setUserFetch] =
useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredObjects, status: featuredFetchStatus },
setFeaturedFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSearchResults, status: featuredSearchFetchStatus },
setFeaturedSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: userSearchResults, status: userSearchFetchStatus },
setUserSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [selectedFilter, setSelectedFilter] = useState(
AssetFilterOption.FEATURED,
);
const [currentPage, setCurrentPage] = useState<number>(0);
const filteredObjects =
const displayedItems =
selectedFilter === AssetFilterOption.FEATURED
? [...shapeItems.value, ...(featuredObjects ?? [])]
: userObjects ?? [];
const pageSize = 21;
const totalPages = Math.ceil(filteredObjects.length / pageSize);
const totalPages = Math.ceil(displayedItems.length / pageSize);
const [fetchStatuses, setFetchStatuses] = useState({
userObjectsFetch: FetchStatus.READY,
featuredObjectsFetch: FetchStatus.READY,
searchFetch: FetchStatus.READY,
});
const isFetching =
fetchStatuses.userObjectsFetch === FetchStatus.READY ||
fetchStatuses.userObjectsFetch === FetchStatus.IN_PROGRESS ||
fetchStatuses.featuredObjectsFetch === FetchStatus.READY ||
fetchStatuses.featuredObjectsFetch === FetchStatus.IN_PROGRESS ||
fetchStatuses.searchFetch === FetchStatus.READY ||
fetchStatuses.searchFetch === FetchStatus.IN_PROGRESS;
const isFetching = isAnyStatusFetching([
userFetchStatus,
featuredFetchStatus,
featuredSearchFetchStatus,
userSearchFetchStatus,
]);
const responseMapping = (data: MediaInfo[]) => {
return data.map((item) => {
const bucketConfig = new BucketConfig();
const itemThumb = bucketConfig.getCdnUrl(
item.cover_image.maybe_cover_image_public_bucket_path ?? "",
600,
100,
);
return {
colorIndex: item.cover_image.default_cover.color_index,
imageIndex: item.cover_image.default_cover.image_index,
media_id: item.token,
name: item.maybe_title ?? "Unknown",
type: AssetType.OBJECT,
media_type: item.media_type,
version: 1,
...(item.cover_image.maybe_cover_image_public_bucket_path
? {
thumbnail: itemThumb,
}
: {}),
};
});
};
const fetchUserObjects = useCallback(
() =>
fetchUserMediaItems({
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setUserFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching User Set Objects",
}),
[],
);
const fetchFeaturedObjects = useCallback(
() =>
fetchFeaturedMediaItems({
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setFeaturedFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage: "Unknown Error in Fetching Featured Set Objects",
}),
[],
);
const filterObjectItems = (searchTerm: string) =>
shapeItems.value.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()),
);
const fetchFeaturedSearchResults = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.IN_PROGRESS,
}));
const searchTerm = searchTermFeatured;
if (!searchTerm.trim()) {
setFilteredSearchObjectsFeatured([]);
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.SUCCESS,
}));
return;
}
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.SearchFeaturedMediaFiles({
search_term: searchTerm,
filter_engine_categories: [FilterEngineCategories.IMAGE_PLANE],
const filteredObjectItems = filterObjectItems(searchTermFeatured);
fetchFeaturedMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setFeaturedSearchFetch(() => ({
status: newState.status,
mediaItems: newState.mediaItems
? [...filteredObjectItems, ...newState.mediaItems]
: filteredObjectItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching Featured Set Objects Search Results",
searchTerm: searchTermFeatured,
});
if (response.success && response.data) {
const newSearchObjects = responseMapping(response.data);
setFilteredSearchObjectsFeatured(newSearchObjects);
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.SUCCESS,
}));
} else {
addToast(
ToastTypes.ERROR,
response.errorMessage || "Failed to fetch search results",
);
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.ERROR,
}));
}
}, [searchTermFeatured]);
const fetchUserSearchResults = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.IN_PROGRESS,
}));
const searchTerm = searchTermMine;
if (!searchTerm.trim()) {
setFilteredSearchObjectsMine([]);
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.SUCCESS,
}));
return;
}
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.SearchUserMediaFiles({
search_term: searchTerm,
filter_engine_categories: [FilterEngineCategories.OBJECT],
fetchUserMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setUserSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching User Set Objects Search Results",
searchTerm: searchTermUser,
});
if (response.success && response.data) {
const newSearchObjects = responseMapping(response.data);
setFilteredSearchObjectsMine(newSearchObjects);
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.SUCCESS,
}));
} else {
addToast(
ToastTypes.ERROR,
response.errorMessage || "Failed to fetch search results",
);
setFetchStatuses((curr) => ({
...curr,
searchFetch: FetchStatus.ERROR,
}));
}
}, [searchTermMine]);
const fetchUserObjects = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.IN_PROGRESS,
}));
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.ListUserMediaFiles({
page_size: 100,
filter_engine_categories: [
FilterEngineCategories.OBJECT,
FilterEngineCategories.IMAGE_PLANE,
],
});
if (response.success && response.data) {
const newObjects = responseMapping(response.data);
setUserObjects(newObjects);
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.SUCCESS,
}));
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching User Objects",
);
setFetchStatuses((curr) => ({
...curr,
userObjectsFetch: FetchStatus.ERROR,
}));
}, []);
const fetchFeaturedObjects = useCallback(async () => {
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.IN_PROGRESS,
}));
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.ListFeaturedMediaFiles({
filter_engine_categories: [FilterEngineCategories.OBJECT],
});
if (response.success && response.data) {
const newObjects = responseMapping(response.data);
setFeaturedObjects(newObjects);
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.SUCCESS,
}));
return;
}
addToast(
ToastTypes.ERROR,
response.errorMessage || "Unknown Error in Fetching Featured Objects",
);
setFetchStatuses((curr) => ({
...curr,
featuredObjectsFetch: FetchStatus.ERROR,
}));
}, []);
}, [searchTermUser]);
useEffect(() => {
if (!userObjects) {
@@ -254,13 +173,15 @@ export const ObjectsTab = () => {
useEffect(() => {
if (selectedFilter === AssetFilterOption.FEATURED) {
setCurrentPage(0);
fetchFeaturedSearchResults();
} else if (selectedFilter === AssetFilterOption.MINE) {
setCurrentPage(0);
fetchUserSearchResults();
}
}, [
searchTermFeatured,
searchTermMine,
searchTermUser,
fetchFeaturedSearchResults,
fetchUserSearchResults,
selectedFilter,
@@ -294,14 +215,19 @@ export const ObjectsTab = () => {
searchTerm={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
: searchTermMine
: searchTermUser
}
onSearchChange={
selectedFilter === AssetFilterOption.FEATURED
? setSearchTermFeatured
: setSearchTermMine
: setSearchTermUser
}
key={selectedFilter}
placeholder={
selectedFilter === AssetFilterOption.FEATURED
? "Search featured objects"
: "Search my objects"
}
/>
)}
</div>
@@ -314,11 +240,11 @@ export const ObjectsTab = () => {
items={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
? filteredSearchObjectsFeatured
: filteredObjects
: searchTermMine
? filteredSearchObjectsMine
: filteredObjects
? featuredSearchResults ?? []
: displayedItems
: searchTermUser
? userSearchResults ?? []
: displayedItems
}
/>
</div>
@@ -3,6 +3,7 @@ import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
import {
AssetFilterOption,
FeatureFlags,
FilterEngineCategories,
IMAGE_FILE_TYPE,
OBJECT_FILE_TYPE,
@@ -13,6 +14,7 @@ import {
Button,
FilterButtons,
Pagination,
SearchFilter,
UploadModal3DPreview,
} from "~/components";
@@ -26,10 +28,19 @@ import {
fetchUserMediaItems,
FetchMediaItemStates,
isAnyStatusFetching,
fetchFeaturedMediaItemsSearchResults,
fetchUserMediaItemsSearchResults,
} from "../utilities";
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureFlag";
export const SetsTab = () => {
const showSearchObjectComponent = usePosthogFeatureFlag(
FeatureFlags.SHOW_SEARCH_OBJECTS,
);
const [openUploadModal, setOpenUploadModal] = useState(false);
const [searchTermFeatured, setSearchTermFeatured] = useState("");
const [searchTermUser, setSearchTermUser] = useState("");
const [
{ mediaItems: userSetObjects, status: userFetchStatus },
@@ -39,17 +50,31 @@ export const SetsTab = () => {
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSetObjects, status: feateredFetchStatus },
{ mediaItems: featuredSetObjects, status: featuredFetchStatus },
setFeaturedFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: featuredSearchResults, status: featuredSearchFetchStatus },
setFeaturedSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [
{ mediaItems: userSearchResults, status: userSearchFetchStatus },
setUserSearchFetch,
] = useState<FetchMediaItemStates>({
mediaItems: undefined,
status: FetchStatus.READY,
});
const [selectedFilter, setSelectedFilter] = useState(
AssetFilterOption.FEATURED,
);
const diasplayedItems =
const displayedItems =
selectedFilter === AssetFilterOption.FEATURED
? featuredSetObjects ?? []
: userSetObjects ?? [];
@@ -57,20 +82,19 @@ export const SetsTab = () => {
const [currentPage, setCurrentPage] = useState<number>(0);
const pageSize = 21;
const totalPages = Math.ceil(diasplayedItems.length / pageSize);
const totalPages = Math.ceil(displayedItems.length / pageSize);
const isFetching = isAnyStatusFetching([
userFetchStatus,
feateredFetchStatus,
featuredFetchStatus,
featuredSearchFetchStatus,
userSearchFetchStatus,
]);
const fetchUserSetObjects = useCallback(
() =>
fetchUserMediaItems({
filterEngineCategories: [
FilterEngineCategories.OBJECT,
FilterEngineCategories.IMAGE_PLANE,
],
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setUserFetch((curr) => ({
status: newState.status,
@@ -101,6 +125,40 @@ export const SetsTab = () => {
[],
);
const fetchFeaturedSearchResults = useCallback(async () => {
fetchFeaturedMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setFeaturedSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching Featured Set Objects Search Results",
searchTerm: searchTermFeatured,
});
}, [searchTermFeatured]);
const fetchUserSearchResults = useCallback(async () => {
fetchUserMediaItemsSearchResults({
filterEngineCategories: [FilterEngineCategories.OBJECT],
setState: (newState: FetchMediaItemStates) => {
setUserSearchFetch((curr) => ({
status: newState.status,
mediaItems: newState.mediaItems
? newState.mediaItems
: curr.mediaItems,
}));
},
defaultErrorMessage:
"Unknown Error in Fetching User Set Objects Search Results",
searchTerm: searchTermUser,
});
}, [searchTermUser]);
useEffect(() => {
if (!userSetObjects) {
fetchUserSetObjects();
@@ -115,6 +173,22 @@ export const SetsTab = () => {
fetchFeaturedSetObjects,
]);
useEffect(() => {
if (selectedFilter === AssetFilterOption.FEATURED) {
setCurrentPage(0);
fetchFeaturedSearchResults();
} else if (selectedFilter === AssetFilterOption.MINE) {
setCurrentPage(0);
fetchUserSearchResults();
}
}, [
searchTermFeatured,
searchTermUser,
fetchFeaturedSearchResults,
fetchUserSearchResults,
selectedFilter,
]);
return (
<>
<TabTitle title="Film Sets" />
@@ -126,7 +200,7 @@ export const SetsTab = () => {
}}
/>
<div className="w-full px-4">
<div className="flex w-full flex-col gap-3 px-4">
<Button
icon={faCirclePlus}
variant="action"
@@ -135,14 +209,42 @@ export const SetsTab = () => {
>
Upload Set Objects
</Button>
{showSearchObjectComponent && (
<SearchFilter
searchTerm={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
: searchTermUser
}
onSearchChange={
selectedFilter === AssetFilterOption.FEATURED
? setSearchTermFeatured
: setSearchTermUser
}
key={selectedFilter}
placeholder={
selectedFilter === AssetFilterOption.FEATURED
? "Search featured sets"
: "Search my sets"
}
/>
)}
</div>
<div className="w-full grow overflow-y-auto px-4 pb-4">
<ItemElements
busy={isFetching}
debug="animations tab"
debug="sets tab"
currentPage={currentPage}
pageSize={pageSize}
items={diasplayedItems}
items={
selectedFilter === AssetFilterOption.FEATURED
? searchTermFeatured
? featuredSearchResults ?? []
: displayedItems
: searchTermUser
? userSearchResults ?? []
: displayedItems
}
/>
</div>
{totalPages > 1 && (
@@ -14,6 +14,7 @@ interface fetchMediaItemsInterface {
setState: ({ mediaItems, status }: FetchMediaItemStates) => void;
filterEngineCategories: FilterEngineCategories[];
defaultErrorMessage?: string;
searchTerm?: string;
}
export const fetchUserMediaItems = async ({
@@ -83,3 +84,98 @@ export const fetchFeaturedMediaItems = async ({
setState({ status: FetchStatus.ERROR });
return;
};
// Search Results
export const fetchFeaturedMediaItemsSearchResults = async ({
setState,
searchTerm,
filterEngineCategories,
defaultErrorMessage,
}: {
setState: ({ mediaItems, status }: FetchMediaItemStates) => void;
searchTerm: string;
filterEngineCategories: FilterEngineCategories[];
defaultErrorMessage?: string;
}) => {
setState({ status: FetchStatus.IN_PROGRESS });
if (!searchTerm.trim()) {
console.log("Search term is empty after trim");
setState({
mediaItems: [],
status: FetchStatus.SUCCESS,
});
return;
}
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.SearchFeaturedMediaFiles({
search_term: searchTerm,
filter_engine_categories: filterEngineCategories,
});
if (response.success && response.data) {
const newSearchObjects = responseMapping(
response.data,
filterEngineCategories,
);
setState({
mediaItems: newSearchObjects,
status: FetchStatus.SUCCESS,
});
} else {
addToast(
ToastTypes.ERROR,
response.errorMessage ||
defaultErrorMessage ||
"Failed to fetch search results",
);
setState({ status: FetchStatus.ERROR });
}
};
export const fetchUserMediaItemsSearchResults = async ({
setState,
searchTerm,
filterEngineCategories,
defaultErrorMessage,
}: {
setState: ({ mediaItems, status }: FetchMediaItemStates) => void;
searchTerm: string;
filterEngineCategories: FilterEngineCategories[];
defaultErrorMessage?: string;
}) => {
setState({ status: FetchStatus.IN_PROGRESS });
if (!searchTerm.trim()) {
setState({
mediaItems: [],
status: FetchStatus.SUCCESS,
});
return;
}
const mediaFilesApi = new MediaFilesApi();
const response = await mediaFilesApi.SearchUserMediaFiles({
search_term: searchTerm,
filter_engine_categories: filterEngineCategories,
});
if (response.success && response.data) {
const newSearchObjects = responseMapping(
response.data,
filterEngineCategories,
);
setState({
mediaItems: newSearchObjects,
status: FetchStatus.SUCCESS,
});
} else {
addToast(
ToastTypes.ERROR,
response.errorMessage ||
defaultErrorMessage ||
"Failed to fetch search results",
);
setState({ status: FetchStatus.ERROR });
}
};
@@ -4,9 +4,7 @@ import { MediaInfo, MediaItem } from "~/pages/PageEnigma/models";
import { BucketConfig } from "~/api/BucketConfig";
export const isAnyStatusFetching = (statuses: FetchStatus[]): boolean => {
return statuses.some((status) => {
return status === FetchStatus.READY || status === FetchStatus.IN_PROGRESS;
});
return statuses.some((status) => status === FetchStatus.IN_PROGRESS);
};
export const responseMapping = (