mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 21:02:14 +00:00
feature/search objects (#575)
- add functions in API manager to use the search API - adds search field to object tab, it searches through featured/mine objects depending on which tab you are on - this feature is hid behind a feature flag until we have a proper design and solution to handling assets --------- Co-authored-by: wil <wil@storyteller.ai>
This commit is contained in:
1 parent
403f0b7990
commit
0c8ce2ced7
21 files changed
+371
-67
No files matched your search
@@ -6,4 +6,4 @@ GRAVATAR_API="https://www.gravatar.com"
|
||||
DEPLOY_PRIME_URL="https://studio-staging.studio.storyteller.ai"
|
||||
REACT_APP_PUBLIC_POSTHOG_KEY="phc_jBFgac0mVALAFk3negnSfYCcHgvkT00yBLQDmCDYNBb"
|
||||
REACT_APP_PUBLIC_POSTHOG_UI="https://us.i.posthog.com"
|
||||
CONTEXT="PRODUCTION"
|
||||
CONTEXT="DEVELOPMENT"
|
||||
@@ -90,13 +90,20 @@ Add the feature flag for all users.
|
||||
To check the feature flag in the app use code similar to this
|
||||
|
||||
```ts
|
||||
const [myFlag, setMyFlag] = useState(false);
|
||||
useEffect(() => {
|
||||
const flag = posthog.isFeatureEnabled("<flag name>");
|
||||
if (flag) {
|
||||
setMyFlag(true);
|
||||
}
|
||||
}, []);
|
||||
import { usePosthogFeatureFlag } from "~/hooks/usePosthogFeatureflag";
|
||||
import { FeatureFlags } from "~/enums";
|
||||
|
||||
const Component = () => {
|
||||
const flag = usePosthogFeatureFlag(FeaturesFlags.FLAG_NAME);
|
||||
// if flag is true, the feature shoud be executed & showed;
|
||||
// if flag is false, the feature should be disabled & hid;
|
||||
// for example
|
||||
return(
|
||||
<>
|
||||
{flag && <ComponentThatDependsOnTheFlag />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Other Documentation
|
||||
|
||||
@@ -34,6 +34,13 @@ interface ListUserMediaQuery {
|
||||
filter_engine_categories?: FilterEngineCategories[];
|
||||
}
|
||||
|
||||
interface SearchFeaturedMediaQuery {
|
||||
search_term: string;
|
||||
filter_media_classes?: FilterMediaClasses[];
|
||||
filter_media_type?: FilterMediaType[];
|
||||
filter_engine_categories?: FilterEngineCategories[];
|
||||
}
|
||||
|
||||
export class MediaFilesApi extends ApiManager {
|
||||
public async DeleteMediaFileByToken({
|
||||
mediaFileToken,
|
||||
@@ -215,6 +222,70 @@ export class MediaFilesApi extends ApiManager {
|
||||
});
|
||||
}
|
||||
|
||||
public async SearchFeaturedMediaFiles(
|
||||
query: SearchFeaturedMediaQuery,
|
||||
): Promise<ApiResponse<MediaInfo[], Pagination>> {
|
||||
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/search_featured`;
|
||||
const queryWithStrings = {
|
||||
search_term: query.search_term,
|
||||
filter_media_classes: query.filter_media_classes
|
||||
? query.filter_media_classes.join(",")
|
||||
: undefined,
|
||||
filter_media_type: query.filter_media_type
|
||||
? query.filter_media_type.join(",")
|
||||
: undefined,
|
||||
filter_engine_categories: query.filter_engine_categories
|
||||
? query.filter_engine_categories.join(",")
|
||||
: undefined,
|
||||
};
|
||||
return await this.get<{
|
||||
success: boolean;
|
||||
results: MediaInfo[];
|
||||
pagination: Pagination;
|
||||
}>({ endpoint, query: queryWithStrings })
|
||||
.then((response) => ({
|
||||
success: true,
|
||||
data: response.results,
|
||||
pagination: response.pagination,
|
||||
}))
|
||||
.catch((err) => ({
|
||||
success: false,
|
||||
errorMessage: err.message,
|
||||
}));
|
||||
}
|
||||
|
||||
public async SearchUserMediaFiles(
|
||||
query: SearchFeaturedMediaQuery,
|
||||
): Promise<ApiResponse<MediaInfo[], Pagination>> {
|
||||
const endpoint = `${this.ApiTargets.BaseApi}/v1/media_files/search_session`;
|
||||
const queryWithStrings = {
|
||||
search_term: query.search_term,
|
||||
filter_media_classes: query.filter_media_classes
|
||||
? query.filter_media_classes.join(",")
|
||||
: undefined,
|
||||
filter_media_type: query.filter_media_type
|
||||
? query.filter_media_type.join(",")
|
||||
: undefined,
|
||||
filter_engine_categories: query.filter_engine_categories
|
||||
? query.filter_engine_categories.join(",")
|
||||
: undefined,
|
||||
};
|
||||
return await this.get<{
|
||||
success: boolean;
|
||||
results: MediaInfo[];
|
||||
pagination: Pagination;
|
||||
}>({ endpoint, query: queryWithStrings })
|
||||
.then((response) => ({
|
||||
success: true,
|
||||
data: response.results,
|
||||
pagination: response.pagination,
|
||||
}))
|
||||
.catch((err) => ({
|
||||
success: false,
|
||||
errorMessage: err.message,
|
||||
}));
|
||||
}
|
||||
|
||||
public async RenameMediaFileByToken({
|
||||
mediaToken,
|
||||
name,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
inputClassName?: string;
|
||||
iconClassName?: string;
|
||||
label?: string;
|
||||
icon?: IconDefinition;
|
||||
isError?: boolean;
|
||||
@@ -24,6 +25,7 @@ export const Input = React.forwardRef(
|
||||
label,
|
||||
icon,
|
||||
inputClassName,
|
||||
iconClassName,
|
||||
className,
|
||||
id,
|
||||
isError,
|
||||
@@ -42,7 +44,7 @@ export const Input = React.forwardRef(
|
||||
{icon && (
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className="text-md absolute pl-3 pt-3"
|
||||
className={twMerge("text-md absolute pl-3 pt-3", iconClassName)}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
faCircleXmark,
|
||||
faFilterList,
|
||||
faSearch,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { Button, Input, Tooltip } from "~/components";
|
||||
import debounce from "lodash/debounce";
|
||||
import { useRef, useState, useEffect, useCallback } from "react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
interface SearchFilterProps {
|
||||
onSearchChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
searchTerm?: string;
|
||||
showFilters?: boolean;
|
||||
}
|
||||
|
||||
export const SearchFilter = ({
|
||||
onSearchChange,
|
||||
placeholder = "Search...",
|
||||
searchTerm = "",
|
||||
showFilters = false,
|
||||
}: SearchFilterProps) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [inputValue, setInputValue] = useState(searchTerm);
|
||||
const [hasText, setHasText] = useState(searchTerm.length > 0);
|
||||
|
||||
useEffect(() => {
|
||||
setInputValue(searchTerm);
|
||||
setHasText(searchTerm.length > 0);
|
||||
}, [searchTerm]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debouncedChangeHandler = useCallback(
|
||||
debounce((value: string) => {
|
||||
onSearchChange(value);
|
||||
}, 500),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
setInputValue(value);
|
||||
setHasText(value.length > 0);
|
||||
debouncedChangeHandler(value);
|
||||
};
|
||||
|
||||
const clearSearch = () => {
|
||||
setInputValue("");
|
||||
setHasText(false);
|
||||
onSearchChange("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className="relative grow">
|
||||
<Input
|
||||
ref={inputRef}
|
||||
icon={faSearch}
|
||||
iconClassName="opacity-50 pt-[11px] text-sm"
|
||||
inputClassName="h-9 rounded-lg text-sm bg-ui-controls-button/30 pr-8 pl-9"
|
||||
placeholder={placeholder}
|
||||
className="grow"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
{hasText && (
|
||||
<FontAwesomeIcon
|
||||
icon={faCircleXmark}
|
||||
className="absolute right-2.5 top-1/2 -translate-y-1/2 transform cursor-pointer opacity-50 transition-all duration-100 hover:opacity-100"
|
||||
onClick={clearSearch}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showFilters && (
|
||||
<Tooltip position="top" content="Filters">
|
||||
<Button
|
||||
icon={faFilterList}
|
||||
variant="secondary"
|
||||
className="h-9 w-9"
|
||||
iconClassName="text-[16px]"
|
||||
></Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./SearchFilter";
|
||||
@@ -17,6 +17,7 @@ export * from "./LoadingBar";
|
||||
export * from "./NumberInput";
|
||||
export * from "./Pagination";
|
||||
export * from "./Pill";
|
||||
export * from "./SearchFilter";
|
||||
export * from "./Select"; //Select's onChange type should be generic
|
||||
export * from "./Slider";
|
||||
export * from "./Tabs";
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { RemixBrowser } from "@remix-run/react";
|
||||
import { startTransition, StrictMode, useEffect } from "react";
|
||||
import { hydrateRoot } from "react-dom/client";
|
||||
import posthog from "posthog-js";
|
||||
import { posthog } from "posthog-js";
|
||||
import { PostHogProvider } from "posthog-js/react";
|
||||
import EnvironmentVariables from "~/Classes/EnvironmentVariables";
|
||||
|
||||
function PosthogInit() {
|
||||
@@ -22,7 +23,9 @@ startTransition(() => {
|
||||
hydrateRoot(
|
||||
document,
|
||||
<StrictMode>
|
||||
<RemixBrowser />
|
||||
<PostHogProvider client={posthog}>
|
||||
<RemixBrowser />
|
||||
</PostHogProvider>
|
||||
<PosthogInit />
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum FeatureFlags {
|
||||
SHOW_SEARCH_OBJECTS = "show-search-objects",
|
||||
}
|
||||
@@ -5,6 +5,7 @@ export * from "./authentication";
|
||||
export * from "./Billing";
|
||||
export * from "./ClipGroup";
|
||||
export * from "./ClipType";
|
||||
export * from "./FeatureFlags";
|
||||
export * from "./FileTypes";
|
||||
export * from "./JobStatus";
|
||||
export * from "./MediaFileAnimationTypes";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./useActiveJobs";
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./usePosthogFeatureFlag";
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useFeatureFlagEnabled } from "posthog-js/react";
|
||||
import { FeatureFlags } from "~/enums/FeatureFlags";
|
||||
import environmentVariables from "~/Classes/EnvironmentVariables";
|
||||
|
||||
export const usePosthogFeatureFlag = (flag: FeatureFlags): boolean => {
|
||||
const enabled = useFeatureFlagEnabled(flag) ?? false;
|
||||
if (environmentVariables.values.CONTEXT !== "PRODUCTION") {
|
||||
return true;
|
||||
}
|
||||
return enabled;
|
||||
};
|
||||
@@ -2,11 +2,11 @@ import { PageEditor } from "~/pages/PageEnigma/PageEditor";
|
||||
import { TrackProvider } from "~/pages/PageEnigma/contexts/TrackContext/TrackProvider";
|
||||
import { DragComponent } from "~/pages/PageEnigma/comps/DragComponent/DragComponent";
|
||||
import { EngineProvider } from "./contexts/EngineContext";
|
||||
import { useActiveJobs } from "~/hooks";
|
||||
import { useActiveJobs } from "~/hooks/useActiveJobs";
|
||||
import { useBackgroundLoadingMedia } from "~/hooks/useBackgroundLoadingMedia";
|
||||
import { useQueueHandler } from "./hooks/useQueueHandler";
|
||||
import { ErrorDialog, LoadingDots } from "~/components";
|
||||
import { GenerateModals } from "~/pages/PageEnigma/comps/GenerateModals/GenerateModals";
|
||||
import { useBackgroundLoadingMedia } from "~/hooks/useBackgroundLoadingMedia";
|
||||
|
||||
import { EditorLoadingBar } from "./comps/EditorLoadingBar";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { faCirclePlus, faMobileNotch } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { MediaInfo, MediaItem } from "~/pages/PageEnigma/models";
|
||||
import {
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
|
||||
import {
|
||||
Button,
|
||||
ButtonDialogue,
|
||||
FilterButtons,
|
||||
Pagination,
|
||||
UploadModalMovement,
|
||||
@@ -131,27 +130,6 @@ export const ExpressionTab = () => {
|
||||
>
|
||||
Upload Expression
|
||||
</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>
|
||||
</div>
|
||||
<div className="h-full w-full overflow-y-auto px-4 pb-4">
|
||||
<ItemElements
|
||||
|
||||
@@ -2,6 +2,8 @@ import { ItemElement } from "./ItemElement";
|
||||
import { MediaItem } from "~/pages/PageEnigma/models";
|
||||
import { dndSidePanelWidth, sidePanelWidth } from "~/pages/PageEnigma/signals";
|
||||
import { H4, P, LoadingDots } from "~/components";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faEmptySet } from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
interface Props {
|
||||
busy?: boolean;
|
||||
@@ -51,11 +53,15 @@ export const ItemElements = ({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (items.length === 0) {
|
||||
if (items.length === 0 && !busy) {
|
||||
return (
|
||||
<div className="h-full w-full text-center">
|
||||
<H4> You do not have anything here. </H4>
|
||||
<P> Plase upload some assets. </P>
|
||||
<div className="flex h-full w-full flex-col items-center justify-center text-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faEmptySet}
|
||||
className="mb-4 text-4xl opacity-30"
|
||||
/>
|
||||
<H4>You do not have anything here.</H4>
|
||||
<P className="text-sm opacity-75">Please upload some assets.</P>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
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";
|
||||
@@ -6,6 +7,7 @@ import { faCirclePlus } from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
AssetFilterOption,
|
||||
AssetType,
|
||||
FeatureFlags,
|
||||
FilterEngineCategories,
|
||||
ToastTypes,
|
||||
} from "~/enums";
|
||||
@@ -16,7 +18,13 @@ import { shapeItems } from "~/pages/PageEnigma/signals";
|
||||
import { BucketConfig } from "~/api/BucketConfig";
|
||||
|
||||
import { ItemElements } from "~/pages/PageEnigma/comps/SidePanelTabs/itemTabs/ItemElements";
|
||||
import { Button, FileWrapper, FilterButtons, Pagination } from "~/components";
|
||||
import {
|
||||
Button,
|
||||
FileWrapper,
|
||||
FilterButtons,
|
||||
SearchFilter,
|
||||
Pagination,
|
||||
} from "~/components";
|
||||
import { TabTitle } from "~/pages/PageEnigma/comps/SidePanelTabs/comps/TabTitle";
|
||||
import { MediaFilesApi } from "~/Classes/ApiManager";
|
||||
import { addToast } from "~/signals";
|
||||
@@ -24,6 +32,17 @@ import { addToast } from "~/signals";
|
||||
export const ObjectsTab = () => {
|
||||
useSignals();
|
||||
|
||||
const showSearchObjectComponent = usePosthogFeatureFlag(
|
||||
FeatureFlags.SHOW_SEARCH_OBJECTS,
|
||||
);
|
||||
|
||||
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,
|
||||
);
|
||||
@@ -46,12 +65,15 @@ export const ObjectsTab = () => {
|
||||
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.featuredObjectsFetch === FetchStatus.IN_PROGRESS ||
|
||||
fetchStatuses.searchFetch === FetchStatus.READY ||
|
||||
fetchStatuses.searchFetch === FetchStatus.IN_PROGRESS;
|
||||
|
||||
const responseMapping = (data: MediaInfo[]) => {
|
||||
return data.map((item) => {
|
||||
@@ -78,6 +100,86 @@ export const ObjectsTab = () => {
|
||||
});
|
||||
};
|
||||
|
||||
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.OBJECT],
|
||||
});
|
||||
|
||||
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],
|
||||
});
|
||||
|
||||
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,
|
||||
@@ -86,7 +188,10 @@ export const ObjectsTab = () => {
|
||||
const mediaFilesApi = new MediaFilesApi();
|
||||
const response = await mediaFilesApi.ListUserMediaFiles({
|
||||
page_size: 100,
|
||||
filter_engine_categories: [FilterEngineCategories.OBJECT, FilterEngineCategories.IMAGE_PLANE],
|
||||
filter_engine_categories: [
|
||||
FilterEngineCategories.OBJECT,
|
||||
FilterEngineCategories.IMAGE_PLANE,
|
||||
],
|
||||
});
|
||||
|
||||
if (response.success && response.data) {
|
||||
@@ -145,6 +250,20 @@ export const ObjectsTab = () => {
|
||||
}
|
||||
}, [userObjects, fetchUserObjects, featuredObjects, fetchFeaturedObjects]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedFilter === AssetFilterOption.FEATURED) {
|
||||
fetchFeaturedSearchResults();
|
||||
} else if (selectedFilter === AssetFilterOption.MINE) {
|
||||
fetchUserSearchResults();
|
||||
}
|
||||
}, [
|
||||
searchTermFeatured,
|
||||
searchTermMine,
|
||||
fetchFeaturedSearchResults,
|
||||
fetchUserSearchResults,
|
||||
selectedFilter,
|
||||
]);
|
||||
|
||||
return (
|
||||
<FileWrapper
|
||||
onSuccess={fetchUserObjects}
|
||||
@@ -158,7 +277,7 @@ export const ObjectsTab = () => {
|
||||
setSelectedFilter(Number(button));
|
||||
}}
|
||||
/>
|
||||
<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}
|
||||
@@ -167,6 +286,21 @@ export const ObjectsTab = () => {
|
||||
>
|
||||
Upload Object
|
||||
</Button>
|
||||
{showSearchObjectComponent && (
|
||||
<SearchFilter
|
||||
searchTerm={
|
||||
selectedFilter === AssetFilterOption.FEATURED
|
||||
? searchTermFeatured
|
||||
: searchTermMine
|
||||
}
|
||||
onSearchChange={
|
||||
selectedFilter === AssetFilterOption.FEATURED
|
||||
? setSearchTermFeatured
|
||||
: setSearchTermMine
|
||||
}
|
||||
key={selectedFilter}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full grow overflow-y-auto rounded px-4 pb-4">
|
||||
<ItemElements
|
||||
@@ -174,7 +308,15 @@ export const ObjectsTab = () => {
|
||||
debug="objects tab"
|
||||
currentPage={currentPage}
|
||||
pageSize={pageSize}
|
||||
items={filteredObjects}
|
||||
items={
|
||||
selectedFilter === AssetFilterOption.FEATURED
|
||||
? searchTermFeatured
|
||||
? filteredSearchObjectsFeatured
|
||||
: filteredObjects
|
||||
: searchTermMine
|
||||
? filteredSearchObjectsMine
|
||||
: filteredObjects
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{totalPages > 1 && (
|
||||
@@ -188,7 +330,7 @@ export const ObjectsTab = () => {
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)} // End FileWrapper Render
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -55,18 +55,6 @@ export const links: LinksFunction = () => [
|
||||
];
|
||||
|
||||
export async function loader() {
|
||||
// if (process.env.NODE_ENV === "development") {
|
||||
// const env = {
|
||||
// BASE_API: "",
|
||||
// GOOGLE_API: "",
|
||||
// // @ts-expect-error ProvessEnv is correct
|
||||
// FUNNEL_API: process.env.FUNNEL_API || "%BUILD_FUNNEL_API%",
|
||||
// // @ts-expect-error ProvessEnv is correct
|
||||
// CDN_API: process.env.CDN_API || "%BUILD_CDN_API%",
|
||||
// GRAVATAR_API: "",
|
||||
// } as Record<string, string | boolean>;
|
||||
// return { ENV: env };
|
||||
// }
|
||||
const env = {
|
||||
// @ts-expect-error ProvessEnv is correct
|
||||
BASE_API: process.env.BASE_API || "%BUILD_BASE_API%",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@layer components {
|
||||
.filter-tab {
|
||||
@apply text-nowrap rounded-lg border-2 border-assets-background bg-assets-background px-2.5 py-1.5 text-sm font-medium text-white/80 transition-all duration-150 hover:border-assets-background/0 hover:bg-assets-background/80;
|
||||
@apply text-nowrap rounded-lg border-2 border-assets-background bg-assets-background px-2.5 py-1 text-[13px] font-medium text-white/80 transition-all duration-150 hover:border-assets-background/0 hover:bg-assets-background/80;
|
||||
}
|
||||
|
||||
.filter-tab.active {
|
||||
|
||||
Generated
+6
-6
@@ -25,6 +25,7 @@
|
||||
"@remix-run/router": "^1.15.3",
|
||||
"@remix-run/serve": "^2.8.1",
|
||||
"@tweenjs/tween.js": "^18.6.4",
|
||||
"@types/lodash": "^4.17.6",
|
||||
"ccapture.js": "github:aceslowman/ccapture.js#npm-fix",
|
||||
"common": "file:common",
|
||||
"csstype": "^3.1.3",
|
||||
@@ -33,6 +34,7 @@
|
||||
"detect-gpu": "^5.0.38",
|
||||
"http-proxy-middleware": "^3.0.0",
|
||||
"isbot": "^4.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"posthog-js": "^1.141.0",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
@@ -7450,10 +7452,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz",
|
||||
"integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==",
|
||||
"dev": true
|
||||
"version": "4.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.6.tgz",
|
||||
"integrity": "sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA=="
|
||||
},
|
||||
"node_modules/@types/mdast": {
|
||||
"version": "3.0.15",
|
||||
@@ -15874,8 +15875,7 @@
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
|
||||
+3
-1
@@ -14,7 +14,7 @@
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build",
|
||||
"chromatic": "npx chromatic --project-token=chpt_0945f90d5d3626c",
|
||||
"prepare": "husky install",
|
||||
"prepare": "husky",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -37,6 +37,7 @@
|
||||
"@remix-run/router": "^1.15.3",
|
||||
"@remix-run/serve": "^2.8.1",
|
||||
"@tweenjs/tween.js": "^18.6.4",
|
||||
"@types/lodash": "^4.17.6",
|
||||
"ccapture.js": "github:aceslowman/ccapture.js#npm-fix",
|
||||
"common": "file:common",
|
||||
"csstype": "^3.1.3",
|
||||
@@ -45,6 +46,7 @@
|
||||
"detect-gpu": "^5.0.38",
|
||||
"http-proxy-middleware": "^3.0.0",
|
||||
"isbot": "^4.1.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"posthog-js": "^1.141.0",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
|
||||
Reference in new issue
Block a user