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

This commit is contained in:
olivicmic committed 2023-07-30 19:27:55 -07:00
1 parent 00f37b1d30
commit 7541f76f72
11 files changed
+324 -35

No files matched your search

@@ -0,0 +1,12 @@
import React from "react";
export default function LoadingIcon() {
return <>
<span
className="spinner-border spinner-border-sm ms-3"
role="status"
aria-hidden="true"
></span>
<span className="visually-hidden">Loading...</span>
</>;
};
@@ -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<HTMLDivElement>): 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 (
<div className="d-flex flex-column gap-3">
{/* Usage refer to https://github.com/KarimMokhtar/react-drag-drop-files */}
<FileUploader {...{ handleChange, maxSize: 50, name: "file", types: FILE_TYPES }}
children={
<div {...{ className: "panel panel-inner upload-zone d-flex align-items-center p-3",
onDragLeave: onDragEvent(1), onDragOver: onDragEvent(0) }}>
<div className="me-4">
<FontAwesomeIcon {...{ className: "upload-icon", icon: file ? faFileAudio : faFileArrowUp }}/>
</div>
<div>
<div className="pb-0">
{file ? (
<span className="filename" title={file.name}>
{file.name.slice(0, file.name.lastIndexOf("."))}
</span>
) : (
<>
<u className="fw-medium">Upload a file</u> or drop it
here...
</>
)}
</div>
<div className="d-flex gap-1">
<div>
{file ? (
<p>
<span className="opacity-50">
{file && `${file.name.split(".").pop().toUpperCase()}`}{" "}
file size: {fileSize}
</span>{" "}
<u className="fw-medium opacity-100 ms-1">Change file</u>
</p>
) : (
<p className="opacity-50">
{FILE_TYPES.join(", ").toString()} supported
</p>
)}
</div>
</div>
</div>
</div>
}
/>
{ file && <>
<div className="panel panel-inner rounded p-3">
<InputVcAudioPlayer {...{ filename: audioLink as string }}/>
</div>
<div className="d-flex gap-3">
<button {...{ className: uploadBtnClass, disabled: uploading || uploadDisabled, onClick: () => handleUploadFile(), type: "submit", }}>
<FontAwesomeIcon {...{ className: "me-2", icon: uploadDisabled ? faCheck : faFileArrowUp, }}/>
{ uploading && <LoadingIcon /> }
</button>
<button className="btn btn-destructive w-100" onClick={handleClear}>
<FontAwesomeIcon icon={faTrash} className="me-2" />
Clear
</button>
</div>
</> }
</div>
);
};
@@ -0,0 +1 @@
export { default } from './Uploader';
@@ -0,0 +1,5 @@
import Uploader from './Uploader';
export {
Uploader,
};
@@ -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<Props> = ({ actionButtons, showButtons, subText, extra, title, titleIcon }) => {
console.log('aaa',extra);
const Extra = extra || null;
// return <div>Hiii</div>;
return <Extra/>;
// return <div className="container-panel hero-section py-4">
// <div className="panel">
// <div className="p-3 py-4 p-md-4">
// <Extra hello="true"/>
// {/* {
// Title ? (<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;
+3
View File
@@ -0,0 +1,3 @@
import useUploader from './useUploader';
export { useUploader };
@@ -0,0 +1 @@
export { default } from "./useUploader";
@@ -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 };
};
@@ -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;
}
}
@@ -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>;
};