From 7541f76f72ebd1d0f9fa62701aecd3102d2790ed Mon Sep 17 00:00:00 2001 From: olivicmic Date: Sun, 30 Jul 2023 19:27:55 -0700 Subject: [PATCH] UploaderComponent revised as Uploader at new shared component directory. Uploader uses the new hook useUploader which allows for state to exist external to the component --- .../common/Uploader/LoadingIcon.tsx | 12 ++ .../components/common/Uploader/Uploader.tsx | 113 ++++++++++++++++++ .../src/components/common/Uploader/index.tsx | 1 + .../src/components/common/index.tsx | 5 + .../layout/PageHeader/PageHeader.tsx | 48 ++++---- .../components/layout/{index.js => index.tsx} | 0 packages/fakeyou.com/src/hooks/index.tsx | 3 + .../src/hooks/useUploader/index.tsx | 1 + .../src/hooks/useUploader/useUploader.tsx | 62 ++++++++++ .../lipsync/LipsyncEditor/LipsyncEditor.scss | 41 +++++++ .../lipsync/LipsyncEditor/LipsyncEditor.tsx | 73 +++++++++-- 11 files changed, 324 insertions(+), 35 deletions(-) create mode 100644 packages/fakeyou.com/src/components/common/Uploader/LoadingIcon.tsx create mode 100644 packages/fakeyou.com/src/components/common/Uploader/Uploader.tsx create mode 100644 packages/fakeyou.com/src/components/common/Uploader/index.tsx create mode 100644 packages/fakeyou.com/src/components/common/index.tsx rename packages/fakeyou.com/src/components/layout/{index.js => index.tsx} (100%) create mode 100644 packages/fakeyou.com/src/hooks/index.tsx create mode 100644 packages/fakeyou.com/src/hooks/useUploader/index.tsx create mode 100644 packages/fakeyou.com/src/hooks/useUploader/useUploader.tsx create mode 100644 packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.scss diff --git a/packages/fakeyou.com/src/components/common/Uploader/LoadingIcon.tsx b/packages/fakeyou.com/src/components/common/Uploader/LoadingIcon.tsx new file mode 100644 index 0000000000..dce6894198 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Uploader/LoadingIcon.tsx @@ -0,0 +1,12 @@ +import React from "react"; + +export default function LoadingIcon() { + return <> + + Loading... + ; +}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/Uploader/Uploader.tsx b/packages/fakeyou.com/src/components/common/Uploader/Uploader.tsx new file mode 100644 index 0000000000..a4d2bb8a4b --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Uploader/Uploader.tsx @@ -0,0 +1,113 @@ +import React from "react"; +import { FileUploader } from "react-drag-drop-files"; +import { faCheck, faFileArrowUp, faFileAudio, faTrash } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { InputVcAudioPlayer } from "v2/view/_common/InputVcAudioPlayer"; +import LoadingIcon from "./LoadingIcon"; + +const FILE_TYPES = ["MP3", "WAV", "FLAC", "OGG"]; + +interface Idempotency { token: any; set: (file?: any) => void; } + +interface Props { + audioLink?: string; + file?: any; + fileSet?: (file?: any) => void; + canConvertSet?: (canConvert: boolean) => void; + handleUploadFile?: () => void; + idempotency?: Idempotency; + onChange?: (file?: any) => void; + onClear?: (file?: any) => void; + uploading?: boolean; + uploadDisabled?: boolean; +} + +const n = () => {}; + +export default function Uploader({ audioLink = "", file, fileSet = n, canConvertSet = n, handleUploadFile = n, idempotency = { token: '', set: n }, onChange = n, onClear = n, uploading = false, uploadDisabled = false }: Props) { + + const handleChange = (file: any) => { + onChange({ target: { name: 'uploader', value: file }}); + }; + + const handleClear = () => { onClear(); }; + + const onDragEvent = (onOff: number) => (e: React.DragEvent): void => { + e.preventDefault(); + e.stopPropagation(); + e.currentTarget.classList[onOff ? "add" : "remove" ]("upload-zone-drag"); + }; + + const fileSize = + file && file.size >= 1024 * 1024 + ? (file.size / 1024 / 1024).toFixed(2) + " MB" + : file + ? `${Math.floor(file.size / 1024)} KB` + : null; + + const uploadBtnClass = uploadDisabled + ? "btn btn-uploaded w-100 disabled" + : "btn btn-primary w-100"; + + return ( +
+ {/* Usage refer to https://github.com/KarimMokhtar/react-drag-drop-files */} + +
+ +
+
+
+ {file ? ( + + {file.name.slice(0, file.name.lastIndexOf("."))} + + ) : ( + <> + Upload a file or drop it + here... + + )} +
+
+
+ {file ? ( +

+ + {file && `${file.name.split(".").pop().toUpperCase()}`}{" "} + file size: {fileSize} + {" "} + Change file +

+ ) : ( +

+ {FILE_TYPES.join(", ").toString()} supported +

+ )} +
+
+
+
+ } + /> + { file && <> +
+ +
+
+ + +
+ } + + ); +}; diff --git a/packages/fakeyou.com/src/components/common/Uploader/index.tsx b/packages/fakeyou.com/src/components/common/Uploader/index.tsx new file mode 100644 index 0000000000..2d16ee2adb --- /dev/null +++ b/packages/fakeyou.com/src/components/common/Uploader/index.tsx @@ -0,0 +1 @@ +export { default } from './Uploader'; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/index.tsx b/packages/fakeyou.com/src/components/common/index.tsx new file mode 100644 index 0000000000..bee7e49142 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/index.tsx @@ -0,0 +1,5 @@ +import Uploader from './Uploader'; + +export { + Uploader, +}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/layout/PageHeader/PageHeader.tsx b/packages/fakeyou.com/src/components/layout/PageHeader/PageHeader.tsx index d6f4b85956..e0c8b6e0e4 100644 --- a/packages/fakeyou.com/src/components/layout/PageHeader/PageHeader.tsx +++ b/packages/fakeyou.com/src/components/layout/PageHeader/PageHeader.tsx @@ -1,8 +1,9 @@ import React from "react"; interface Props { + childProps?: any; titleIcon?: JSX.Element; - extra?: React.ComponentType; + titleComponent?: React.ComponentType; title?: string; subText: string; showButtons: boolean; @@ -10,32 +11,23 @@ interface Props { } -//export default function PageHeader({ actionButtons, showButtons, subText, Extra, title, titleIcon }: Props) - -const PageHeader: React.FunctionComponent = ({ actionButtons, showButtons, subText, extra, title, titleIcon }) => { - console.log('aaa',extra); - const Extra = extra || null; - // return
Hiii
; - return ; - // return
- //
- //
- // - // {/* { - // Title ? () : <h1 className="fw-bold text-center text-md-start"> - // { titleIcon } - // { title } - // </h1> - // }*/} - // <p className="text-center text-md-start pt-1">{ subText }</p> - // { showButtons && ( - // <div className="d-flex flex-column flex-md-row gap-3 justify-content-center justify-content-md-start mt-4"> - // { actionButtons } - // </div> - // )} - // </div> - // </div> - // </div>; +export default function PageHeader({ actionButtons, childProps, showButtons, subText, titleComponent: Title, title, titleIcon }: Props) { + return <div className="container-panel hero-section py-4"> + <div className="panel"> + <div className="p-3 py-4 p-md-4"> + { Title ? <Title {...childProps }/> : <h1 className="fw-bold text-center text-md-start"> + { titleIcon } + { title } + </h1> } + <p className="text-center text-md-start pt-1">{ subText }</p> + { showButtons && ( + <div className="d-flex flex-column flex-md-row gap-3 justify-content-center justify-content-md-start mt-4"> + { actionButtons } + </div> + )} + </div> + </div> + </div>; }; -export default PageHeader; +// export default PageHeader; diff --git a/packages/fakeyou.com/src/components/layout/index.js b/packages/fakeyou.com/src/components/layout/index.tsx similarity index 100% rename from packages/fakeyou.com/src/components/layout/index.js rename to packages/fakeyou.com/src/components/layout/index.tsx diff --git a/packages/fakeyou.com/src/hooks/index.tsx b/packages/fakeyou.com/src/hooks/index.tsx new file mode 100644 index 0000000000..8ff5d6100e --- /dev/null +++ b/packages/fakeyou.com/src/hooks/index.tsx @@ -0,0 +1,3 @@ +import useUploader from './useUploader'; + +export { useUploader }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/hooks/useUploader/index.tsx b/packages/fakeyou.com/src/hooks/useUploader/index.tsx new file mode 100644 index 0000000000..bd6d3c6c8e --- /dev/null +++ b/packages/fakeyou.com/src/hooks/useUploader/index.tsx @@ -0,0 +1 @@ +export { default } from "./useUploader"; \ No newline at end of file diff --git a/packages/fakeyou.com/src/hooks/useUploader/useUploader.tsx b/packages/fakeyou.com/src/hooks/useUploader/useUploader.tsx new file mode 100644 index 0000000000..632ea77018 --- /dev/null +++ b/packages/fakeyou.com/src/hooks/useUploader/useUploader.tsx @@ -0,0 +1,62 @@ +import { useState } from 'react'; +import { v4 as uuidv4 } from "uuid"; +import { UploadAudio, UploadAudioIsOk, UploadAudioRequest } from "@storyteller/components/src/api/upload/UploadAudio"; + +const n = (o: any) => o; + +interface Props { + formClearedSet?: (cleared: boolean) => void; +} + +export default function useUploader({ formClearedSet = n }: Props) { + const [file, fileSet] = useState<any>(undefined); + const [audioLink, audioLinkSet] = useState<string>(); + const [uploading, uploadingSet] = useState(false); + const [uploadDisabled, uploadDisabledSet] = useState<boolean>(false); + const [mediaUploadToken, mediaUploadTokenSet] = useState<string | undefined>(undefined); + const [convertIdempotencyToken, convertIdempotencyTokenSet] = useState(uuidv4()); + const [canConvert, canConvertSet] = useState(false); + const [iToken, iTokenSet] = useState(uuidv4()); // Auto generated + const resetIToken = () => iTokenSet(uuidv4()); + + const handleUploadFile = async () => { + if (file === undefined) { return false; } + + uploadingSet(true); + + const request: UploadAudioRequest = { + uuid_idempotency_token: iToken, + file: file, + source: "file", + }; + + let result = await UploadAudio(request); + + if (UploadAudioIsOk(result)) { + uploadDisabledSet(true); + mediaUploadTokenSet(result.upload_token); + formClearedSet(false); + canConvertSet(true); + } + + uploadingSet(false); + }; + const inputChange = (file?: any) => { + convertIdempotencyTokenSet(uuidv4()); + fileSet(file || null); + audioLinkSet(file ? URL.createObjectURL(file) : ""); + canConvertSet(false); + resetIToken(); + uploadDisabledSet(false); + formClearedSet(!file); + }; + const onChange = ({ target = {} }: { target: any }) => { + inputChange(target.value); + }; + const onClear = () => { + inputChange(); + mediaUploadTokenSet(undefined); + }; + + return { audioLink, canConvert, convertIdempotencyToken, file, fileSet, handleUploadFile, idempotency: { token: iToken, set: iTokenSet}, mediaUploadToken, mediaUploadTokenSet, onChange, onClear, resetIToken, uploading, uploadDisabled, uploadingSet }; +}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.scss b/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.scss new file mode 100644 index 0000000000..3e8d0401f6 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.scss @@ -0,0 +1,41 @@ +.progress-header { + display: flex; + align-items: center; + margin-bottom: 1rem; + + .progress-heading { + display: inline-block; + margin: auto 1rem auto 0; + } + + .async-progress-tracker { + display: inline-flex; + justify-content: space-around; + flex-grow: 1; + margin-right: 1rem; + padding-left: 0; + + li { + display: flex; + align-items: center; + font-size: 1.25rem; + font-weight: 500; + } + + svg { + border: 2px solid #fff; + border-radius: 50%; + margin-right: .5rem; + height: 2rem; + width: 2rem; + stroke: white; + } + } + + button { + margin: 0 0 0 auto; + background-color: #ccccff; + // flex-grow: 1; + width: fit-content; + } +} \ No newline at end of file diff --git a/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.tsx b/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.tsx index 98cdddf69b..667c78c6b7 100644 --- a/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/LipsyncEditor.tsx @@ -1,14 +1,73 @@ import React from "react"; -import { PageHeader } from "components/layout" +import { useUploader } from "hooks"; +import { Uploader } from "components/common"; +import { PageHeader } from "components/layout"; +import './LipsyncEditor.scss'; -const Title: React.ComponentType = () => <div> - <h1 className="fw-bold text-center text-md-start"> - Hello - </h1> -</div>; +// interface Props { +// titleIcon?: JSX.Element; +// extra?: React.ComponentType; +// title?: string; +// subText: string; +// showButtons: boolean; +// actionButtons?: JSX.Element; +// } + +const ProgressCheck = ({ disabled = false }: {disabled?: boolean}) => <svg> + { !disabled && <polyline {...{ + fill: 'none', + points: '7 16 12 20 20 10', + strokeLinecap: 'round', + strokeLinejoin: 'round', + strokeWidth: '4', + }}/> } +</svg>; + +const Title = ({ ...rest }) => { + console.log('🥚',rest) + return <div {...{ className: 'progress-header' }}> + <h1 {...{ className: "fw-bold text-center text-md-start progress-heading" }}> + Generate a lipsync + </h1> + <ul {...{ className: 'async-progress-tracker' }}> + <li> + <ProgressCheck {...{}}/> + Image + </li> + <li> + <ProgressCheck {...{ disabled: false }}/> + Audio + </li> + </ul> + <button className="btn btn-primary"> + Generate + </button> + </div> +}; export default function LipsyncEditor({ ...rest }) { + const { file: audioFile, ...uploader } = useUploader({}); + + const subText = "Select an image with a clear face, or one of our existing templates, then choose either text to speech or uploaded audio(eg. music). Then you can generate a beautifully lipsynced video."; + const headerProps = { + childProps: { audioFile }, + titleComponent: Title, + subText, + showButtons: false + }; + return <div> - <PageHeader {...{ extra: Title, subText: "some text", showButtons: false,}}/> + <PageHeader { ...headerProps }/> + <div className="container"> + <div className="panel d-flex p-md-4"> + <div {...{ className: "col-12 col-lg-6" }}> + <h5>Image</h5> + </div> + <div {...{ className: "col-12 col-lg-6" }}> + <h5>Audio</h5> + <Uploader {...{ file: audioFile, ...uploader }}/> + </div> + </div> + </div> </div>; }; \ No newline at end of file