fixed bug where entity input uploaded files would not display after clearing a previous selection, also added busy states

This commit is contained in:
Victor committed 2024-07-19 15:35:40 -07:00
1 parent a9a4c39e3f
commit b4c85b287d
4 files changed
+96 -34

No files matched your search

@@ -21,6 +21,7 @@ interface EntityInputProps {
accept?: AcceptTypes | AcceptTypes[];
aspectRatio?: "square" | "landscape" | "portrait";
className?: string;
debug?: string;
label?: string;
name?: string;
onChange?: any;
@@ -136,6 +137,7 @@ export default function EntityInput({
accept,
aspectRatio = "square",
className,
debug,
label,
name = "",
onChange,
@@ -149,7 +151,14 @@ export default function EntityInput({
const presetToken = search ? urlSearch.get("preset_token") : "";
const queryUser = search ? urlSearch.get("query_user") : "";
const [mediaToken, mediaTokenSet] = useState(presetToken || value || "");
const { media, mediaSet, prompt } = useMedia({
const {
busy: mediaBusy,
media,
mediaSet,
prompt,
reload,
} = useMedia({
debug,
mediaToken: mediaToken || value,
});
const [updated, updatedSet] = useState(false);
@@ -163,9 +172,12 @@ export default function EntityInput({
onChange({ target: { name, value: token } });
};
const { inputProps } = useMediaUploader({
const { busy: uploaderBusy, inputProps } = useMediaUploader({
autoUpload: true,
onSuccess: (res: UploaderResponse) => selectToken(res.media_file_token),
onSuccess: (res: UploaderResponse) => {
reload();
selectToken(res.media_file_token);
},
});
const onSelect = (data: MediaFile) => {
@@ -173,10 +185,18 @@ export default function EntityInput({
selectToken(data.token);
};
const busy = false;
const busy = mediaBusy || uploaderBusy;
const index = busy ? 0 : media ? 1 : 2;
const [animating, animatingSet] = useState(false);
if (debug)
console.log(`🐞 EntityInput Debug at ${debug}`, {
index,
busy,
media,
view: ["busy slide", "full slide", "empty slide"][index],
});
const transitions = useTransition(index, {
config: { mass: 1, tension: 80, friction: 10 },
from: { opacity: 0, transform: `translateX(${5}rem)` },
@@ -219,7 +239,8 @@ export default function EntityInput({
return [
<AnimatedSlide
{...{
className: "fy-entity-input-busy",
className:
"fy-entity-input-busy d-flex justify-content-center align-items-center",
render: MediaBusy,
...sharedProps,
}}
@@ -9,9 +9,9 @@ import {
} from "@storyteller/components/src/api/prompts/GetPrompts";
import { DeleteMedia } from "@storyteller/components/src/api/media_files/DeleteMedia";
import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes";
import { usePrevious } from "hooks";
export default function useMedia({
debug = "",
mediaToken = "",
onSuccess = (res: MediaFile) => {},
onRemove = (res: any) => {},
@@ -20,6 +20,7 @@ export default function useMedia({
const [writeStatus, writeStatusSet] = useState(FetchStatus.paused);
const [media, mediaSet] = useState<MediaFile | undefined>();
const [prompt, promptSet] = useState<Prompt | undefined>();
const remove = (as_mod: boolean) => {
writeStatusSet(FetchStatus.in_progress);
DeleteMedia(mediaToken, {
@@ -31,19 +32,20 @@ export default function useMedia({
});
};
const previousToken = usePrevious(mediaToken);
const reload = () => {
statusSet(FetchStatus.ready);
mediaSet(undefined);
};
const busy =
status === FetchStatus.in_progress ||
writeStatus === FetchStatus.in_progress;
useEffect(() => {
if (
status === FetchStatus.ready &&
mediaToken &&
(!media || previousToken !== mediaToken)
) {
// this condidition handles all media file fetches
// it is triggered when status is ready and there is a media token but no media
if (status === FetchStatus.ready && mediaToken && !media) {
statusSet(FetchStatus.in_progress);
GetMedia(mediaToken, {})
.then(res => {
@@ -51,6 +53,7 @@ export default function useMedia({
statusSet(FetchStatus.success);
onSuccess(res.media_file);
mediaSet(res.media_file);
if (res.media_file.maybe_prompt_token) {
GetPrompts(res.media_file.maybe_prompt_token, {}).then(
promptRes => {
@@ -67,13 +70,16 @@ export default function useMedia({
});
}
// this triggers a media refetch when there is media, but the hook mediaToken param is updated
if (media && media.token !== mediaToken) {
mediaSet(undefined);
statusSet(FetchStatus.ready);
}
}, [media, mediaToken, previousToken, prompt, onSuccess, status, statusSet]);
}, [media, mediaToken, prompt, onSuccess, status, statusSet]);
return {
busy,
media,
mediaFile: media,
mediaSet,
@@ -81,6 +87,7 @@ export default function useMedia({
remove,
reload,
status,
statusSet,
writeStatus,
};
}
@@ -1,36 +1,56 @@
import { useState } from "react";
import { useFile } from "hooks";
import { v4 as uuidv4 } from "uuid";
import { isSelectedType, MediaFilters, UploaderResponse } from "components/entities/EntityTypes";
import {
isSelectedType,
MediaFilters,
UploaderResponse,
} from "components/entities/EntityTypes";
import { UploadMedia } from "@storyteller/components/src/api/media_files/UploadMedia";
import { UploadEngineAsset }from "@storyteller/components/src/api/media_files/UploadEngineAsset";
import { UploadEngineAsset } from "@storyteller/components/src/api/media_files/UploadEngineAsset";
import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype";
import { GetFileTypeByExtension as extension } from "@storyteller/components/src/utils/GetFileTypeByExtension";
import { MediaFileClass } from "@storyteller/components/src/api/enums/MediaFileClass";
import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes";
interface Props {
autoUpload?: boolean,
autoUpload?: boolean;
onSuccess?: (res: UploaderResponse) => any;
}
const n = () => {};
export default function useMediaUploader({ autoUpload, onSuccess = n }: Props) {
const [mediaClass,mediaClassSet] = useState<MediaFileClass>(MediaFileClass.Unknown);
const [engineSubtype,engineSubtypeSet] = useState<MediaFileSubtype | undefined>();
const [status, statusSet] = useState(FetchStatus.ready);
const [mediaClass, mediaClassSet] = useState<MediaFileClass>(
MediaFileClass.Unknown
);
const [engineSubtype, engineSubtypeSet] = useState<
MediaFileSubtype | undefined
>();
const createUpload = (inputFile: File, todo = n ) => {
const createUpload = (inputFile: File, todo = n) => {
statusSet(FetchStatus.in_progress);
const fileExtension = extension(inputFile.name || "");
const isEngineAsset = isSelectedType(MediaFilters.engine_asset, fileExtension);
const isEngineAsset = isSelectedType(
MediaFilters.engine_asset,
fileExtension
);
const baseConfig = { uuid_idempotency_token: uuidv4(), file: inputFile };
const engineConfig = { ...baseConfig, media_file_subtype: engineSubtype, media_file_class: mediaClass };
const engineConfig = {
...baseConfig,
media_file_subtype: engineSubtype,
media_file_class: mediaClass,
};
const mediaConfig = { ...baseConfig, source: "file" };
const uploader = isEngineAsset ? UploadEngineAsset(engineConfig) : UploadMedia(mediaConfig);
const uploader = isEngineAsset
? UploadEngineAsset(engineConfig)
: UploadMedia(mediaConfig);
if (inputFile) {
uploader
.then((res: UploaderResponse) => {
uploader.then((res: UploaderResponse) => {
if ("media_file_token" in res) {
statusSet(FetchStatus.success);
onSuccess(res);
todo();
}
@@ -39,16 +59,27 @@ export default function useMediaUploader({ autoUpload, onSuccess = n }: Props) {
};
const { file, clear, inputProps } = useFile({
...autoUpload ? { onChange: (inputFile: File) => createUpload(inputFile,clear) } : {}
...(autoUpload
? {
onChange: (inputFile: File) => {
if (inputFile) createUpload(inputFile, clear);
},
}
: {}),
});
const upload = () => createUpload(file,clear);
const upload = () => {
createUpload(file, clear);
};
const engineSubtypeChange = ({ target }: { target: any }) => engineSubtypeSet(target.value);
const engineSubtypeChange = ({ target }: { target: any }) =>
engineSubtypeSet(target.value);
const mediaClassChange = ({ target }: { target: any }) => mediaClassSet(target.value);
const mediaClassChange = ({ target }: { target: any }) =>
mediaClassSet(target.value);
return {
busy: status === FetchStatus.in_progress,
clear,
engineSubtype,
engineSubtypeChange,
@@ -57,10 +88,12 @@ export default function useMediaUploader({ autoUpload, onSuccess = n }: Props) {
file,
inputProps,
isAudio: isSelectedType(MediaFilters.audio, extension(file?.name || "")),
isEngineAsset: isSelectedType(MediaFilters.engine_asset, extension(file?.name || "")),
isEngineAsset: isSelectedType(
MediaFilters.engine_asset,
extension(file?.name || "")
),
isImage: isSelectedType(MediaFilters.image, extension(file?.name || "")),
isVideo: isSelectedType(MediaFilters.video, extension(file?.name || "")),
upload
}
};
upload,
};
}
@@ -127,6 +127,7 @@ export default function AIFaceMirror({ sessionSubscriptionsWrapper }: Props) {
{...{
accept: ["video"],
aspectRatio: "landscape",
// debug: "AFM driver input",
name: "faceDriverToken",
value: faceDriverToken,
onChange: ({ target }: { target: any }) => {