mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
Merge branch 'main' of github.com:storytold/GottaGoFast into main
This commit is contained in:
commit
5e9a620204
14 files changed
+990
-569
No files matched your search
@@ -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 = (
|
||||
|
||||
Reference in new issue
Block a user