diff --git a/package.json b/package.json index b5493da5ec..662268d411 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "dependencies": { "@react-spring/types": "^9.7.3", + "moment": "^2.29.4", "universal-cookie": "^6.1.1", "yarn": "^1.22.19" }, diff --git a/packages/components/src/api/voice_designer/voice_dataset_samples/DeleteSample.ts b/packages/components/src/api/voice_designer/voice_dataset_samples/DeleteSample.ts index f3ad28cf02..555824999b 100644 --- a/packages/components/src/api/voice_designer/voice_dataset_samples/DeleteSample.ts +++ b/packages/components/src/api/voice_designer/voice_dataset_samples/DeleteSample.ts @@ -1,15 +1,15 @@ import MakeRequest from "../../MakeRequest"; -export interface DreateVoiceRequest { +export interface DeleteSampleRequest { set_delete: boolean; as_mod: boolean; } -export interface DreateVoiceResponse { +export interface DeleteSampleResponse { success: boolean } -export const DeleteDataset = MakeRequest({ +export const DeleteSample = MakeRequest({ method: "DELETE", routingFunction: (sampleToken: string) => `/v1/voice_designer/sample/${ sampleToken }/delete`, }); diff --git a/packages/components/src/api/voice_designer/voice_dataset_samples/ListSamplesForDataset.ts b/packages/components/src/api/voice_designer/voice_dataset_samples/ListSamplesForDataset.ts index 1db3716992..db7c6fe7f4 100644 --- a/packages/components/src/api/voice_designer/voice_dataset_samples/ListSamplesForDataset.ts +++ b/packages/components/src/api/voice_designer/voice_dataset_samples/ListSamplesForDataset.ts @@ -3,19 +3,8 @@ import MakeRequest from "../../MakeRequest"; export interface ListSamplesForDatasetRequest {} export interface ListSamplesForDatasetResponse { - sample_token: string, - - media_file_token: string, - media_type: string, - - public_bucket_directory_hash: string, - maybe_public_bucket_prefix?: string, - maybe_public_bucket_extension?: string, - - maybe_creator_user_token?: string, - - created_at: Date, - updated_at: Date, + samples: any, + success: true } export const ListSamplesForDataset = MakeRequest({ diff --git a/packages/fakeyou.com/src/components/common/AudioBlobPreview/AudioBlobPreview.scss b/packages/fakeyou.com/src/components/common/AudioBlobPreview/AudioBlobPreview.scss new file mode 100644 index 0000000000..967ca1da2b --- /dev/null +++ b/packages/fakeyou.com/src/components/common/AudioBlobPreview/AudioBlobPreview.scss @@ -0,0 +1,4 @@ +.fy-audio-blob { + padding: 1rem; + margin-bottom: 1rem; +} \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/AudioBlobPreview/AudioBlobPreview.tsx b/packages/fakeyou.com/src/components/common/AudioBlobPreview/AudioBlobPreview.tsx new file mode 100644 index 0000000000..d6be44c70b --- /dev/null +++ b/packages/fakeyou.com/src/components/common/AudioBlobPreview/AudioBlobPreview.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { a, useTransition } from "@react-spring/web"; +import { InputVcAudioPlayer } from "v2/view/_common/InputVcAudioPlayer"; +import { FileActions } from "components/common"; +import "./AudioBlobPreview.scss"; + +interface Props { + blob?: any, + clear?: any, + file?: any, + hideActions: boolean, + onRest?: () => void, + success?: any, + submit?: () => void, + working?: any +} + +export default function AudioBlobPreview({ clear, blob, file, hideActions, onRest, success, submit, working }: Props) { + const transitions = useTransition(file, { + config: { tension: 120, friction: 15 }, + from: { opacity: 0 }, + enter: { opacity: 1 }, + leave: { opacity: 0 }, + onRest, + }); + + return transitions((style, i) => i ? +
+ +
+ { + !hideActions && + } +
: null ); +}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/AudioBlobPreview/index.ts b/packages/fakeyou.com/src/components/common/AudioBlobPreview/index.ts new file mode 100644 index 0000000000..6c44e7fa66 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/AudioBlobPreview/index.ts @@ -0,0 +1 @@ +export { default } from './AudioBlobPreview'; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/AudioInput/AudioInput.tsx b/packages/fakeyou.com/src/components/common/AudioInput/AudioInput.tsx index 9466897ab9..ce33446072 100644 --- a/packages/fakeyou.com/src/components/common/AudioInput/AudioInput.tsx +++ b/packages/fakeyou.com/src/components/common/AudioInput/AudioInput.tsx @@ -1,7 +1,7 @@ import React from "react"; -import { a, useTransition } from '@react-spring/web'; -import { InputVcAudioPlayer } from "v2/view/_common/InputVcAudioPlayer"; -import { FileActions, FileDetails, FileLabel, FileWrapper } from 'components/common'; +// import { a, useTransition } from '@react-spring/web'; +// import { InputVcAudioPlayer } from "v2/view/_common/InputVcAudioPlayer"; +import { FileDetails, FileLabel, FileWrapper } from 'components/common'; import { faFileAudio } from "@fortawesome/pro-solid-svg-icons"; import './AudioInput.scss' @@ -9,39 +9,38 @@ const fileTypes = ["MP3", "WAV", "FLAC", "OGG"]; interface Props { blob?: string; + children?: any; clear?: (file?: any) => void; file?: any; - hideActions?: boolean; + hideDetails?: boolean; hideClearDetails?: boolean; inputProps?: any; - onRest?: () => void; - success?: boolean; - submit?: () => void; - working?: boolean; [x:string]: any; } const n = () => {}; -export default function AudioInput({ blob = "", clear = n, file, hideActions, hideClearDetails, inputProps, onRest = n, success = false, submit = n, working = false, ...rest }: Props) { +export default function AudioInput({ children, clear = n, file, hideDetails, hideClearDetails, inputProps, ...rest }: Props) { - const transitions = useTransition(file, { - config: { tension: 120, friction: 15 }, - from: { opacity: 0 }, - enter: { opacity: 1 }, - leave: { opacity: 0 }, - onRest, - }); + // const transitions = useTransition(file, { + // config: { tension: 120, friction: 15 }, + // from: { opacity: 0 }, + // enter: { opacity: 1 }, + // leave: { opacity: 0 }, + // onRest, + // }); return
{ file ? : } - { transitions((style, i) => i ? -
- -
- { !hideActions && } -
: null ) } + { children + // transitions((style, i) => i ? + //
+ // + //
+ // { !hideActions && } + //
: null ) + }
; }; \ 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 index 0e20205b93..881cf8429c 100644 --- a/packages/fakeyou.com/src/components/common/index.tsx +++ b/packages/fakeyou.com/src/components/common/index.tsx @@ -1,3 +1,4 @@ +import AudioBlobPreview from "./AudioBlobPreview"; import AudioInput from "./AudioInput"; import BasicVideo from "./BasicVideo"; import Button from "./Button"; @@ -20,6 +21,7 @@ import TempInput from "./TempInput"; import TempSelect from "./TempSelect"; export { + AudioBlobPreview, AudioInput, BasicVideo, Button, diff --git a/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx b/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx index 65738f1796..2acd014f4e 100644 --- a/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx +++ b/packages/fakeyou.com/src/components/layout/InferenceJobsList/InferenceJobsList.tsx @@ -28,7 +28,7 @@ interface JobsListProps{ export default function InferenceJobsList({ filter, onSelect, statusTxt, t, inferenceJobs }: JobsListProps) { // const { inferenceJobs = [] } = useInferenceJobs({ type: 0 }); // const oldJobs = thejobs || inferenceJobs; - console.log("🥬",inferenceJobs); + const [pending, pendingSet] = useState({ success: true, pending_job_count: 0, @@ -97,7 +97,6 @@ export default function InferenceJobsList({ filter, onSelect, statusTxt, t, infe { jobs // .filter(filter) .map((job: any, key: number) => { - console.log("🍇",job); return
diff --git a/packages/fakeyou.com/src/hooks/useFile/useFile.tsx b/packages/fakeyou.com/src/hooks/useFile/useFile.tsx index 82b84c39bb..103f835e0a 100644 --- a/packages/fakeyou.com/src/hooks/useFile/useFile.tsx +++ b/packages/fakeyou.com/src/hooks/useFile/useFile.tsx @@ -12,21 +12,12 @@ interface Props { export default function useFile({ debug, onChange = n, onClear = n, onSubmit = n }: Props) { const [file, fileSet] = useState(undefined); const [blob, blobSet] = useState(); - const [status, statusSet] = useState(0); const inputRef = useRef(null); - const submit = async () => { - if (file === undefined) { return false; } - statusSet(1); - const onUploadResult = await onSubmit(file); - if (onUploadResult) { statusSet(2); } - else { statusSet(3); } - }; const fileChange = (inputFile?: any) => { - onChange(file); + onChange(inputFile); fileSet(inputFile || null); blobSet(inputFile ? URL.createObjectURL(inputFile) : ""); - statusSet(0); }; const inputChange = ({ target = {} }: { target: any }) => { fileChange(target.value); @@ -35,7 +26,6 @@ export default function useFile({ debug, onChange = n, onClear = n, onSubmit = n if (inputRef?.current?.value) inputRef.current.value = ''; fileChange(); onClear(); - statusSet(0); }; return { @@ -45,10 +35,6 @@ export default function useFile({ debug, onChange = n, onClear = n, onSubmit = n inputProps: { onChange: inputChange, inputRef - }, - status, - submit, - success: status === 2, - working: status === 1, + } }; }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/FaceAnimatorInput.tsx b/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/FaceAnimatorInput.tsx index bbc6f6a1f9..e1afbd1063 100644 --- a/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/FaceAnimatorInput.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/lipsync/LipsyncEditor/FaceAnimatorInput.tsx @@ -1,6 +1,6 @@ import React from "react"; import { animated } from "@react-spring/web"; -import { AudioInput, BasicVideo, Checkbox, ImageInput, SegmentButtons } from "components/common"; +import { AudioBlobPreview, AudioInput, BasicVideo, Checkbox, ImageInput, SegmentButtons } from "components/common"; import { FaceAnimatorSlide } from "./FaceAnimatorTypes"; export default function FaceAnimatorInput({ @@ -35,9 +35,17 @@ export default function FaceAnimatorInput({ onRest: (p: any, c: any, item: any, l: any) => { toggle.audio(!!audioProps.file); }, - hideActions: true, + // hideActions: true, }} - /> + > + { + toggle.audio(!!audioProps.file); + }, + hideActions: true, + }}/> +
diff --git a/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerFormPage.tsx b/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerFormPage.tsx index ae7dcb82f9..8b0a5f2e14 100644 --- a/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerFormPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerFormPage.tsx @@ -79,15 +79,13 @@ function VoiceDesignerFormPage({ enqueueInferenceJob }: { enqueueInferenceJob: a ? ["Edit Details", "Edit Samples"] : ["Voice Details", "Upload Samples"]; - const key = !isNewCreation && (dataset_token || uuidv4()); + // const key = !isNewCreation && (dataset_token || uuidv4()); const displayStep = (step: any) => { switch (step) { case 0: return ; case 1: - return ( - - ); + return ; default: return null; } diff --git a/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerMainPage.tsx b/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerMainPage.tsx index b9be9ffa5e..a8777ad320 100644 --- a/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerMainPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerMainPage.tsx @@ -9,8 +9,11 @@ import Container from "components/common/Container"; import { NavLink } from "react-router-dom"; import ListItems from "./components/NewList"; import Modal from "components/common/Modal"; +import { Button } from "components/common"; import useVoiceRequests from "./useVoiceRequests"; import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faMicrophone } from "@fortawesome/pro-solid-svg-icons"; import { FrontendInferenceJobType, @@ -25,14 +28,9 @@ function VoiceDesignerMainPage({ usePrefixedDocumentTitle("AI Voice Designer"); const { pathname } = useLocation(); const { t } = useLocalize("FaceAnimator"); - const { datasets, voices, isFetching } = useVoiceRequests({ - requestDatasets: true, - requestVoices: true, - }); + const { datasets, voices, isLoading } = useVoiceRequests({ requestDatasets: true,requestVoices: true }); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); - const view = ["/voice-designer/datasets", "/voice-designer/voices"].indexOf( - pathname - ); + const view = ["/voice-designer/datasets", "/voice-designer/voices"].indexOf(pathname); const [deleteItem, setDeleteItem] = useState(""); const [deleteType, setDeleteType] = useState(""); const [deleteText, setDeleteText] = useState({ @@ -76,26 +74,24 @@ function VoiceDesignerMainPage({ history.push(`/voice-designer/voice/${token}`); }; - const voiceClick = - (todo: any, type: string) => - ({ target }: { target: any }) => { - let voiceToken = - voices.list[target.name.split(",")[0].split(":")[1]].voice_token; - todo(voiceToken, type); - }; + const DataBadge = () => Dataset; + const VoiceBadge = () => ; - const datasetClick = - (todo: any, type: string) => - ({ target }: { target: any }) => { - let datasetToken = - datasets.list[target.name.split(",")[0].split(":")[1]].dataset_token; - todo(datasetToken, type); - }; + // these need to be abstracted to use over again -V + const voiceClick = (todo: any, type: string) => ({ target }: { target: any }) => { + let voiceToken = voices.list[target.name.split(",")[0].split(":")[1]].voice_token; + todo(voiceToken, type); + }; + + const datasetClick = (todo: any, type: string) => ({ target }: { target: any }) => { + let datasetToken = datasets.list[target.name.split(",")[0].split(":")[1]].dataset_token; + todo(datasetToken, type); + }; const actionDataSets = datasets.list.map((dataset, i) => { return { ...dataset, - badge: "dataset", + badge: DataBadge, buttons: [ { label: "Edit", @@ -117,7 +113,7 @@ function VoiceDesignerMainPage({ const actionVoices = voices.list.map((voice, i) => { return { ...voice, - badge: "voice", + badge: VoiceBadge, buttons: [ { label: "Edit", @@ -157,6 +153,18 @@ function VoiceDesignerMainPage({ to: "/voice-designer/create", }; + const dataPlaceholder = () =>
+
+ You haven't created any voices. +
+
; + return ( <> @@ -204,10 +212,7 @@ function VoiceDesignerMainPage({
- +
diff --git a/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerVoiceEditPage.tsx b/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerVoiceEditPage.tsx index fcf0824f2b..f2820c747f 100644 --- a/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerVoiceEditPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/voice_designer/VoiceDesignerVoiceEditPage.tsx @@ -88,7 +88,6 @@ function VoiceDesignerVoiceEditPage() {
- aaaaa * { + margin-right: .5rem; +} diff --git a/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/NewList.tsx b/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/NewList.tsx index e3dfeb87dd..b68d4fbc30 100644 --- a/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/NewList.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/NewList.tsx @@ -1,124 +1,64 @@ -import React from "react"; -import Button from "components/common/Button"; -import { faMicrophone, faPlus } from "@fortawesome/pro-solid-svg-icons"; +import React from 'react'; +import Skeleton from "components/common/Skeleton"; +import { Button } from "components/common"; import "./ListItems/ListItems.scss"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import Skeleton from "components/common/Skeleton"; + interface ListItemsProps { data: any[]; + dataPlaceholder?: any; isLoading: boolean; } interface ListItem { - badge: "voice" | "dataset"; + badge?: any; buttons: any; index: number; isCreating?: boolean; name: string; } -const Working = () => ( -
-

Voice is being created...

-
- Loading... -
-
-); +const LoaderView = () =>
+

+ +

+
; -const ItemRow = ({ - badge, - buttons, - index, - isCreating, - name, - ...rest -}: ListItem) => { - return ( -
-
- {badge === "dataset" ? ( -
- Dataset -
{name}
-
- ) : null} - {badge === "voice" ? ( -
-
- - - - {name} -
-
- ) : null} -
-
- {isCreating ? ( - - ) : ( -
- {buttons && buttons.length - ? buttons.map((action: any, key: number) => { - return ( -
- )} -
+const ItemRow = ({ badge: Badge, buttons, index, isCreating, name, ...rest }: ListItem) => { + return
+
+
+ { Badge ? : null } + { name } +
- ); +
+
+ {buttons && buttons.length + ? buttons.map((action: any, key: number) => { + return ( +
+
+
; }; -export default function NewList({ data, isLoading }: ListItemsProps) { - const DataPlaceholder = () => ( -
-
You haven't created any voices.
-
- ); +export default function NewList({ data, dataPlaceholder, isLoading }: ListItemsProps) { + const DataPlaceholder = dataPlaceholder || null; - if (isLoading) { - const loaderView = ( -
-

- -

-
- ); - - return ( -
- {[...Array(2)].map((_, i) => ( - {loaderView} - ))} -
- ); - } - - return data.length ? ( -
- {data.map((item, key) => ( - - ))} -
- ) : ( - - ); -} + return isLoading ? : + data.length ?
+ { data.map((item, key) => ) } +
: + ; +}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/steps/UploadSamples.tsx b/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/steps/UploadSamples.tsx index af3fdf0b02..cd4fdff0d4 100644 --- a/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/steps/UploadSamples.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/voice_designer/components/steps/UploadSamples.tsx @@ -1,31 +1,94 @@ -import React, { useState } from "react"; -import VoiceDesignerUploadComponent from "../VoiceDesignerUploadComponent"; +import React, { useEffect, useState } from "react"; +// import VoiceDesignerUploadComponent from "../VoiceDesignerUploadComponent"; +import { AudioInput } from "components/common"; + +import moment from 'moment'; + +import ListItems from "../NewList"; import { v4 as uuidv4 } from "uuid"; +import { ListSamplesForDataset } from "@storyteller/components/src/api/voice_designer/voice_dataset_samples/ListSamplesForDataset"; +import { UploadSample } from "@storyteller/components/src/api/voice_designer/voice_dataset_samples/UploadSample"; +import { DeleteSample } from "@storyteller/components/src/api/voice_designer/voice_dataset_samples/DeleteSample"; + +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faWaveform } from "@fortawesome/pro-solid-svg-icons"; + + function UploadSamples({ audioProps, datasetToken, setAudioSamplesReady }: { audioProps: any, datasetToken?: string, setAudioSamplesReady:any }) { - /* eslint-disable @typescript-eslint/no-unused-vars */ - const [convertLoading, setConvertLoading] = useState(false); - const [canConvert, setCanConvert] = useState(false); - const [mediaUploadToken, setMediaUploadToken] = useState( - undefined - ); - const [convertIdempotencyToken, setConvertIdempotencyToken] = useState( - uuidv4() - ); - /* eslint-disable @typescript-eslint/no-unused-vars */ + // const [convertLoading, setConvertLoading] = useState(false); + // const [canConvert, setCanConvert] = useState(false); + // const [mediaUploadToken, setMediaUploadToken] = useState(); + // const [formIsCleared, setFormIsCleared] = useState(false); + const [uploaded,uploadedSet] = useState(false); + const [samples,samplesSet] = useState([]); + const [listFetched,listFetchedSet] = useState(false); - const [formIsCleared, setFormIsCleared] = useState(false); + const SampleBadge = () => ; - const changeConvertIdempotencyToken = () => { - setConvertIdempotencyToken(uuidv4()); + const sampleClick = () => ({ target }: { target: any }) => { + let sampleToken = samples[target.name.split(",")[0].split(":")[1]].sample_token; + DeleteSample(sampleToken,{ as_mod: false, set_delete: true }) + .then((res) => { + listFetchedSet(false); + // set unBusy here + }); }; + const actionSamples = samples.map((sample: any, i: number) => { + let date = new Date(sample.created_at); + return { + ...sample, + badge: SampleBadge, + buttons: [ + { + label: "Delete", + small: true, + variant: "secondary", + onClick: sampleClick() + }, + ], + name: `Sample from ${ date ? moment(date).format("MMMM Do YYYY, h:mm a") : "" }`, + }; + }); + + useEffect(() => { + if (audioProps.file && datasetToken && !uploaded) { + uploadedSet(true); + UploadSample("",{ + dataset_token: datasetToken || "", + file: audioProps.file, + uuid_idempotency_token: uuidv4(), + }) + .then((res) => { + if (res.success) { + audioProps.clear(); + uploadedSet(false); + listFetchedSet(false); + } + }); + } + + + if (datasetToken && !listFetched) { + listFetchedSet(true); + ListSamplesForDataset(datasetToken,{}) + .then((res) => { + if (res.success && res.samples) { + samplesSet(res.samples); + } + }); + } + + },[audioProps, datasetToken, listFetched, uploaded]); + return (
- + + { samples.length ? : null }
diff --git a/packages/fakeyou.com/src/v2/view/pages/voice_designer/useVoiceRequests.tsx b/packages/fakeyou.com/src/v2/view/pages/voice_designer/useVoiceRequests.tsx index 0a27cfe44c..6949ff96d6 100644 --- a/packages/fakeyou.com/src/v2/view/pages/voice_designer/useVoiceRequests.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/voice_designer/useVoiceRequests.tsx @@ -152,7 +152,6 @@ export default function useVoiceRequests({ }, [user, datasetStatus, voicesStatus]); return { - isFetching: isBusy(datasetStatus) || isBusy(voicesStatus), datasets: { byToken: datasetByToken, create: createDataset, @@ -166,6 +165,7 @@ export default function useVoiceRequests({ inference: { enqueue: EnqueueTts, }, + isLoading: isBusy(datasetStatus) || isBusy(voicesStatus), languages, visibilityOptions, voices: { diff --git a/yarn.lock b/yarn.lock index df1f9bd527..8540c73f19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4576,6 +4576,7 @@ __metadata: craco: ^0.0.3 cross-env: ^7.0.3 lerna: ^4.0.0 + moment: ^2.29.4 universal-cookie: ^6.1.1 yarn: ^1.22.19 languageName: unknown @@ -14797,6 +14798,13 @@ __metadata: languageName: node linkType: hard +"moment@npm:^2.29.4": + version: 2.29.4 + resolution: "moment@npm:2.29.4" + checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e + languageName: node + linkType: hard + "move-concurrently@npm:^1.0.1": version: 1.0.1 resolution: "move-concurrently@npm:1.0.1"