mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 01:38:44 +00:00
Merge branch 'main' of github.com:storytold/storyteller-board into main
This commit is contained in:
commit
7e3c6efe59
19 files changed
+407
-123
No files matched your search
@@ -8,5 +8,8 @@ yarn run build2
|
||||
echo "Copy test files into dist"
|
||||
cp -r test/* dist/
|
||||
|
||||
echo "Copy netlify headers config into dist"
|
||||
cp _headers dist/
|
||||
|
||||
echo "List files after build"
|
||||
find dist/
|
||||
@@ -1,7 +1,7 @@
|
||||
// to do fix https://codesandbox.io/p/sandbox/react-konva-infinite-grid-kkndq?file=%2Fsrc%2Findex.js the dotted background doesn't move when draggable.
|
||||
import { Engine } from "./Engine";
|
||||
export { Engine as EngineType };
|
||||
export const KonvaApp = async (element: HTMLDivElement) => {
|
||||
export const KonvaApp = (element: HTMLDivElement) => {
|
||||
const engine = new Engine(element);
|
||||
engine.initializeStage("");
|
||||
return engine;
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
|
||||
// Components
|
||||
import { KonvaCanvasContainer } from "./KonvaCanvasContainer";
|
||||
import { ContextualButtonRetry } from "./ContextualButtonRetry";
|
||||
import { ContextualToolbarImage } from "./ContextualToolbarImage";
|
||||
import { ContextualLoadingBar } from "./ContextualLoadingBar";
|
||||
import { SignaledToolbarMain } from "./SignaledToolbarMain";
|
||||
import { SignaledDialogError } from "./SignaledComponents";
|
||||
import { EngineType } from "~/KonvaApp";
|
||||
import { SignaledDialogs } from "./SignaledDialogs";
|
||||
import {
|
||||
SignaledDialogError,
|
||||
SignaledCanvasDragDropFiles,
|
||||
} from "./OtherSignaledComponents";
|
||||
|
||||
// The KonvaApp is the root of the Konva stage
|
||||
// and only entry point for anything in Konva JS
|
||||
import { EngineType } from "~/KonvaApp";
|
||||
import { KonvaApp } from "~/KonvaApp";
|
||||
|
||||
import { Button } from "~/components/ui";
|
||||
import { uiAccess } from "~/signals";
|
||||
// all the signal-contexts are wrapped in hooks
|
||||
import { useAppUiContext } from "./contextSignals/appUi";
|
||||
|
||||
// common hooks
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
|
||||
export const KonvaRootComponent = ({ className }: { className: string }) => {
|
||||
// This is a hook that will log the number of times the component has rerendered
|
||||
// Let's make sure we only log once
|
||||
useRenderCounter("KonvaRootComponent");
|
||||
|
||||
const appUiContext = useAppUiContext();
|
||||
const engineRef = useRef<EngineType | null>(null);
|
||||
|
||||
const konvaContainerCallbackRef = useCallback((node: HTMLDivElement) => {
|
||||
@@ -31,23 +38,25 @@ export const KonvaRootComponent = ({ className }: { className: string }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
className="absolute bottom-0 left-0"
|
||||
onClick={() => {
|
||||
uiAccess.dialogueError.show({
|
||||
title: "Test Error",
|
||||
message: "This is a test error",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Error Dialogue Test
|
||||
</Button>
|
||||
<KonvaCanvasContainer
|
||||
ref={konvaContainerCallbackRef}
|
||||
className={className}
|
||||
// retreive the classNames from the parent for sizing/styling
|
||||
/>
|
||||
<SignaledToolbarMain />
|
||||
<SignaledCanvasDragDropFiles
|
||||
openAddImage={appUiContext.openAddImage}
|
||||
openAddVideo={appUiContext.openAddVideo}
|
||||
/>
|
||||
<SignaledToolbarMain
|
||||
openAddImage={appUiContext.openAddImage}
|
||||
openAddVideo={appUiContext.openAddVideo}
|
||||
openAIStylize={appUiContext.openAIStylize}
|
||||
/>
|
||||
<SignaledDialogs
|
||||
appUiSignal={appUiContext.signal}
|
||||
resetAll={appUiContext.resetAll}
|
||||
/>
|
||||
|
||||
<ContextualToolbarImage />
|
||||
<ContextualButtonRetry />
|
||||
<ContextualLoadingBar />
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { DialogError, CanvasDragDropFiles } from "~/components/features";
|
||||
import { uiAccess } from "~/signals";
|
||||
|
||||
// global signals
|
||||
import { dialogueError } from "~/signals/uiAccess/dialogueError";
|
||||
import { getFileExtension } from "~/utilities";
|
||||
|
||||
// enums
|
||||
import { IMAGE_FILE_TYPE, VIDEO_FILE_TYPE } from "~/constants/fileTypeEnums";
|
||||
|
||||
export const SignaledDialogError = () => {
|
||||
const props = dialogueError.signal.value;
|
||||
const { isShowing, title, message } = props;
|
||||
const onClose = useCallback(() => {
|
||||
dialogueError.hide();
|
||||
}, []);
|
||||
return (
|
||||
<DialogError
|
||||
isShowing={isShowing}
|
||||
title={title}
|
||||
message={message}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SignaledCanvasDragDropFiles = ({
|
||||
openAddImage,
|
||||
openAddVideo,
|
||||
}: {
|
||||
openAddImage: (file?: File) => void;
|
||||
openAddVideo: (file?: File) => void;
|
||||
}) => {
|
||||
// const [files, setFiles] = useState<File[]>([]);
|
||||
const imageFileTypes = Object.values(IMAGE_FILE_TYPE) as string[];
|
||||
const videoFileTypes = Object.values(VIDEO_FILE_TYPE) as string[];
|
||||
const fileTypes = [...imageFileTypes, ...videoFileTypes];
|
||||
const onSetFiles = useCallback((files: File[]) => {
|
||||
if (files.length < 0) {
|
||||
return;
|
||||
}
|
||||
if (files.length > 1) {
|
||||
uiAccess.dialogueError.show({
|
||||
title: "Error in Adding files",
|
||||
message:
|
||||
"Sorry, we don't support adding multiple files at once, yet! it's coming soon.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const file = files[0];
|
||||
const fileExt = getFileExtension(file);
|
||||
if (imageFileTypes.includes(fileExt)) {
|
||||
openAddImage(file);
|
||||
return;
|
||||
}
|
||||
if (videoFileTypes.includes(fileExt)) {
|
||||
openAddVideo(file);
|
||||
return;
|
||||
}
|
||||
uiAccess.dialogueError.show({
|
||||
title: "Error in Adding files",
|
||||
message: `Sorry, we do not support this file type: .${fileExt.toUpperCase()}`,
|
||||
});
|
||||
return;
|
||||
}, []);
|
||||
return (
|
||||
<CanvasDragDropFiles
|
||||
className="col-span-12 col-start-1 row-span-11 row-start-1"
|
||||
onSetFiles={onSetFiles}
|
||||
fileTypes={fileTypes}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { dialogueError } from "~/signals/uiAccess/dialogueError";
|
||||
|
||||
import { DialogError } from "~/components/features";
|
||||
|
||||
export const SignaledDialogError = () => {
|
||||
const props = dialogueError.signal.value;
|
||||
const { isShowing, title, message } = props;
|
||||
const onClose = useCallback(() => {
|
||||
dialogueError.hide();
|
||||
}, []);
|
||||
return (
|
||||
<DialogError
|
||||
isShowing={isShowing}
|
||||
title={title}
|
||||
message={message}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Signal } from "@preact/signals-react";
|
||||
import { dispatchUiEvents } from "~/signals";
|
||||
import {
|
||||
DialogAddImage,
|
||||
DialogAddVideo,
|
||||
DialogAiStylize,
|
||||
} from "~/components/features";
|
||||
|
||||
import { AppUiSignalType } from "./contextSignals/appUi";
|
||||
|
||||
export const SignaledDialogs = ({
|
||||
appUiSignal,
|
||||
resetAll,
|
||||
}: {
|
||||
appUiSignal: Signal<AppUiSignalType>;
|
||||
resetAll: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<DialogAddImage
|
||||
isOpen={appUiSignal.value.isAddImageOpen ?? false}
|
||||
stagedImage={appUiSignal.value.stagedImage}
|
||||
closeCallback={resetAll}
|
||||
/>
|
||||
<DialogAddVideo
|
||||
isOpen={appUiSignal.value.isAddVideoOpen ?? false}
|
||||
stagedVideo={appUiSignal.value.stagedVideo}
|
||||
closeCallback={resetAll}
|
||||
onUploadedVideo={(response) => {
|
||||
if (!response.data) {
|
||||
return;
|
||||
}
|
||||
dispatchUiEvents.addVideoToEngine({
|
||||
url: response.data.public_bucket_url,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DialogAiStylize
|
||||
isOpen={appUiSignal.value.isAiStylizeOpen ?? false}
|
||||
onRequestAIStylize={(data) => {
|
||||
const { selectedArtStyle: artstyle, ...rest } = data;
|
||||
dispatchUiEvents.aiStylize.dispatchRequest({
|
||||
artstyle,
|
||||
...rest,
|
||||
});
|
||||
}}
|
||||
closeCallback={resetAll}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,30 +1,21 @@
|
||||
import { MouseEventHandler, useCallback, useState } from "react";
|
||||
|
||||
import { MouseEventHandler } from "react";
|
||||
import { toolbarMain } from "~/signals/uiAccess/toolbarMain";
|
||||
import { dispatchers } from "~/signals/uiEvents/toolbarMain";
|
||||
import { dispatchUiEvents } from "~/signals";
|
||||
|
||||
import {
|
||||
ToolbarMain,
|
||||
DialogAddImage,
|
||||
DialogAddVideo,
|
||||
DialogAiStylize,
|
||||
} from "~/components/features";
|
||||
import { ToolbarMain } from "~/components/features";
|
||||
import { LoadingBar } from "~/components/ui";
|
||||
|
||||
import { ToolbarMainButtonNames } from "~/components/features/ToolbarMain/enum";
|
||||
|
||||
const initialState = {
|
||||
isAddVideoOpen: false,
|
||||
isAddImageOpen: false,
|
||||
isAiStylizeOpen: false,
|
||||
};
|
||||
|
||||
export const SignaledToolbarMain = () => {
|
||||
const [state, setState] = useState(initialState);
|
||||
const closeAll = useCallback(() => {
|
||||
setState(initialState);
|
||||
}, []);
|
||||
export const SignaledToolbarMain = ({
|
||||
openAddImage,
|
||||
openAddVideo,
|
||||
openAIStylize,
|
||||
}: {
|
||||
openAddImage: () => void;
|
||||
openAddVideo: () => void;
|
||||
openAIStylize: () => void;
|
||||
}) => {
|
||||
const loadingBar = toolbarMain.loadingBar.signal.value;
|
||||
const buttonProps = Object.values(ToolbarMainButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
@@ -62,51 +53,9 @@ export const SignaledToolbarMain = () => {
|
||||
<ToolbarMain
|
||||
disabled={toolbarMain.signal.value.disabled}
|
||||
buttonProps={buttonProps}
|
||||
openAddImage={() => {
|
||||
setState({
|
||||
...initialState, //this closes all other opened things
|
||||
isAddImageOpen: true,
|
||||
});
|
||||
}}
|
||||
openAddVideo={() => {
|
||||
setState({
|
||||
...initialState, //this closes all other opened things
|
||||
isAddVideoOpen: true,
|
||||
});
|
||||
}}
|
||||
openAIStylize={() => {
|
||||
setState({
|
||||
...initialState, //this closes all other opened things
|
||||
isAiStylizeOpen: true,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DialogAddImage
|
||||
isOpen={state.isAddImageOpen ?? false}
|
||||
closeCallback={closeAll}
|
||||
/>
|
||||
<DialogAddVideo
|
||||
isOpen={state.isAddVideoOpen ?? false}
|
||||
closeCallback={closeAll}
|
||||
onUploadedVideo={(response) => {
|
||||
if (!response.data) {
|
||||
return;
|
||||
}
|
||||
dispatchUiEvents.addVideoToEngine({
|
||||
url: response.data.public_bucket_url,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DialogAiStylize
|
||||
isOpen={state.isAiStylizeOpen ?? false}
|
||||
onRequestAIStylize={(data) => {
|
||||
const { selectedArtStyle: artstyle, ...rest } = data;
|
||||
dispatchUiEvents.aiStylize.dispatchRequest({
|
||||
artstyle,
|
||||
...rest,
|
||||
});
|
||||
}}
|
||||
closeCallback={closeAll}
|
||||
openAddImage={() => openAddImage()}
|
||||
openAddVideo={() => openAddVideo()}
|
||||
openAIStylize={() => openAIStylize()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
import { signal } from "@preact/signals-react";
|
||||
export type AppUiSignalType = {
|
||||
isAddVideoOpen: boolean;
|
||||
stagedVideo: File | null;
|
||||
isAddImageOpen: boolean;
|
||||
stagedImage: File | null;
|
||||
isAiStylizeOpen: boolean;
|
||||
};
|
||||
const appUiInitialState = {
|
||||
isAddVideoOpen: false,
|
||||
stagedVideo: null,
|
||||
isAddImageOpen: false,
|
||||
stagedImage: null,
|
||||
isAiStylizeOpen: false,
|
||||
};
|
||||
export const useAppUiContext = () => {
|
||||
const appUiRef = useRef(signal<AppUiSignalType>(appUiInitialState));
|
||||
const appUiSignal = appUiRef.current;
|
||||
const resetAll = useCallback(() => {
|
||||
appUiSignal.value = appUiInitialState;
|
||||
}, []);
|
||||
const openAddImage = useCallback((file?: File) => {
|
||||
appUiSignal.value = {
|
||||
...appUiInitialState,
|
||||
isAddImageOpen: true,
|
||||
stagedImage: file ? file : null,
|
||||
};
|
||||
}, []);
|
||||
const openAddVideo = useCallback((file?: File) => {
|
||||
appUiSignal.value = {
|
||||
...appUiInitialState,
|
||||
isAddVideoOpen: true,
|
||||
stagedVideo: file ? file : null,
|
||||
};
|
||||
}, []);
|
||||
const openAIStylize = useCallback(() => {
|
||||
appUiSignal.value = {
|
||||
...appUiInitialState,
|
||||
isAiStylizeOpen: true,
|
||||
};
|
||||
}, []);
|
||||
return {
|
||||
signal: appUiSignal,
|
||||
resetAll,
|
||||
openAddImage,
|
||||
openAddVideo,
|
||||
openAIStylize,
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ContextualToolbarImage } from "~/KonvaRootComponent/ContextualToolbarImage";
|
||||
import { ContextualLoadingBar } from "~/KonvaRootComponent/ContextualLoadingBar";
|
||||
import { ContextualButtonRetry } from "~/KonvaRootComponent/ContextualButtonRetry";
|
||||
import { SignaledDialogError } from "~/KonvaRootComponent/SignaledComponents";
|
||||
import { SignaledDialogError } from "~/KonvaRootComponent/OtherSignaledComponents";
|
||||
|
||||
import { useRenderCounter } from "~/hooks/useRenderCounter";
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
DragEventHandler,
|
||||
HTMLAttributes,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { faFilePlus } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
interface CanvasDragDropFilesInterface extends HTMLAttributes<HTMLDivElement> {
|
||||
fileTypes: string[];
|
||||
onSetFiles: (file: File[]) => void;
|
||||
}
|
||||
enum DragStates {
|
||||
IDLE = "idle",
|
||||
READY = "ready",
|
||||
DRAGGING = "dragging",
|
||||
}
|
||||
export const CanvasDragDropFiles = ({
|
||||
fileTypes,
|
||||
onSetFiles,
|
||||
className,
|
||||
children,
|
||||
}: CanvasDragDropFilesInterface) => {
|
||||
const [dragging, setDragging] = useState<DragStates>(DragStates.IDLE);
|
||||
const lastDragging = useRef<DragStates>(DragStates.IDLE);
|
||||
|
||||
const handleDrop: DragEventHandler<HTMLDivElement> = (event) => {
|
||||
event.preventDefault();
|
||||
if (!event.dataTransfer) {
|
||||
return;
|
||||
}
|
||||
const droppedFiles = event.dataTransfer.files;
|
||||
if (droppedFiles.length > 0) {
|
||||
const newFiles = Array.from(droppedFiles) as File[];
|
||||
onSetFiles(newFiles);
|
||||
setDragging(DragStates.READY);
|
||||
}
|
||||
};
|
||||
const handleDragOver: DragEventHandler<HTMLDivElement> = (event) => {
|
||||
event.preventDefault();
|
||||
setDragging((curr) => {
|
||||
if (!event.dataTransfer) {
|
||||
return curr;
|
||||
}
|
||||
if (curr !== DragStates.DRAGGING) {
|
||||
lastDragging.current = curr;
|
||||
}
|
||||
return DragStates.DRAGGING;
|
||||
});
|
||||
};
|
||||
|
||||
const handleDragExit: DragEventHandler<HTMLDivElement> = (event) => {
|
||||
event.preventDefault();
|
||||
setDragging(lastDragging.current);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const mouseLeaveHandler = (event: MouseEvent) => {
|
||||
if (
|
||||
event.clientY <= 0 ||
|
||||
event.clientX <= 0 ||
|
||||
event.clientX >= window.innerWidth ||
|
||||
event.clientY >= window.innerHeight
|
||||
) {
|
||||
setDragging(DragStates.READY);
|
||||
}
|
||||
};
|
||||
const mouseEnterHandler = () => {
|
||||
setDragging((curr) => {
|
||||
if (curr === DragStates.DRAGGING) {
|
||||
return curr;
|
||||
}
|
||||
return DragStates.IDLE;
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener("mouseleave", mouseLeaveHandler);
|
||||
document.addEventListener("mouseenter", mouseEnterHandler);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mouseleave", mouseLeaveHandler);
|
||||
document.removeEventListener("mouseenter", mouseEnterHandler);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
"h-full w-full border-4 border-dashed border-transparent bg-transparent",
|
||||
dragging === DragStates.IDLE && "pointer-events-none",
|
||||
dragging === DragStates.DRAGGING && "border-gray-400 bg-gray-400/50",
|
||||
"relative",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{dragging === DragStates.DRAGGING && (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-4">
|
||||
<FontAwesomeIcon icon={faFilePlus} className="text-5xl" />
|
||||
<p className="mt-1 text-lg">
|
||||
Drag and drop files here to add them to the board!
|
||||
</p>
|
||||
<p className="text-lg">
|
||||
Acceptable file types:{" "}
|
||||
<b>
|
||||
{fileTypes.reduce((acc, curr, idx) => {
|
||||
const prefix = curr[0] !== "." ? "." : "";
|
||||
const suffix = idx === fileTypes.length - 1 ? "" : ", ";
|
||||
return acc + prefix + curr.toUpperCase() + suffix;
|
||||
}, "")}
|
||||
</b>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
<div
|
||||
className="absolute left-0 top-0 h-full w-full"
|
||||
onDrop={handleDrop}
|
||||
onDragEnter={handleDragOver}
|
||||
onDragOver={handleDragOver}
|
||||
onDragExit={handleDragExit}
|
||||
onDragLeave={handleDragExit}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react";
|
||||
|
||||
import { FileUploader, IMAGE_FILE_TYPE } from "../FileUploader";
|
||||
import { FileUploader } from "../FileUploader";
|
||||
import { Button } from "~/components/ui";
|
||||
|
||||
import { IMAGE_FILE_TYPE } from "~/constants/fileTypeEnums";
|
||||
|
||||
import {
|
||||
paperWrapperStyles,
|
||||
dialogBackgroundStyles,
|
||||
@@ -12,13 +14,26 @@ import {
|
||||
import { dispatchUiEvents } from "~/signals/uiEvents/";
|
||||
|
||||
export const DialogAddImage = ({
|
||||
stagedImage = null,
|
||||
isOpen,
|
||||
closeCallback,
|
||||
}: {
|
||||
stagedImage?: File | null;
|
||||
isOpen: boolean;
|
||||
closeCallback: () => void;
|
||||
}) => {
|
||||
const [assetFile, setAssetFile] = useState<File | null>(null);
|
||||
const previouslyStagedImageRef = useRef<File | null>(null);
|
||||
|
||||
const currFile =
|
||||
stagedImage &&
|
||||
stagedImage !== assetFile &&
|
||||
stagedImage !== previouslyStagedImageRef.current
|
||||
? stagedImage
|
||||
: assetFile;
|
||||
if (previouslyStagedImageRef.current !== stagedImage) {
|
||||
previouslyStagedImageRef.current = stagedImage;
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
setAssetFile(null);
|
||||
@@ -26,8 +41,8 @@ export const DialogAddImage = ({
|
||||
}
|
||||
|
||||
function handleEnter() {
|
||||
if (assetFile) {
|
||||
dispatchUiEvents.addImageToEngine(assetFile);
|
||||
if (currFile) {
|
||||
dispatchUiEvents.addImageToEngine(currFile);
|
||||
}
|
||||
handleClose();
|
||||
}
|
||||
@@ -44,18 +59,18 @@ export const DialogAddImage = ({
|
||||
<FileUploader
|
||||
title=""
|
||||
fileTypes={Object.values(IMAGE_FILE_TYPE)}
|
||||
file={assetFile}
|
||||
file={currFile}
|
||||
setFile={(file: File | null) => {
|
||||
setAssetFile(file);
|
||||
}}
|
||||
/>
|
||||
{assetFile && (
|
||||
{currFile && (
|
||||
<div className="relative flex items-center justify-center rounded-xl bg-ui-border">
|
||||
<label className="absolute left-0 top-0 rounded-br-xl rounded-tl-xl border border-ui-border bg-white p-2 shadow-md">
|
||||
Preview
|
||||
</label>
|
||||
<img
|
||||
src={URL.createObjectURL(assetFile)}
|
||||
src={URL.createObjectURL(currFile)}
|
||||
className="border border-dashed border-white"
|
||||
/>
|
||||
</div>
|
||||
@@ -64,7 +79,7 @@ export const DialogAddImage = ({
|
||||
<Button onClick={handleClose} variant="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleEnter} disabled={assetFile === null}>
|
||||
<Button onClick={handleEnter} disabled={currFile === null}>
|
||||
Enter
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -24,20 +24,32 @@ const initialState = {
|
||||
dialogStatus: DialogAddMediaStatuses.STAGING_FILE,
|
||||
};
|
||||
export const DialogAddVideo = ({
|
||||
stagedVideo = null,
|
||||
isOpen,
|
||||
closeCallback,
|
||||
onUploadedVideo,
|
||||
}: {
|
||||
stagedVideo?: File | null;
|
||||
isOpen: boolean;
|
||||
closeCallback: () => void;
|
||||
onUploadedVideo: (response: ApiResponse<MediaFile>) => void;
|
||||
}) => {
|
||||
// useRenderCounter("DialogAddVideo");
|
||||
|
||||
const [{ file, dialogStatus }, setStates] = useState<{
|
||||
file: File | null;
|
||||
dialogStatus: DialogAddMediaStatuses;
|
||||
}>(initialState);
|
||||
const previouslyStagedVideoRef = useRef<File | null>(null);
|
||||
|
||||
const currFile =
|
||||
stagedVideo &&
|
||||
stagedVideo !== file &&
|
||||
stagedVideo !== previouslyStagedVideoRef.current
|
||||
? stagedVideo
|
||||
: file;
|
||||
if (previouslyStagedVideoRef.current !== stagedVideo) {
|
||||
previouslyStagedVideoRef.current = stagedVideo;
|
||||
}
|
||||
console.log(currFile);
|
||||
const trimDataRef = useRef(signal<TrimData | undefined>(undefined));
|
||||
const trimData = trimDataRef.current;
|
||||
|
||||
@@ -81,7 +93,7 @@ export const DialogAddVideo = ({
|
||||
{dialogStatus === DialogAddMediaStatuses.STAGING_FILE && (
|
||||
<>
|
||||
<QuickTrimVideoUploader
|
||||
file={file}
|
||||
file={currFile}
|
||||
onFileStaged={(file) => {
|
||||
setStates((curr) => ({ ...curr, file }));
|
||||
}}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { Signal } from "@preact/signals-react";
|
||||
|
||||
import { FileUploader, VIDEO_FILE_TYPE } from "../FileUploader";
|
||||
import { FileUploader } from "../FileUploader";
|
||||
import { VideoControls } from "./VideoControls";
|
||||
import { TrimmerPlaybar, TrimData } from "./TrimmerPlaybar";
|
||||
|
||||
import { VIDEO_FILE_TYPE } from "~/constants/fileTypeEnums";
|
||||
|
||||
export type { TrimData };
|
||||
|
||||
export const QuickTrimVideoUploader = ({
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
faFileImage,
|
||||
faFileVideo,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { getFileName } from "~/utilities";
|
||||
|
||||
import { VIDEO_FILE_TYPE } from "./enums";
|
||||
import { VIDEO_FILE_TYPE } from "~/constants/fileTypeEnums";
|
||||
// import { getFileName } from "~/utilities";
|
||||
|
||||
interface Props {
|
||||
file: File | null;
|
||||
|
||||
@@ -25,5 +25,3 @@ export const FileUploader = ({ fileTypes, title, file, setFile }: Props) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { IMAGE_FILE_TYPE, VIDEO_FILE_TYPE } from "./enums";
|
||||
@@ -1,3 +1,4 @@
|
||||
export { CanvasDragDropFiles } from "./CanvasDragDropFiles";
|
||||
export { DialogAddImage } from "./DialogAddImage";
|
||||
export { DialogAddVideo } from "./DialogAddVideo";
|
||||
export { DialogAiStylize } from "./DialogAiStylize";
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export enum IMAGE_FILE_TYPE {
|
||||
PNG = "png",
|
||||
JPG = "jpg",
|
||||
JPEG = "jpeg",
|
||||
WEBP = "webp",
|
||||
}
|
||||
|
||||
export enum VIDEO_FILE_TYPE {
|
||||
MP4 = "mp4",
|
||||
}
|
||||
@@ -8,5 +8,5 @@ export const getFileName = (file: File) => {
|
||||
return file.name.substring(0, file.name.lastIndexOf("."));
|
||||
};
|
||||
export const getFileExtension = (file: File) => {
|
||||
return file.name.substring(file.name.lastIndexOf("."));
|
||||
return file.name.substring(file.name.lastIndexOf(".") + 1);
|
||||
};
|
||||
Reference in new issue
Block a user