diff --git a/src/components/EditFile/EditFile.tsx b/src/components/EditFile/EditFile.tsx index c7eeabc..1804f60 100644 --- a/src/components/EditFile/EditFile.tsx +++ b/src/components/EditFile/EditFile.tsx @@ -29,7 +29,7 @@ import {objectToBase64} from "../../utils/toBase64"; import {RootState} from "../../state/store"; import {setEditVideo, updateInHashMap, updateVideo,} from "../../state/features/videoSlice"; import {QSHARE_FILE_BASE,} from "../../constants/Identifiers.ts"; -import {MultiplePublish} from "../common/MultiplePublish/MultiplePublish"; +import {MultiplePublish} from "../common/MultiplePublish/MultiplePublishAll"; import {TextEditor} from "../common/TextEditor/TextEditor"; import {extractTextFromHTML} from "../common/TextEditor/utils"; import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts"; @@ -65,7 +65,7 @@ export const EditFile = () => { const editVideoProperties = useSelector( (state: RootState) => state.video.editVideoProperties ); - const [publishes, setPublishes] = useState([]); + const [publishes, setPublishes] = useState(null); const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false); const [videoPropertiesToSetToRedux, setVideoPropertiesToSetToRedux] = useState(null); @@ -378,7 +378,11 @@ export const EditFile = () => { }; listOfPublishes.push(requestBodyJson); - setPublishes(listOfPublishes); + const multiplePublish = { + action: "PUBLISH_MULTIPLE_QDN_RESOURCES", + resources: [...listOfPublishes], + }; + setPublishes(multiplePublish); setIsOpenMultiplePublish(true); setVideoPropertiesToSetToRedux({ ...editVideoProperties, @@ -726,6 +730,18 @@ export const EditFile = () => { {isOpenMultiplePublish && ( { + setIsOpenMultiplePublish(false); + setPublishes(null) + if(messageNotification){ + dispatch( + setNotification({ + msg: messageNotification, + alertType: 'error' + }) + ) + } + }} onSubmit={() => { setIsOpenMultiplePublish(false); const clonedCopy = structuredClone(videoPropertiesToSetToRedux); diff --git a/src/components/PublishFile/PublishFile.tsx b/src/components/PublishFile/PublishFile.tsx index 402d198..8366c2d 100644 --- a/src/components/PublishFile/PublishFile.tsx +++ b/src/components/PublishFile/PublishFile.tsx @@ -51,7 +51,7 @@ import { } from "../../constants/Identifiers.ts"; -import { MultiplePublish } from "../common/MultiplePublish/MultiplePublish"; +import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll"; import { CrowdfundSubTitle, CrowdfundSubTitleRow, @@ -114,7 +114,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => { useState(null); const [playlistSetting, setPlaylistSetting] = useState(null); - const [publishes, setPublishes] = useState([]); + const [publishes, setPublishes] = useState(null); const { getRootProps, getInputProps } = useDropzone({ maxFiles: 10, maxSize: 419430400, // 400 MB in bytes @@ -308,8 +308,14 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => { filename: `video_metadata.json`, }; listOfPublishes.push(requestBodyJson); - setPublishes(listOfPublishes); + + const multiplePublish = { + action: "PUBLISH_MULTIPLE_QDN_RESOURCES", + resources: [...listOfPublishes], + }; + setPublishes(multiplePublish); setIsOpenMultiplePublish(true); + } catch (error: any) { let notificationObj: any = null; if (typeof error === "string") { @@ -658,6 +664,18 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => { {isOpenMultiplePublish && ( { + setIsOpenMultiplePublish(false); + setPublishes(null) + if(messageNotification){ + dispatch( + setNotification({ + msg: messageNotification, + alertType: 'error' + }) + ) + } + }} onSubmit={() => { setIsOpenMultiplePublish(false); setIsOpen(false); diff --git a/src/components/common/MultiplePublish/MultiplePublish.tsx b/src/components/common/MultiplePublish/MultiplePublish.tsx deleted file mode 100644 index 3c1c429..0000000 --- a/src/components/common/MultiplePublish/MultiplePublish.tsx +++ /dev/null @@ -1,136 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - Box, - Button, - CircularProgress, - Modal, - Typography, - useTheme, -} from "@mui/material"; -import React, { useCallback, useEffect, useState, useRef } from "react"; -import { ModalBody } from "../../PublishFile/Upload-styles.tsx"; -import { CircleSVG } from "../../../assets/svgs/CircleSVG"; -import { EmptyCircleSVG } from "../../../assets/svgs/EmptyCircleSVG"; - -export const MultiplePublish = ({ publishes, isOpen, onSubmit }) => { - const theme = useTheme(); - const listOfSuccessfulPublishesRef = useRef([]) - const [listOfSuccessfulPublishes, setListOfSuccessfulPublishes] = useState< - any[] - >([]); - const [currentlyInPublish, setCurrentlyInPublish] = useState(null); - const hasStarted = useRef(false); - const publish = useCallback(async (pub: any) => { - await qortalRequest(pub); - }, []); - const [isPublishing, setIsPublishing] = useState(true) - - const handlePublish = useCallback( - async (pub: any) => { - try { - setCurrentlyInPublish(pub?.identifier); - - await publish(pub); - - setListOfSuccessfulPublishes((prev: any) => [...prev, pub?.identifier]); - listOfSuccessfulPublishesRef.current = [...listOfSuccessfulPublishesRef.current, pub?.identifier] - } catch (error) { - console.log({ error }); - await new Promise((res) => { - setTimeout(() => { - res(); - }, 5000); - }); - // await handlePublish(pub); - } - }, - [publish] - ); - - const startPublish = useCallback( - async (pubs: any) => { - setIsPublishing(true) - const filterPubs = pubs.filter((pub)=> !listOfSuccessfulPublishesRef.current.includes(pub.identifier)) - for (const pub of filterPubs) { - await handlePublish(pub); - - } - - if(listOfSuccessfulPublishesRef.current.length === pubs.length){ - onSubmit() - } - setIsPublishing(false) - }, - [handlePublish, onSubmit, listOfSuccessfulPublishes, publishes] - ); - - useEffect(() => { - if (publishes && !hasStarted.current) { - hasStarted.current = true; - startPublish(publishes); - } - }, [startPublish, publishes, listOfSuccessfulPublishes]); - - - return ( - - - {publishes.map((publish: any) => { - return ( - - {publish?.title} - {publish?.identifier === currentlyInPublish ? ( - - ) : listOfSuccessfulPublishes.includes(publish.identifier) ? ( - - ) : ( - - )} - - ); - })} - {!isPublishing && listOfSuccessfulPublishes.length !== publishes.length && ( - <> - Some files were not published. Please try again. It's important that all the files get published. Maybe wait a couple minutes if the error keeps occurring - - - )} - - - - ); -}; diff --git a/src/components/common/MultiplePublish/MultiplePublishAll.tsx b/src/components/common/MultiplePublish/MultiplePublishAll.tsx new file mode 100644 index 0000000..36fc0c3 --- /dev/null +++ b/src/components/common/MultiplePublish/MultiplePublishAll.tsx @@ -0,0 +1,211 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + Box, + Button, + CircularProgress, + Modal, + Typography, + useTheme, +} from "@mui/material"; +import React, { useCallback, useEffect, useState, useRef } from "react"; +import { CircleSVG } from "../../../assets/svgs/CircleSVG"; +import { EmptyCircleSVG } from "../../../assets/svgs/EmptyCircleSVG"; +import { styled } from "@mui/system"; + +interface Publish { + resources: any[]; + action: string; +} + +interface MultiplePublishProps { + publishes: Publish; + isOpen: boolean; + onSubmit: ()=> void + onError: (message?: string)=> void +} +export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: MultiplePublishProps) => { + const theme = useTheme(); + const listOfSuccessfulPublishesRef = useRef([]) + const [listOfSuccessfulPublishes, setListOfSuccessfulPublishes] = useState< + any[] + >([]); + const [listOfUnsuccessfulPublishes, setListOfUnSuccessfulPublishes] = useState< + any[] + >([]); + const [currentlyInPublish, setCurrentlyInPublish] = useState(null); + const hasStarted = useRef(false); + const publish = useCallback(async (pub: any) => { + const lengthOfResources = pub?.resources?.length + const lengthOfTimeout = lengthOfResources * 30000 + return await qortalRequestWithTimeout(pub, lengthOfTimeout); + }, []); + const [isPublishing, setIsPublishing] = useState(true) + + const handlePublish = useCallback( + async (pub: any) => { + try { + setCurrentlyInPublish(pub?.identifier); + setIsPublishing(true) + const res = await publish(pub); + + onSubmit() + setListOfUnSuccessfulPublishes([]) + + } catch (error: any) { + const unsuccessfulPublishes = error?.error?.unsuccessfulPublishes || [] + if(error?.error === 'User declined request'){ + onError() + return + } + + if(error?.error === 'The request timed out'){ + onError("The request timed out") + + return + } + + + if(unsuccessfulPublishes?.length > 0){ + setListOfUnSuccessfulPublishes(unsuccessfulPublishes) + + } + } finally { + + setIsPublishing(false) + } + }, + [publish] + ); + + const retry = ()=> { + let newlistOfMultiplePublishes: any[] = []; + listOfUnsuccessfulPublishes?.forEach((item)=> { + const findPub = publishes?.resources.find((res: any)=> res?.identifier === item.identifier) + if(findPub){ + newlistOfMultiplePublishes.push(findPub) + } + }) + const multiplePublish = { + ...publishes, + resources: newlistOfMultiplePublishes + }; + handlePublish(multiplePublish) + } + + const startPublish = useCallback( + async (pubs: any) => { + await handlePublish(pubs); + }, + [handlePublish, onSubmit, listOfSuccessfulPublishes, publishes] + ); + + useEffect(() => { + if (publishes && !hasStarted.current) { + hasStarted.current = true; + startPublish(publishes); + } + }, [startPublish, publishes, listOfSuccessfulPublishes]); + + + return ( + + + {publishes?.resources?.map((publish: any) => { + const unpublished = listOfUnsuccessfulPublishes.map(item => item?.identifier) + return ( + + {publish?.identifier} + {!isPublishing && hasStarted.current ? ( + <> + {!unpublished.includes(publish.identifier) ? ( + + ) : ( + + )} + + ): } + + + ); + })} + {!isPublishing && listOfUnsuccessfulPublishes.length > 0 && ( + <> + Some files were not published. Please try again. It's important that all the files get published. Maybe wait a couple minutes if the error keeps occurring + + + )} + + + + ); +}; + + +export const ModalBody = styled(Box)(({ theme }) => ({ + position: "absolute", + backgroundColor: theme.palette.background.default, + borderRadius: "4px", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + width: "75%", + maxWidth: "900px", + padding: "15px 35px", + display: "flex", + flexDirection: "column", + gap: "17px", + overflowY: "auto", + maxHeight: "95vh", + boxShadow: + theme.palette.mode === "dark" + ? "0px 4px 5px 0px hsla(0,0%,0%,0.14), 0px 1px 10px 0px hsla(0,0%,0%,0.12), 0px 2px 4px -1px hsla(0,0%,0%,0.2)" + : "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px", + "&::-webkit-scrollbar-track": { + backgroundColor: theme.palette.background.paper, + }, + "&::-webkit-scrollbar-track:hover": { + backgroundColor: theme.palette.background.paper, + }, + "&::-webkit-scrollbar": { + width: "16px", + height: "10px", + backgroundColor: theme.palette.mode === "light" ? "#f6f8fa" : "#292d3e", + }, + "&::-webkit-scrollbar-thumb": { + backgroundColor: theme.palette.mode === "light" ? "#d3d9e1" : "#575757", + borderRadius: "8px", + backgroundClip: "content-box", + border: "4px solid transparent", + }, + "&::-webkit-scrollbar-thumb:hover": { + backgroundColor: theme.palette.mode === "light" ? "#b7bcc4" : "#474646", + }, +})); \ No newline at end of file diff --git a/src/constants/Categories.ts b/src/constants/Categories.ts index 035afc0..7a7d2ab 100644 --- a/src/constants/Categories.ts +++ b/src/constants/Categories.ts @@ -14,43 +14,50 @@ interface Categories { [key: number]: SubCategory[]; } +const sortCategory = (a: SubCategory, b: SubCategory) => { + if (a.name === "Other") return 1; + else if (b.name === "Other") return -1; + else return a.name.localeCompare(b.name); +}; + export const categories = [ {"id": 1, "name": "Software"}, {"id": 2, "name": "Gaming"}, - {"id": 3, "name": "Media"} -]; + {"id": 3, "name": "Media"}, + {"id": 4, "name": "Other"} +].sort(sortCategory); export const subCategories: Categories = { 1: [ {"id": 101, "name": "OS"}, {"id": 102, "name": "Application"}, {"id": 103, "name": "Source Code"}, {"id": 104, "name": "Other"} - ], + ].sort(sortCategory), 2: [ {"id": 201, "name": "NES"}, {"id": 202, "name": "SNES"}, {"id": 203, "name": "PC"}, - {"id": 204, "name": "Other Gaming Systems"} - ], + {"id": 204, "name": "Other"} + ].sort(sortCategory), 3: [ {"id": 301, "name": "Audio"}, {"id": 302, "name": "Video"}, {"id": 303, "name": "Image"}, {"id": 304, "name": "Document"}, - {"id": 305, "name": "Other Media Formats"} - ] + {"id": 305, "name": "Other"} + ].sort(sortCategory) }; -export const subCategories2: Categories = { - 201: [ // NES + +const gamingSystems = [ {"id": 20101, "name": "ROM"}, {"id": 20102, "name": "Romhack"}, {"id": 20103, "name": "Emulator"}, - ], - 202: [ // SNES - {"id": 20201, "name": "ROM"}, - {"id": 20202, "name": "Romhack"}, - {"id": 20203, "name": "Emulator"}, - ], + {"id": 20104, "name": "Guide"}, + {"id": 20105, "name": "Other"}, + ].sort(sortCategory) +export const subCategories2: Categories = { + 201: gamingSystems, // NES + 202: gamingSystems, // SNES 301: [ // Audio {"id": 30101, "name": "Music"}, {"id": 30102, "name": "Podcasts"}, @@ -67,7 +74,7 @@ export const subCategories2: Categories = { {"id": 30113, "name": "Nature Sounds"}, {"id": 30114, "name": "Soundtracks"}, {"id": 30115, "name": "Interviews"} - ], + ].sort(sortCategory), 302: [ // Under Video {"id": 30201, "name": "Movies"}, {"id": 30202, "name": "Series"}, @@ -92,7 +99,7 @@ export const subCategories2: Categories = { {"id": 30221, "name": "Personal Development"}, {"id": 30222, "name": "Other"}, {"id": 30223, "name": "History"} - ], + ].sort(sortCategory), 303: [ // Image {"id": 30301, "name": "Nature"}, {"id": 30302, "name": "Urban & Cityscapes"}, @@ -114,14 +121,14 @@ export const subCategories2: Categories = { {"id": 30318, "name": "Still Life & Objects"}, {"id": 30319, "name": "Architecture & Buildings"}, {"id": 30320, "name": "Landscapes & Seascapes"} - ], + ].sort(sortCategory), 304: [ // Document {"id": 30401, "name": "PDF"}, {"id": 30402, "name": "Word Document"}, {"id": 30403, "name": "Spreadsheet"}, {"id": 30404, "name": "Powerpoint"}, {"id": 30405, "name": "Books"} - ] + ].sort(sortCategory) }; export const subCategories3: Categories = { 30201: [ // Under Movies @@ -141,7 +148,7 @@ export const subCategories3: Categories = { {"id": 3020114, "name": "International Films"}, {"id": 3020115, "name": "Biographies & True Stories"}, {"id": 3020116, "name": "Other"} - ], + ].sort(sortCategory), 30202: [ // Under Series {"id": 3020201, "name": "Dramas"}, {"id": 3020202, "name": "Comedies"}, @@ -159,7 +166,7 @@ export const subCategories3: Categories = { {"id": 3020214, "name": "International Series"}, {"id": 3020215, "name": "Miniseries"}, {"id": 3020216, "name": "Other"} - ], + ].sort(sortCategory), 30405: [ // Under Books {"id": 3040501, "name": "Fiction"}, {"id": 3040502, "name": "Non-Fiction"}, @@ -177,7 +184,7 @@ export const subCategories3: Categories = { {"id": 3040514, "name": "Travel"}, {"id": 3040515, "name": "Comics & Graphic Novels"}, - ], + ].sort(sortCategory), 30101: [ // Under Music {"id": 3010101, "name": "Rock"}, {"id": 3010102, "name": "Pop"}, @@ -199,7 +206,7 @@ export const subCategories3: Categories = { {"id": 3010118, "name": "Children's Music"}, {"id": 3010119, "name": "New Age"}, {"id": 3010120, "name": "Classical Crossover"} - ] + ].sort(sortCategory) }; @@ -207,6 +214,7 @@ export const icons = { 1: softwareIcon, 2: gamingIcon, 3: mediaIcon, + 4: softwareIcon, 302: videoIcon, 301: audioIcon, 304: documentIcon diff --git a/src/pages/Home/FileList.tsx b/src/pages/Home/FileList.tsx index f997cf0..1badb83 100644 --- a/src/pages/Home/FileList.tsx +++ b/src/pages/Home/FileList.tsx @@ -376,7 +376,7 @@ export const FileList = ({ mode }: VideoListProps) => { }} onKeyDown={searchOnEnter} value={filterName} - placeholder="User's Exact Name" + placeholder="User's Name (Exact)" sx={{ marginTop: "20px", borderBottom: "1px solid white",