Browse Source

Merge pull request #1 from QortalSeth/main

Publish Title TextField allows more characters are allowed than before
pull/2/head
Qortal Dev 8 months ago committed by GitHub
parent
commit
fa0a1dc16a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 76
      src/components/EditFile/EditFile.tsx
  2. 0
      src/components/EditFile/Upload-styles.tsx
  3. 11
      src/components/EditPlaylist/EditPlaylist.tsx
  4. 6
      src/components/PlaylistListEdit/PlaylistListEdit.tsx
  5. 2
      src/components/Playlists/Playlists.tsx
  6. 59
      src/components/PublishFile/PublishFile.tsx
  7. 26
      src/components/PublishFile/Upload-styles.tsx
  8. 6
      src/components/common/Comments/CommentEditor.tsx
  9. 8
      src/components/common/Comments/CommentSection.tsx
  10. 136
      src/components/common/MultiplePublish/MultiplePublish.tsx
  11. 211
      src/components/common/MultiplePublish/MultiplePublishAll.tsx
  12. 6
      src/components/layout/Navbar/Navbar.tsx
  13. 221
      src/constants/Categories.ts
  14. 17
      src/constants/Identifiers.ts
  15. 1
      src/constants/Misc.ts
  16. 237
      src/constants/index.ts
  17. 22
      src/hooks/useFetchFiles.tsx
  18. 4
      src/pages/Home/Channels.tsx
  19. 0
      src/pages/Home/FileList-styles.tsx
  20. 48
      src/pages/Home/FileList.tsx
  21. 17
      src/pages/Home/FileListComponentLevel.tsx
  22. 4
      src/pages/Home/Home.tsx
  23. 4
      src/pages/IndividualProfile/IndividualProfile.tsx
  24. 7
      src/pages/VideoContent/VideoContent.tsx
  25. 4
      src/wrappers/GlobalWrapper.tsx

76
src/components/EditVideo/EditVideo.tsx → src/components/EditFile/EditFile.tsx

@ -1,17 +1,10 @@
import React, { useEffect, useState } from "react";
import React, {useEffect, useState} from "react";
import {
AddCoverImageButton,
AddLogoIcon,
CoverImagePreview,
CrowdfundActionButton,
CrowdfundActionButtonRow,
CustomInputField,
CustomSelect,
LogoPreviewRow,
ModalBody,
NewCrowdfundTitle,
StyledButton,
TimesIcon,
} from "./Upload-styles";
import {
Box,
@ -28,27 +21,19 @@ import {
import RemoveIcon from "@mui/icons-material/Remove";
import ShortUniqueId from "short-unique-id";
import { useDispatch, useSelector } from "react-redux";
import AddBoxIcon from "@mui/icons-material/AddBox";
import { useDropzone } from "react-dropzone";
import { setNotification } from "../../state/features/notificationsSlice";
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
import { RootState } from "../../state/store";
import {
upsertVideosBeginning,
addToHashMap,
upsertVideos,
setEditVideo,
updateVideo,
updateInHashMap,
} from "../../state/features/videoSlice";
import ImageUploader from "../common/ImageUploader";
import { QTUBE_VIDEO_BASE, categories, subCategories, subCategories2,
subCategories3, } from "../../constants";
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublish";
import { TextEditor } from "../common/TextEditor/TextEditor";
import { extractTextFromHTML } from "../common/TextEditor/utils";
import {useDispatch, useSelector} from "react-redux";
import {useDropzone} from "react-dropzone";
import {setNotification} from "../../state/features/notificationsSlice";
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/MultiplePublishAll";
import {TextEditor} from "../common/TextEditor/TextEditor";
import {extractTextFromHTML} from "../common/TextEditor/utils";
import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
import {titleFormatter} from "../../constants/Misc.ts";
const uid = new ShortUniqueId();
const shortuid = new ShortUniqueId({ length: 5 });
@ -70,7 +55,7 @@ interface VideoFile {
identifier?:string;
filename?:string
}
export const EditVideo = () => {
export const EditFile = () => {
const theme = useTheme();
const dispatch = useDispatch();
const username = useSelector((state: RootState) => state.auth?.user?.name);
@ -80,7 +65,7 @@ export const EditVideo = () => {
const editVideoProperties = useSelector(
(state: RootState) => state.video.editVideoProperties
);
const [publishes, setPublishes] = useState<any[]>([]);
const [publishes, setPublishes] = useState<any>(null);
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
const [videoPropertiesToSetToRedux, setVideoPropertiesToSetToRedux] =
useState(null);
@ -302,7 +287,7 @@ export const EditVideo = () => {
const file = publish.file;
const id = uid();
const identifier = `${QTUBE_VIDEO_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
@ -347,7 +332,7 @@ export const EditVideo = () => {
description: metadescription,
identifier,
filename,
tag1: QTUBE_VIDEO_BASE,
tag1: QSHARE_FILE_BASE,
};
listOfPublishes.push(requestBodyVideo);
fileReferences.push({
@ -388,12 +373,16 @@ export const EditVideo = () => {
title: title.slice(0, 50),
description: metadescription,
identifier: editVideoProperties.id,
tag1: QTUBE_VIDEO_BASE,
tag1: QSHARE_FILE_BASE,
filename: `video_metadata.json`,
};
listOfPublishes.push(requestBodyJson);
setPublishes(listOfPublishes);
const multiplePublish = {
action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
resources: [...listOfPublishes],
};
setPublishes(multiplePublish);
setIsOpenMultiplePublish(true);
setVideoPropertiesToSetToRedux({
...editVideoProperties,
@ -686,10 +675,7 @@ export const EditVideo = () => {
value={title}
onChange={(e) => {
const value = e.target.value;
const formattedValue = value.replace(
/[^a-zA-Z0-9\s-_!?]/g,
""
);
const formattedValue = value.replace(titleFormatter, "");
setTitle(formattedValue);
}}
inputProps={{ maxLength: 180 }}
@ -744,6 +730,18 @@ export const EditVideo = () => {
{isOpenMultiplePublish && (
<MultiplePublish
isOpen={isOpenMultiplePublish}
onError={(messageNotification)=> {
setIsOpenMultiplePublish(false);
setPublishes(null)
if(messageNotification){
dispatch(
setNotification({
msg: messageNotification,
alertType: 'error'
})
)
}
}}
onSubmit={() => {
setIsOpenMultiplePublish(false);
const clonedCopy = structuredClone(videoPropertiesToSetToRedux);

0
src/components/EditVideo/Upload-styles.tsx → src/components/EditFile/Upload-styles.tsx

11
src/components/EditPlaylist/EditPlaylist.tsx

@ -12,7 +12,7 @@ import {
NewCrowdfundTitle,
StyledButton,
TimesIcon,
} from "./Upload-styles";
} from "./Upload-styles.tsx";
import {
Box,
FormControl,
@ -43,11 +43,12 @@ import {
setEditPlaylist,
} from "../../state/features/videoSlice";
import ImageUploader from "../common/ImageUploader";
import { QTUBE_PLAYLIST_BASE, QTUBE_VIDEO_BASE, categories, subCategories } from "../../constants";
import { QSHARE_PLAYLIST_BASE, QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { Playlists } from "../Playlists/Playlists";
import { PlaylistListEdit } from "../PlaylistListEdit/PlaylistListEdit";
import { TextEditor } from "../common/TextEditor/TextEditor";
import { extractTextFromHTML } from "../common/TextEditor/utils";
import {categories, subCategories} from "../../constants/Categories.ts";
const uid = new ShortUniqueId();
const shortuid = new ShortUniqueId({ length: 5 });
@ -289,7 +290,7 @@ export const EditPlaylist = () => {
let commentsId = editVideoProperties?.id
if(isNew){
commentsId = `${QTUBE_PLAYLIST_BASE}_cm_${id}`
commentsId = `${QSHARE_PLAYLIST_BASE}_cm_${id}`
}
const stringDescription = extractTextFromHTML(description)
@ -322,7 +323,7 @@ export const EditPlaylist = () => {
.trim()
.toLowerCase();
if(isNew){
identifier = `${QTUBE_PLAYLIST_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
identifier = `${QSHARE_PLAYLIST_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
}
const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE",
@ -332,7 +333,7 @@ export const EditPlaylist = () => {
title: title.slice(0, 50),
description: metadescription,
identifier: identifier,
tag1: QTUBE_VIDEO_BASE,
tag1: QSHARE_FILE_BASE,
};
await qortalRequest(requestBodyJson);

6
src/components/PlaylistListEdit/PlaylistListEdit.tsx

@ -3,13 +3,13 @@ import { CardContentContainerComment } from "../common/Comments/Comments-styles"
import {
CrowdfundSubTitle,
CrowdfundSubTitleRow,
} from "../UploadVideo/Upload-styles";
} from "../PublishFile/Upload-styles.tsx";
import { Box, Button, Input, Typography, useTheme } from "@mui/material";
import { useNavigate } from "react-router-dom";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { removeVideo } from "../../state/features/videoSlice";
import AddIcon from '@mui/icons-material/Add';
import { QTUBE_VIDEO_BASE } from "../../constants";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { useSelector } from "react-redux";
import { RootState } from "../../state/store";
export const PlaylistListEdit = ({ playlistData, removeVideo, addVideo }) => {
@ -21,7 +21,7 @@ export const PlaylistListEdit = ({ playlistData, removeVideo, addVideo }) => {
const [filterSearch, setFilterSearch] = useState("")
const search = async()=> {
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&mode=ALL&identifier=${QTUBE_VIDEO_BASE}&title=${filterSearch}&limit=20&includemetadata=true&reverse=true&name=${username}&exactmatchnames=true&offset=0`
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&mode=ALL&identifier=${QSHARE_FILE_BASE}&title=${filterSearch}&limit=20&includemetadata=true&reverse=true&name=${username}&exactmatchnames=true&offset=0`
const response = await fetch(url, {
method: 'GET',
headers: {

2
src/components/Playlists/Playlists.tsx

@ -1,6 +1,6 @@
import React from 'react'
import { CardContentContainerComment } from '../common/Comments/Comments-styles'
import { CrowdfundSubTitle, CrowdfundSubTitleRow } from '../UploadVideo/Upload-styles'
import { CrowdfundSubTitle, CrowdfundSubTitleRow } from '../PublishFile/Upload-styles.tsx'
import { Box, Typography, useTheme } from '@mui/material'
import { useNavigate } from 'react-router-dom'

59
src/components/UploadVideo/UploadVideo.tsx → src/components/PublishFile/PublishFile.tsx

@ -44,21 +44,23 @@ import {
} from "../../state/features/videoSlice";
import ImageUploader from "../common/ImageUploader";
import {
QTUBE_PLAYLIST_BASE,
QTUBE_VIDEO_BASE,
categories,
subCategories,
subCategories2,
subCategories3,
} from "../../constants";
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublish";
QSHARE_PLAYLIST_BASE,
QSHARE_FILE_BASE,
} from "../../constants/Identifiers.ts";
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
import {
CrowdfundSubTitle,
CrowdfundSubTitleRow,
} from "../EditPlaylist/Upload-styles";
} from "../EditPlaylist/Upload-styles.tsx";
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
import { TextEditor } from "../common/TextEditor/TextEditor";
import { extractTextFromHTML } from "../common/TextEditor/utils";
import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
import {titleFormatter} from "../../constants/Misc.ts";
const uid = new ShortUniqueId();
const shortuid = new ShortUniqueId({ length: 5 });
@ -78,7 +80,7 @@ interface VideoFile {
description: string;
coverImage?: string;
}
export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
const theme = useTheme();
const dispatch = useDispatch();
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
@ -112,7 +114,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
useState<any>(null);
const [playlistSetting, setPlaylistSetting] = useState<null | string>(null);
const [publishes, setPublishes] = useState<any[]>([]);
const [publishes, setPublishes] = useState<any>(null);
const { getRootProps, getInputProps } = useDropzone({
maxFiles: 10,
maxSize: 419430400, // 400 MB in bytes
@ -214,7 +216,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
const file = publish.file;
const id = uid();
const identifier = `${QTUBE_VIDEO_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
@ -260,7 +262,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
description: metadescription,
identifier,
filename,
tag1: QTUBE_VIDEO_BASE,
tag1: QSHARE_FILE_BASE,
};
listOfPublishes.push(requestBodyVideo);
fileReferences.push({
@ -274,13 +276,13 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
}
const idMeta = uid();
const identifier = `${QTUBE_VIDEO_BASE}${sanitizeTitle.slice(0, 30)}_${idMeta}`;
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${idMeta}`;
const fileObject: any = {
title,
version: 1,
fullDescription,
htmlDescription: description,
commentsId: `${QTUBE_VIDEO_BASE}_cm_${idMeta}`,
commentsId: `${QSHARE_FILE_BASE}_cm_${idMeta}`,
category,
subcategory,
subcategory2,
@ -302,12 +304,18 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
title: title.slice(0, 50),
description: metadescription,
identifier: identifier + "_metadata",
tag1: QTUBE_VIDEO_BASE,
tag1: QSHARE_FILE_BASE,
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") {
@ -599,10 +607,7 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
value={title}
onChange={(e) => {
const value = e.target.value;
const formattedValue = value.replace(
/[^a-zA-Z0-9\s-_!?]/g,
""
);
const formattedValue = value.replace(titleFormatter, "");
setTitle(formattedValue);
}}
inputProps={{ maxLength: 180 }}
@ -659,6 +664,18 @@ export const UploadVideo = ({ editId, editContent }: NewCrowdfundProps) => {
{isOpenMultiplePublish && (
<MultiplePublish
isOpen={isOpenMultiplePublish}
onError={(messageNotification)=> {
setIsOpenMultiplePublish(false);
setPublishes(null)
if(messageNotification){
dispatch(
setNotification({
msg: messageNotification,
alertType: 'error'
})
)
}
}}
onSubmit={() => {
setIsOpenMultiplePublish(false);
setIsOpen(false);

26
src/components/UploadVideo/Upload-styles.tsx → src/components/PublishFile/Upload-styles.tsx

@ -67,9 +67,9 @@ export const ModalBody = styled(Box)(({ theme }) => ({
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",
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,
},
@ -203,11 +203,11 @@ export const CrowdfundDescription = styled(Typography)(({ theme }) => ({
export const Spacer = ({ height }: any) => {
return (
<Box
sx={{
height: height,
}}
/>
<Box
sx={{
height: height,
}}
/>
);
};
@ -314,14 +314,14 @@ export const AddCrowdFundButton = styled(Button)(({ theme }) => ({
gap: "8px",
color: "#ffffff",
backgroundColor:
theme.palette.mode === "dark" ? theme.palette.primary.main : "#2a9a86",
theme.palette.mode === "dark" ? theme.palette.primary.main : "#2a9a86",
border: "none",
borderRadius: "5px",
transition: "all 0.3s ease-in-out",
"&:hover": {
cursor: "pointer",
backgroundColor:
theme.palette.mode === "dark" ? theme.palette.primary.dark : "#217e6d",
theme.palette.mode === "dark" ? theme.palette.primary.dark : "#217e6d",
},
}));
@ -333,14 +333,14 @@ export const EditCrowdFundButton = styled(Button)(({ theme }) => ({
gap: "8px",
color: "#ffffff",
backgroundColor:
theme.palette.mode === "dark" ? theme.palette.primary.main : "#2a9a86",
theme.palette.mode === "dark" ? theme.palette.primary.main : "#2a9a86",
border: "none",
borderRadius: "5px",
transition: "all 0.3s ease-in-out",
"&:hover": {
cursor: "pointer",
backgroundColor:
theme.palette.mode === "dark" ? theme.palette.primary.dark : "#217e6d",
theme.palette.mode === "dark" ? theme.palette.primary.dark : "#217e6d",
},
}));
@ -584,4 +584,4 @@ export const CustomSelect = styled(Select)(({ theme }) => ({
fontWeight: 400,
color: theme.palette.text.primary,
},
}));
}));

6
src/components/common/Comments/CommentEditor.tsx

@ -11,7 +11,7 @@ import {
CommentInputContainer,
SubmitCommentButton,
} from "./Comments-styles";
import { COMMENT_BASE } from "../../../constants";
import { QSHARE_COMMENT_BASE } from "../../../constants/Identifiers.ts";
const uid = new ShortUniqueId();
const notification = localforage.createInstance({
@ -201,13 +201,13 @@ export const CommentEditor = ({
try {
const id = uid();
let identifier = `${COMMENT_BASE}${postId.slice(-12)}_base_${id}`;
let identifier = `${QSHARE_COMMENT_BASE}${postId.slice(-12)}_base_${id}`;
let idForNotification = identifier;
if (isReply && commentId) {
const removeBaseCommentId = commentId;
removeBaseCommentId.replace("_base_", "");
identifier = `${COMMENT_BASE}${postId.slice(
identifier = `${QSHARE_COMMENT_BASE}${postId.slice(
-12
)}_reply_${removeBaseCommentId.slice(-6)}_${id}`;
idForNotification = commentId;

8
src/components/common/Comments/CommentSection.tsx

@ -14,8 +14,8 @@ import {
LoadMoreCommentsButtonRow,
NoCommentsRow,
} from "./Comments-styles";
import { COMMENT_BASE } from "../../../constants";
import { CrowdfundSubTitle, CrowdfundSubTitleRow } from "../../UploadVideo/Upload-styles";
import { QSHARE_COMMENT_BASE } from "../../../constants/Identifiers.ts";
import { CrowdfundSubTitle, CrowdfundSubTitleRow } from "../../PublishFile/Upload-styles.tsx";
interface CommentSectionProps {
postId: string;
@ -105,7 +105,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => {
const offset = 0;
const removeBaseCommentId = commentId.replace("_base_", "");
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${COMMENT_BASE}${postId.slice(
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${QSHARE_COMMENT_BASE}${postId.slice(
-12
)}_reply_${removeBaseCommentId.slice(
-6
@ -150,7 +150,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => {
if (isNewMessages && numberOfComments) {
offset = numberOfComments;
}
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${COMMENT_BASE}${postId.slice(
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${QSHARE_COMMENT_BASE}${postId.slice(
-12
)}_base_&limit=20&includemetadata=false&offset=${offset}&reverse=false&excludeblocked=true`;
const response = await fetch(url, {

136
src/components/common/MultiplePublish/MultiplePublish.tsx

@ -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 "../../UploadVideo/Upload-styles";
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<void>((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 (
<Modal
open={isOpen}
aria-labelledby="modal-title"
aria-describedby="modal-description"
>
<ModalBody
sx={{
minHeight: "50vh",
}}
>
{publishes.map((publish: any) => {
return (
<Box
sx={{
display: "flex",
gap: "20px",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography>{publish?.title}</Typography>
{publish?.identifier === currentlyInPublish ? (
<CircularProgress
size={20}
thickness={2}
sx={{
color: theme.palette.secondary.main,
}}
/>
) : listOfSuccessfulPublishes.includes(publish.identifier) ? (
<CircleSVG
color={theme.palette.text.primary}
height="24px"
width="24px"
/>
) : (
<EmptyCircleSVG
color={theme.palette.text.primary}
height="24px"
width="24px"
/>
)}
</Box>
);
})}
{!isPublishing && listOfSuccessfulPublishes.length !== publishes.length && (
<>
<Typography sx={{
marginTop: '20px',
fontSize: '16px'
}}>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</Typography>
<Button onClick={()=> {
startPublish(publishes)
}}>Try again</Button>
</>
)}
</ModalBody>
</Modal>
);
};

211
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 (
<Modal
open={isOpen}
aria-labelledby="modal-title"
aria-describedby="modal-description"
>
<ModalBody
sx={{
minHeight: "50vh",
}}
>
{publishes?.resources?.map((publish: any) => {
const unpublished = listOfUnsuccessfulPublishes.map(item => item?.identifier)
return (
<Box
sx={{
display: "flex",
gap: "20px",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Typography>{publish?.identifier}</Typography>
{!isPublishing && hasStarted.current ? (
<>
{!unpublished.includes(publish.identifier) ? (
<CircleSVG
color={theme.palette.text.primary}
height="24px"
width="24px"
/>
) : (
<EmptyCircleSVG
color={theme.palette.text.primary}
height="24px"
width="24px"
/>
)}
</>
): <CircularProgress size={16} color="secondary"/>}
</Box>
);
})}
{!isPublishing && listOfUnsuccessfulPublishes.length > 0 && (
<>
<Typography sx={{
marginTop: '20px',
fontSize: '16px'
}}>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</Typography>
<Button variant="contained" onClick={()=> {
retry()
}}>Try again</Button>
</>
)}
</ModalBody>
</Modal>
);
};
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",
},
}));

6
src/components/layout/Navbar/Navbar.tsx

@ -35,8 +35,8 @@ import {
} from "../../../state/features/videoSlice";
import { RootState } from "../../../state/store";
import { useWindowSize } from "../../../hooks/useWindowSize";
import { UploadVideo } from "../../UploadVideo/UploadVideo";
import { StyledButton } from "../../UploadVideo/Upload-styles";
import { PublishFile } from "../../PublishFile/PublishFile.tsx";
import { StyledButton } from "../../PublishFile/Upload-styles.tsx";
interface Props {
isAuthenticated: boolean;
userName: string | null;
@ -400,7 +400,7 @@ const NavBar: React.FC<Props> = ({
<AvatarContainer>
{isAuthenticated && userName && (
<>
<UploadVideo />
<PublishFile />
</>
)}

221
src/constants/Categories.ts

@ -0,0 +1,221 @@
import softwareIcon from "../assets/icons/software.webp";
import gamingIcon from "../assets/icons/gaming.webp";
import mediaIcon from "../assets/icons/media.webp";
import videoIcon from "../assets/icons/video.webp";
import audioIcon from "../assets/icons/audio.webp";
import documentIcon from "../assets/icons/document.webp";
interface SubCategory {
id: number;
name: string;
}
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": 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"}
].sort(sortCategory),
3: [
{"id": 301, "name": "Audio"},
{"id": 302, "name": "Video"},
{"id": 303, "name": "Image"},
{"id": 304, "name": "Document"},
{"id": 305, "name": "Other"}
].sort(sortCategory)
};
const gamingSystems = [
{"id": 20101, "name": "ROM"},
{"id": 20102, "name": "Romhack"},
{"id": 20103, "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"},
{"id": 30103, "name": "Audiobooks"},
{"id": 30104, "name": "Sound Effects"},
{"id": 30105, "name": "Lectures & Speeches"},
{"id": 30106, "name": "Radio Shows"},
{"id": 30107, "name": "Ambient Sounds"},
{"id": 30108, "name": "Language Learning Material"},
{"id": 30109, "name": "Comedy & Satire"},
{"id": 30110, "name": "Documentaries"},
{"id": 30111, "name": "Guided Meditations & Yoga"},
{"id": 30112, "name": "Live Performances"},
{"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"},
{"id": 30203, "name": "Music"},
{"id": 30204, "name": "Education"},
{"id": 30205, "name": "Lifestyle"},
{"id": 30206, "name": "Gaming"},
{"id": 30207, "name": "Technology"},
{"id": 30208, "name": "Sports"},
{"id": 30209, "name": "News & Politics"},
{"id": 30210, "name": "Cooking & Food"},
{"id": 30211, "name": "Animation"},
{"id": 30212, "name": "Science"},
{"id": 30213, "name": "Health & Wellness"},
{"id": 30214, "name": "DIY & Crafts"},
{"id": 30215, "name": "Kids & Family"},
{"id": 30216, "name": "Comedy"},
{"id": 30217, "name": "Travel & Adventure"},
{"id": 30218, "name": "Art & Design"},
{"id": 30219, "name": "Nature & Environment"},
{"id": 30220, "name": "Business & Finance"},
{"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"},
{"id": 30303, "name": "People & Portraits"},
{"id": 30304, "name": "Art & Abstract"},
{"id": 30305, "name": "Travel & Adventure"},
{"id": 30306, "name": "Animals & Wildlife"},
{"id": 30307, "name": "Sports & Action"},
{"id": 30308, "name": "Food & Cuisine"},
{"id": 30309, "name": "Fashion & Beauty"},
{"id": 30310, "name": "Technology & Science"},
{"id": 30311, "name": "Historical & Cultural"},
{"id": 30312, "name": "Aerial & Drone"},
{"id": 30313, "name": "Black & White"},
{"id": 30314, "name": "Events & Celebrations"},
{"id": 30315, "name": "Business & Corporate"},
{"id": 30316, "name": "Health & Wellness"},
{"id": 30317, "name": "Transportation & Vehicles"},
{"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
{"id": 3020101, "name": "Action & Adventure"},
{"id": 3020102, "name": "Comedy"},
{"id": 3020103, "name": "Drama"},
{"id": 3020104, "name": "Fantasy & Science Fiction"},
{"id": 3020105, "name": "Horror & Thriller"},
{"id": 3020106, "name": "Documentaries"},
{"id": 3020107, "name": "Animated"},
{"id": 3020108, "name": "Family & Kids"},
{"id": 3020109, "name": "Romance"},
{"id": 3020110, "name": "Mystery & Crime"},
{"id": 3020111, "name": "Historical & War"},
{"id": 3020112, "name": "Musicals & Music Films"},
{"id": 3020113, "name": "Indie Films"},
{"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"},
{"id": 3020203, "name": "Reality & Competition"},
{"id": 3020204, "name": "Documentaries & Docuseries"},
{"id": 3020205, "name": "Sci-Fi & Fantasy"},
{"id": 3020206, "name": "Crime & Mystery"},
{"id": 3020207, "name": "Animated Series"},
{"id": 3020208, "name": "Kids & Family"},
{"id": 3020209, "name": "Historical & Period Pieces"},
{"id": 3020210, "name": "Action & Adventure"},
{"id": 3020211, "name": "Horror & Thriller"},
{"id": 3020212, "name": "Romance"},
{"id": 3020213, "name": "Anthologies"},
{"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"},
{"id": 3040503, "name": "Science Fiction & Fantasy"},
{"id": 3040504, "name": "Biographies & Memoirs"},
{"id": 3040505, "name": "Children's Books"},
{"id": 3040506, "name": "Educational"},
{"id": 3040507, "name": "Self-Help"},
{"id": 3040508, "name": "Cookbooks, Food & Wine"},
{"id": 3040509, "name": "Mystery & Thriller"},
{"id": 3040510, "name": "History"},
{"id": 3040511, "name": "Poetry"},
{"id": 3040512, "name": "Art & Photography"},
{"id": 3040513, "name": "Religion & Spirituality"},
{"id": 3040514, "name": "Travel"},
{"id": 3040515, "name": "Comics & Graphic Novels"},
].sort(sortCategory),
30101: [ // Under Music
{"id": 3010101, "name": "Rock"},
{"id": 3010102, "name": "Pop"},
{"id": 3010103, "name": "Classical"},
{"id": 3010104, "name": "Jazz"},
{"id": 3010105, "name": "Electronic"},
{"id": 3010106, "name": "Country"},
{"id": 3010107, "name": "Hip Hop/Rap"},
{"id": 3010108, "name": "Blues"},
{"id": 3010109, "name": "R&B/Soul"},
{"id": 3010110, "name": "Reggae"},
{"id": 3010111, "name": "Folk"},
{"id": 3010112, "name": "Metal"},
{"id": 3010113, "name": "World Music"},
{"id": 3010114, "name": "Latin"},
{"id": 3010115, "name": "Indie"},
{"id": 3010116, "name": "Punk"},
{"id": 3010117, "name": "Soundtracks"},
{"id": 3010118, "name": "Children's Music"},
{"id": 3010119, "name": "New Age"},
{"id": 3010120, "name": "Classical Crossover"}
].sort(sortCategory)
};
export const icons = {
1: softwareIcon,
2: gamingIcon,
3: mediaIcon,
4: softwareIcon,
302: videoIcon,
301: audioIcon,
304: documentIcon
}

17
src/constants/Identifiers.ts

@ -0,0 +1,17 @@
const useTestIdentifiers = false;
export const QSHARE_FILE_BASE = useTestIdentifiers
? "MYTEST_share_vid_"
: "qshare_file_";
export const QSHARE_PLAYLIST_BASE = useTestIdentifiers
? "MYTEST_share_playlist_"
: "qshare_playlist_";
export const QSHARE_COMMENT_BASE = useTestIdentifiers
? "qcomment_v1_MYTEST_"
: "qcomment_v1_qshare_";

1
src/constants/Misc.ts

@ -0,0 +1 @@
export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.~;:|]/g;

237
src/constants/index.ts

@ -1,237 +0,0 @@
import softwareIcon from '../assets/icons/software.webp'
import gamingIcon from '../assets/icons/gaming.webp'
import mediaIcon from '../assets/icons/media.webp'
import audioIcon from '../assets/icons/audio.webp'
import videoIcon from '../assets/icons/video.webp'
import documentIcon from '../assets/icons/document.webp'
const useTestIdentifiers = false;
export const QTUBE_VIDEO_BASE = useTestIdentifiers
? "MYTEST_share_vid_"
: "qshare_file_";
export const QTUBE_PLAYLIST_BASE = useTestIdentifiers
? "MYTEST_share_playlist_"
: "qshare_playlist_";
export const COMMENT_BASE = useTestIdentifiers
? "qcomment_v1_MYTEST_"
: "qcomment_v1_qshare_";
interface SubCategory {
id: number;
name: string;
}
interface CategoryMap {
[key: number]: SubCategory[];
}
export const categories = [
{"id": 1, "name": "Software"},
{"id": 2, "name": "Gaming"},
{"id": 3, "name": "Media"}
];
export const subCategories: CategoryMap = {
1: [
{"id": 101, "name": "OS"},
{"id": 102, "name": "Application"},
{"id": 103, "name": "Source Code"},
{"id": 104, "name": "Other"}
],
2: [
{"id": 201, "name": "NES"},
{"id": 202, "name": "SNES"},
{"id": 203, "name": "PC"},
{"id": 204, "name": "Other Gaming Systems"}
],
3: [
{"id": 301, "name": "Audio"},
{"id": 302, "name": "Video"},
{"id": 303, "name": "Image"},
{"id": 304, "name": "Document"},
{"id": 305, "name": "Other Media Formats"}
]
};
export const subCategories2: CategoryMap = {
201: [ // NES
{"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"},
],
301: [ // Audio
{"id": 30101, "name": "Music"},
{"id": 30102, "name": "Podcasts"},
{"id": 30103, "name": "Audiobooks"},
{"id": 30104, "name": "Sound Effects"},
{"id": 30105, "name": "Lectures & Speeches"},
{"id": 30106, "name": "Radio Shows"},
{"id": 30107, "name": "Ambient Sounds"},
{"id": 30108, "name": "Language Learning Material"},
{"id": 30109, "name": "Comedy & Satire"},
{"id": 30110, "name": "Documentaries"},
{"id": 30111, "name": "Guided Meditations & Yoga"},
{"id": 30112, "name": "Live Performances"},
{"id": 30113, "name": "Nature Sounds"},
{"id": 30114, "name": "Soundtracks"},
{"id": 30115, "name": "Interviews"}
],
302: [ // Under Video
{"id": 30201, "name": "Movies"},
{"id": 30202, "name": "Series"},
{"id": 30203, "name": "Music"},
{"id": 30204, "name": "Education"},
{"id": 30205, "name": "Lifestyle"},
{"id": 30206, "name": "Gaming"},
{"id": 30207, "name": "Technology"},
{"id": 30208, "name": "Sports"},
{"id": 30209, "name": "News & Politics"},
{"id": 30210, "name": "Cooking & Food"},
{"id": 30211, "name": "Animation"},
{"id": 30212, "name": "Science"},
{"id": 30213, "name": "Health & Wellness"},
{"id": 30214, "name": "DIY & Crafts"},
{"id": 30215, "name": "Kids & Family"},
{"id": 30216, "name": "Comedy"},
{"id": 30217, "name": "Travel & Adventure"},
{"id": 30218, "name": "Art & Design"},
{"id": 30219, "name": "Nature & Environment"},
{"id": 30220, "name": "Business & Finance"},
{"id": 30221, "name": "Personal Development"},
{"id": 30222, "name": "Other"},
{"id": 30223, "name": "History"}
],
303: [ // Image
{"id": 30301, "name": "Nature"},
{"id": 30302, "name": "Urban & Cityscapes"},
{"id": 30303, "name": "People & Portraits"},
{"id": 30304, "name": "Art & Abstract"},
{"id": 30305, "name": "Travel & Adventure"},
{"id": 30306, "name": "Animals & Wildlife"},
{"id": 30307, "name": "Sports & Action"},
{"id": 30308, "name": "Food & Cuisine"},
{"id": 30309, "name": "Fashion & Beauty"},
{"id": 30310, "name": "Technology & Science"},
{"id": 30311, "name": "Historical & Cultural"},
{"id": 30312, "name": "Aerial & Drone"},
{"id": 30313, "name": "Black & White"},
{"id": 30314, "name": "Events & Celebrations"},
{"id": 30315, "name": "Business & Corporate"},
{"id": 30316, "name": "Health & Wellness"},
{"id": 30317, "name": "Transportation & Vehicles"},
{"id": 30318, "name": "Still Life & Objects"},
{"id": 30319, "name": "Architecture & Buildings"},
{"id": 30320, "name": "Landscapes & Seascapes"}
],
304: [ // Document
{"id": 30401, "name": "PDF"},
{"id": 30402, "name": "Word Document"},
{"id": 30403, "name": "Spreadsheet"},
{"id": 30404, "name": "Powerpoint"},
{"id": 30405, "name": "Books"}
]
};
export const subCategories3: CategoryMap = {
30201: [ // Under Movies
{"id": 3020101, "name": "Action & Adventure"},
{"id": 3020102, "name": "Comedy"},
{"id": 3020103, "name": "Drama"},
{"id": 3020104, "name": "Fantasy & Science Fiction"},
{"id": 3020105, "name": "Horror & Thriller"},
{"id": 3020106, "name": "Documentaries"},
{"id": 3020107, "name": "Animated"},
{"id": 3020108, "name": "Family & Kids"},
{"id": 3020109, "name": "Romance"},
{"id": 3020110, "name": "Mystery & Crime"},
{"id": 3020111, "name": "Historical & War"},
{"id": 3020112, "name": "Musicals & Music Films"},
{"id": 3020113, "name": "Indie Films"},
{"id": 3020114, "name": "International Films"},
{"id": 3020115, "name": "Biographies & True Stories"},
{"id": 3020116, "name": "Other"}
],
30202: [ // Under Series
{"id": 3020201, "name": "Dramas"},
{"id": 3020202, "name": "Comedies"},
{"id": 3020203, "name": "Reality & Competition"},
{"id": 3020204, "name": "Documentaries & Docuseries"},
{"id": 3020205, "name": "Sci-Fi & Fantasy"},
{"id": 3020206, "name": "Crime & Mystery"},
{"id": 3020207, "name": "Animated Series"},
{"id": 3020208, "name": "Kids & Family"},
{"id": 3020209, "name": "Historical & Period Pieces"},
{"id": 3020210, "name": "Action & Adventure"},
{"id": 3020211, "name": "Horror & Thriller"},
{"id": 3020212, "name": "Romance"},
{"id": 3020213, "name": "Anthologies"},
{"id": 3020214, "name": "International Series"},
{"id": 3020215, "name": "Miniseries"},
{"id": 3020216, "name": "Other"}
],
30405: [ // Under Books
{"id": 3040501, "name": "Fiction"},
{"id": 3040502, "name": "Non-Fiction"},
{"id": 3040503, "name": "Science Fiction & Fantasy"},
{"id": 3040504, "name": "Biographies & Memoirs"},
{"id": 3040505, "name": "Children's Books"},
{"id": 3040506, "name": "Educational"},
{"id": 3040507, "name": "Self-Help"},
{"id": 3040508, "name": "Cookbooks, Food & Wine"},
{"id": 3040509, "name": "Mystery & Thriller"},
{"id": 3040510, "name": "History"},
{"id": 3040511, "name": "Poetry"},
{"id": 3040512, "name": "Art & Photography"},
{"id": 3040513, "name": "Religion & Spirituality"},
{"id": 3040514, "name": "Travel"},
{"id": 3040515, "name": "Comics & Graphic Novels"},
],
30101: [ // Under Music
{"id": 3010101, "name": "Rock"},
{"id": 3010102, "name": "Pop"},
{"id": 3010103, "name": "Classical"},
{"id": 3010104, "name": "Jazz"},
{"id": 3010105, "name": "Electronic"},
{"id": 3010106, "name": "Country"},
{"id": 3010107, "name": "Hip Hop/Rap"},
{"id": 3010108, "name": "Blues"},
{"id": 3010109, "name": "R&B/Soul"},
{"id": 3010110, "name": "Reggae"},
{"id": 3010111, "name": "Folk"},
{"id": 3010112, "name": "Metal"},
{"id": 3010113, "name": "World Music"},
{"id": 3010114, "name": "Latin"},
{"id": 3010115, "name": "Indie"},
{"id": 3010116, "name": "Punk"},
{"id": 3010117, "name": "Soundtracks"},
{"id": 3010118, "name": "Children's Music"},
{"id": 3010119, "name": "New Age"},
{"id": 3010120, "name": "Classical Crossover"}
]
};
export const icons = {
1: softwareIcon,
2: gamingIcon,
3: mediaIcon,
302: videoIcon,
301: audioIcon,
304: documentIcon
}

22
src/hooks/useFetchVideos.tsx → src/hooks/useFetchFiles.tsx

@ -14,13 +14,13 @@ import {
} from '../state/features/globalSlice'
import { RootState } from '../state/store'
import { fetchAndEvaluateVideos } from '../utils/fetchVideos'
import { QTUBE_PLAYLIST_BASE, QTUBE_VIDEO_BASE } from '../constants'
import { QSHARE_PLAYLIST_BASE, QSHARE_FILE_BASE } from '../constants/Identifiers.ts'
import { RequestQueue } from '../utils/queue'
import { queue } from '../wrappers/GlobalWrapper'
export const useFetchVideos = () => {
export const useFetchFiles = () => {
const dispatch = useDispatch()
const hashMapVideos = useSelector(
(state: RootState) => state.video.hashMapVideos
@ -95,7 +95,7 @@ export const useFetchVideos = () => {
dispatch(setIsLoadingGlobal(true))
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`
const response = await fetch(url, {
method: 'GET',
headers: {
@ -213,11 +213,11 @@ export const useFetchVideos = () => {
}
if(type === 'playlists'){
defaultUrl = defaultUrl + `&service=PLAYLIST`
defaultUrl = defaultUrl + `&identifier=${QTUBE_PLAYLIST_BASE}`
defaultUrl = defaultUrl + `&identifier=${QSHARE_PLAYLIST_BASE}`
} else {
defaultUrl = defaultUrl + `&service=DOCUMENT`
defaultUrl = defaultUrl + `&identifier=${QTUBE_VIDEO_BASE}`
defaultUrl = defaultUrl + `&identifier=${QSHARE_FILE_BASE}`
}
// const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=${videoLimit}&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}`
@ -288,7 +288,7 @@ export const useFetchVideos = () => {
const offset = filteredVideos.length
const replaceSpacesWithUnderscore = filterValue.replace(/ /g, '_');
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${replaceSpacesWithUnderscore}&identifier=${QTUBE_VIDEO_BASE}&limit=10&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}`
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${replaceSpacesWithUnderscore}&identifier=${QSHARE_FILE_BASE}&limit=10&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}`
const response = await fetch(url, {
method: 'GET',
headers: {
@ -345,7 +345,7 @@ export const useFetchVideos = () => {
try {
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`
const response = await fetch(url, {
method: 'GET',
headers: {
@ -382,12 +382,12 @@ export const useFetchVideos = () => {
return {
getVideos,
getFiles: getVideos,
checkAndUpdateVideo,
getVideo,
hashMapVideos,
getNewVideos,
checkNewVideos,
getVideosFiltered
getNewFiles: getNewVideos,
checkNewFiles: checkNewVideos,
getFilesFiltered: getVideosFiltered
}
}

4
src/pages/Home/Channels.tsx

@ -9,9 +9,9 @@ import {
Typography,
useTheme
} from '@mui/material'
import { useFetchVideos } from '../../hooks/useFetchVideos'
import { useFetchFiles } from '../../hooks/useFetchFiles.tsx'
import LazyLoad from '../../components/common/LazyLoad'
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './VideoList-styles'
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './FileList-styles.tsx'
import ResponsiveImage from '../../components/ResponsiveImage'
import { formatDate, formatTimestampSeconds } from '../../utils/time'
import { ChannelCard, ChannelTitle } from './Home-styles'

0
src/pages/Home/VideoList-styles.tsx → src/pages/Home/FileList-styles.tsx

48
src/pages/Home/VideoList.tsx → src/pages/Home/FileList.tsx

@ -21,7 +21,7 @@ import {
Typography,
useTheme,
} from "@mui/material";
import { useFetchVideos } from "../../hooks/useFetchVideos";
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
import LazyLoad from "../../components/common/LazyLoad";
import {
BlockIconContainer,
@ -40,7 +40,7 @@ import {
VideoCardTitle,
VideoContainer,
VideoUploadDate,
} from "./VideoList-styles";
} from "./FileList-styles.tsx";
import ResponsiveImage from "../../components/ResponsiveImage";
import { formatDate, formatTimestampSeconds } from "../../utils/time";
import { Subtitle, SubtitleContainer } from "./Home-styles";
@ -59,17 +59,17 @@ import {
setEditPlaylist,
setEditVideo,
} from "../../state/features/videoSlice";
import { categories, icons, subCategories, subCategories2, subCategories3 } from "../../constants";
import { Playlists } from "../../components/Playlists/Playlists";
import { PlaylistSVG } from "../../assets/svgs/PlaylistSVG";
import BlockIcon from "@mui/icons-material/Block";
import EditIcon from '@mui/icons-material/Edit';
import { formatBytes } from "../VideoContent/VideoContent";
import {categories, icons, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
interface VideoListProps {
mode?: string;
}
export const VideoList = ({ mode }: VideoListProps) => {
export const FileList = ({ mode }: VideoListProps) => {
const theme = useTheme();
const prevVal = useRef("");
const isFiltering = useSelector(
@ -152,10 +152,10 @@ export const VideoList = ({ mode }: VideoListProps) => {
(state: RootState) => state.video
);
const navigate = useNavigate();
const { getVideos, getNewVideos, checkNewVideos, getVideosFiltered } =
useFetchVideos();
const { getFiles, getNewFiles, checkNewFiles, getFilesFiltered } =
useFetchFiles();
const getVideosHandler = React.useCallback(
const getFilesHandler = React.useCallback(
async (reset?: boolean, resetFilers?: boolean) => {
@ -168,7 +168,7 @@ export const VideoList = ({ mode }: VideoListProps) => {
subcategory2: selectedSubCategoryVideos2?.id,
subcategory3: selectedSubCategoryVideos3?.id,
})
await getVideos(
await getFiles(
{
name: filterName,
category: selectedCategoryVideos?.id,
@ -184,9 +184,9 @@ export const VideoList = ({ mode }: VideoListProps) => {
isFetching.current = false;
},
[
getVideos,
getFiles,
filterValue,
getVideosFiltered,
getFilesFiltered,
isFiltering,
filterName,
selectedCategoryVideos,
@ -198,24 +198,30 @@ export const VideoList = ({ mode }: VideoListProps) => {
]
);
const searchOnEnter = e => {
if (e.keyCode == 13) {
getFilesHandler(true);
}
};
useEffect(() => {
if (isFiltering && filterValue !== prevVal?.current) {
prevVal.current = filterValue;
getVideosHandler();
getFilesHandler();
}
}, [filterValue, isFiltering, filteredVideos]);
const getVideosHandlerMount = React.useCallback(async () => {
const getFilesHandlerMount = React.useCallback(async () => {
if (firstFetch.current) return;
firstFetch.current = true;
setIsLoading(true);
await getVideos();
await getFiles();
afterFetch.current = true;
isFetching.current = false;
setIsLoading(false);
}, [getVideos]);
}, [getFiles]);
let videos = globalVideos;
@ -259,12 +265,12 @@ export const VideoList = ({ mode }: VideoListProps) => {
globalVideos.length === 0
) {
isFetching.current = true;
getVideosHandlerMount();
getFilesHandlerMount();
} else {
firstFetch.current = true;
afterFetch.current = true;
}
}, [getVideosHandlerMount, globalVideos]);
}, [getFilesHandlerMount, globalVideos]);
const filtersToDefault = async () => {
setFilterType("videos");
@ -274,7 +280,7 @@ export const VideoList = ({ mode }: VideoListProps) => {
setSelectedSubCategoryVideos(null);
ReactDOM.flushSync(() => {
getVideosHandler(true, true);
getFilesHandler(true, true);
});
};
@ -340,6 +346,7 @@ export const VideoList = ({ mode }: VideoListProps) => {
onChange={(e) => {
setFilterSearch(e.target.value);
}}
onKeyDown={searchOnEnter}
value={filterSearch}
placeholder="Search"
sx={{
@ -367,8 +374,9 @@ export const VideoList = ({ mode }: VideoListProps) => {
onChange={(e) => {
setFilterName(e.target.value);
}}
onKeyDown={searchOnEnter}
value={filterName}
placeholder="User's name"
placeholder="User's Name (Exact)"
sx={{
marginTop: "20px",
borderBottom: "1px solid white",
@ -640,7 +648,7 @@ export const VideoList = ({ mode }: VideoListProps) => {
</Button>
<Button
onClick={() => {
getVideosHandler(true);
getFilesHandler(true);
}}
sx={{
marginTop: "20px",
@ -833,7 +841,7 @@ export const VideoList = ({ mode }: VideoListProps) => {
</VideoContainer>
<LazyLoad
onLoadMore={getVideosHandler}
onLoadMore={getFilesHandler}
isLoading={isLoading}
></LazyLoad>
</Box>

17
src/pages/Home/VideoListComponentLevel.tsx → src/pages/Home/FileListComponentLevel.tsx

@ -12,20 +12,21 @@ import {
Typography,
useTheme
} from '@mui/material'
import { useFetchVideos } from '../../hooks/useFetchVideos'
import { useFetchFiles } from '../../hooks/useFetchFiles.tsx'
import LazyLoad from '../../components/common/LazyLoad'
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './VideoList-styles'
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './FileList-styles.tsx'
import ResponsiveImage from '../../components/ResponsiveImage'
import { formatDate, formatTimestampSeconds } from '../../utils/time'
import { Video } from '../../state/features/videoSlice'
import { queue } from '../../wrappers/GlobalWrapper'
import { QTUBE_VIDEO_BASE, categories, icons, subCategories, subCategories2, subCategories3 } from '../../constants'
import { QSHARE_FILE_BASE } from '../../constants/Identifiers.ts'
import { formatBytes } from '../VideoContent/VideoContent'
import {categories, icons, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
interface VideoListProps {
mode?: string
}
export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
export const FileListComponentLevel = ({ mode }: VideoListProps) => {
const { name: paramName } = useParams()
const theme = useTheme()
const [isLoading, setIsLoading] = useState<boolean>(true)
@ -48,15 +49,15 @@ export const VideoListComponentLevel = ({ mode }: VideoListProps) => {
const navigate = useNavigate()
const {
getVideo,
getNewVideos,
checkNewVideos,
getNewFiles,
checkNewFiles,
checkAndUpdateVideo
} = useFetchVideos()
} = useFetchFiles()
const getVideos = React.useCallback(async () => {
try {
const offset = videos.length
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}_&limit=50&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}_&limit=50&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`
const response = await fetch(url, {
method: 'GET',
headers: {

4
src/pages/Home/Home.tsx

@ -1,5 +1,5 @@
import React from 'react'
import { VideoList } from './VideoList'
import { FileList } from './FileList.tsx'
import { useSelector } from 'react-redux'
import { RootState } from '../../state/store'
@ -8,7 +8,7 @@ export const Home = () => {
return (
<>
<VideoList />
<FileList />
</>
)

4
src/pages/IndividualProfile/IndividualProfile.tsx

@ -1,5 +1,5 @@
import React, { useMemo } from 'react'
import { VideoListComponentLevel } from '../Home/VideoListComponentLevel'
import { FileListComponentLevel } from '../Home/FileListComponentLevel.tsx'
import { HeaderContainer, ProfileContainer } from './Profile-styles'
import { AuthorTextComment, StyledCardColComment, StyledCardHeaderComment } from '../VideoContent/VideoContent-styles'
import { Avatar, Box, useTheme } from '@mui/material'
@ -57,7 +57,7 @@ export const IndividualProfile = () => {
</StyledCardHeaderComment>
</Box>
</HeaderContainer>
<VideoListComponentLevel />
<FileListComponentLevel />
</ProfileContainer>
)

7
src/pages/VideoContent/VideoContent.tsx

@ -32,11 +32,12 @@ import { CommentSection } from "../../components/common/Comments/CommentSection"
import {
CrowdfundSubTitle,
CrowdfundSubTitleRow,
} from "../../components/UploadVideo/Upload-styles";
import { QTUBE_VIDEO_BASE, categories, subCategories, subCategories2, subCategories3 } from "../../constants";
} from "../../components/PublishFile/Upload-styles.tsx";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { Playlists } from "../../components/Playlists/Playlists";
import { DisplayHtml } from "../../components/common/TextEditor/DisplayHtml";
import FileElement from "../../components/common/FileElement";
import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
export function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
@ -110,7 +111,7 @@ export const VideoContent = () => {
if (!name || !id) return;
dispatch(setIsLoadingGlobal(true));
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0&identifier=${id}`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0&identifier=${id}`;
const response = await fetch(url, {
method: "GET",
headers: {

4
src/wrappers/GlobalWrapper.tsx

@ -15,7 +15,7 @@ import { setUserAvatarHash } from "../state/features/globalSlice";
import { VideoPlayerGlobal } from "../components/common/VideoPlayerGlobal";
import { Rnd } from "react-rnd";
import { RequestQueue } from "../utils/queue";
import { EditVideo } from "../components/EditVideo/EditVideo";
import { EditFile } from "../components/EditFile/EditFile.tsx";
import { EditPlaylist } from "../components/EditPlaylist/EditPlaylist";
import ConsentModal from "../components/common/ConsentModal";
@ -138,7 +138,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
userAvatar={userAvatar}
authenticate={askForAccountInformation}
/>
<EditVideo />
<EditFile />
<EditPlaylist />
<Rnd
onDragStart={onDragStart}

Loading…
Cancel
Save