mirror of
https://github.com/Qortal/q-share.git
synced 2025-02-01 07:42:22 +00:00
commit
a3bce74ad8
10
.prettierrc
Normal file
10
.prettierrc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true
|
||||||
|
}
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -17,6 +17,7 @@
|
|||||||
"dompurify": "^3.0.6",
|
"dompurify": "^3.0.6",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"prettier": "^3.2.4",
|
||||||
"quill-image-resize-module-react": "^3.0.0",
|
"quill-image-resize-module-react": "^3.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@ -3429,6 +3430,20 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/prop-types": {
|
"node_modules/prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
@ -6574,6 +6589,11 @@
|
|||||||
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
|
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"version": "3.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
|
||||||
|
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ=="
|
||||||
|
},
|
||||||
"prop-types": {
|
"prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"dompurify": "^3.0.6",
|
"dompurify": "^3.0.6",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"prettier": "^3.2.4",
|
||||||
"quill-image-resize-module-react": "^3.0.0",
|
"quill-image-resize-module-react": "^3.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -8,7 +8,7 @@ import { Provider } from "react-redux";
|
|||||||
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
||||||
import Notification from "./components/common/Notification/Notification";
|
import Notification from "./components/common/Notification/Notification";
|
||||||
import { Home } from "./pages/Home/Home";
|
import { Home } from "./pages/Home/Home";
|
||||||
import { VideoContent } from "./pages/VideoContent/VideoContent";
|
import { FileContent } from "./pages/FileContent/FileContent.tsx";
|
||||||
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
||||||
import { IndividualProfile } from "./pages/IndividualProfile/IndividualProfile";
|
import { IndividualProfile } from "./pages/IndividualProfile/IndividualProfile";
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ function App() {
|
|||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/share/:name/:id" element={<VideoContent />} />
|
<Route path="/share/:name/:id" element={<FileContent />} />
|
||||||
<Route path="/channel/:name" element={<IndividualProfile />} />
|
<Route path="/channel/:name" element={<IndividualProfile />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</GlobalWrapper>
|
</GlobalWrapper>
|
||||||
|
BIN
src/assets/icons/book.webp
Normal file
BIN
src/assets/icons/book.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
Binary file not shown.
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 28 KiB |
BIN
src/assets/icons/image.webp
Normal file
BIN
src/assets/icons/image.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
src/assets/icons/unknown.webp
Normal file
BIN
src/assets/icons/unknown.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
@ -1,4 +1,4 @@
|
|||||||
import React, {useEffect, useState} from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
CrowdfundActionButton,
|
CrowdfundActionButton,
|
||||||
CrowdfundActionButtonRow,
|
CrowdfundActionButtonRow,
|
||||||
@ -6,18 +6,7 @@ import {
|
|||||||
ModalBody,
|
ModalBody,
|
||||||
NewCrowdfundTitle,
|
NewCrowdfundTitle,
|
||||||
} from "./Upload-styles";
|
} from "./Upload-styles";
|
||||||
import {
|
import { Box, Modal, Typography, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
FormControl,
|
|
||||||
InputLabel,
|
|
||||||
MenuItem,
|
|
||||||
Modal,
|
|
||||||
OutlinedInput,
|
|
||||||
Select,
|
|
||||||
SelectChangeEvent,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import RemoveIcon from "@mui/icons-material/Remove";
|
import RemoveIcon from "@mui/icons-material/Remove";
|
||||||
|
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
@ -27,13 +16,22 @@ import {useDropzone} from "react-dropzone";
|
|||||||
import { setNotification } from "../../state/features/notificationsSlice";
|
import { setNotification } from "../../state/features/notificationsSlice";
|
||||||
import { objectToBase64 } from "../../utils/toBase64";
|
import { objectToBase64 } from "../../utils/toBase64";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import {setEditVideo, updateInHashMap, updateVideo,} from "../../state/features/videoSlice";
|
import {
|
||||||
import {QSHARE_FILE_BASE,} from "../../constants/Identifiers.ts";
|
setEditFile,
|
||||||
|
updateFile,
|
||||||
|
updateInHashMap,
|
||||||
|
} from "../../state/features/fileSlice.ts";
|
||||||
|
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
||||||
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
|
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
|
||||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||||
import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
|
import { allCategoryData } from "../../constants/Categories/1stCategories.ts";
|
||||||
import { titleFormatter } from "../../constants/Misc.ts";
|
import { titleFormatter } from "../../constants/Misc.ts";
|
||||||
|
import {
|
||||||
|
CategoryList,
|
||||||
|
CategoryListRef,
|
||||||
|
getCategoriesFromObject,
|
||||||
|
} from "../common/CategoryList/CategoryList.tsx";
|
||||||
|
|
||||||
const uid = new ShortUniqueId();
|
const uid = new ShortUniqueId();
|
||||||
const shortuid = new ShortUniqueId({ length: 5 });
|
const shortuid = new ShortUniqueId({ length: 5 });
|
||||||
@ -53,7 +51,7 @@ interface VideoFile {
|
|||||||
description: string;
|
description: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
identifier?: string;
|
identifier?: string;
|
||||||
filename?:string
|
filename?: string;
|
||||||
}
|
}
|
||||||
export const EditFile = () => {
|
export const EditFile = () => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -62,8 +60,8 @@ export const EditFile = () => {
|
|||||||
const userAddress = useSelector(
|
const userAddress = useSelector(
|
||||||
(state: RootState) => state.auth?.user?.address
|
(state: RootState) => state.auth?.user?.address
|
||||||
);
|
);
|
||||||
const editVideoProperties = useSelector(
|
const editFileProperties = useSelector(
|
||||||
(state: RootState) => state.video.editVideoProperties
|
(state: RootState) => state.file.editFileProperties
|
||||||
);
|
);
|
||||||
const [publishes, setPublishes] = useState<any>(null);
|
const [publishes, setPublishes] = useState<any>(null);
|
||||||
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
|
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
|
||||||
@ -75,21 +73,14 @@ export const EditFile = () => {
|
|||||||
const [coverImage, setCoverImage] = useState<string>("");
|
const [coverImage, setCoverImage] = useState<string>("");
|
||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
const [files, setFiles] = useState<VideoFile[]>([]);
|
const [files, setFiles] = useState<VideoFile[]>([]);
|
||||||
|
const [editCategories, setEditCategories] = useState<string[]>([]);
|
||||||
const [selectedCategoryVideos, setSelectedCategoryVideos] =
|
const categoryListRef = useRef<CategoryListRef>(null);
|
||||||
useState<any>(null);
|
|
||||||
const [selectedSubCategoryVideos, setSelectedSubCategoryVideos] =
|
|
||||||
useState<any>(null);
|
|
||||||
const [selectedSubCategoryVideos2, setSelectedSubCategoryVideos2] =
|
|
||||||
useState<any>(null);
|
|
||||||
const [selectedSubCategoryVideos3, setSelectedSubCategoryVideos3] =
|
|
||||||
useState<any>(null);
|
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
maxFiles: 10,
|
maxFiles: 10,
|
||||||
maxSize: 419430400, // 400 MB in bytes
|
maxSize: 419430400, // 400 MB in bytes
|
||||||
onDrop: (acceptedFiles, rejectedFiles) => {
|
onDrop: (acceptedFiles, rejectedFiles) => {
|
||||||
const formatArray = acceptedFiles.map((item) => {
|
const formatArray = acceptedFiles.map(item => {
|
||||||
return {
|
return {
|
||||||
file: item,
|
file: item,
|
||||||
title: "",
|
title: "",
|
||||||
@ -98,11 +89,11 @@ export const EditFile = () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
setFiles((prev) => [...prev, ...formatArray]);
|
setFiles(prev => [...prev, ...formatArray]);
|
||||||
|
|
||||||
let errorString = null;
|
let errorString = null;
|
||||||
rejectedFiles.forEach(({ file, errors }) => {
|
rejectedFiles.forEach(({ file, errors }) => {
|
||||||
errors.forEach((error) => {
|
errors.forEach(error => {
|
||||||
if (error.code === "file-too-large") {
|
if (error.code === "file-too-large") {
|
||||||
errorString = "File must be under 400mb";
|
errorString = "File must be under 400mb";
|
||||||
}
|
}
|
||||||
@ -120,109 +111,21 @@ export const EditFile = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (editVideoProperties) {
|
|
||||||
// const descriptionString = editVideoProperties?.description || "";
|
|
||||||
// // Splitting the string at the asterisks
|
|
||||||
// const parts = descriptionString.split("**");
|
|
||||||
|
|
||||||
// // The part within the asterisks
|
|
||||||
// const extractedString = parts[1];
|
|
||||||
|
|
||||||
// // The part after the last asterisks
|
|
||||||
// const description = parts[2] || ""; // Using '|| '' to handle cases where there is no text after the last **
|
|
||||||
// setTitle(editVideoProperties?.title || "");
|
|
||||||
// setDescription(editVideoProperties?.fullDescription || "");
|
|
||||||
// setCoverImage(editVideoProperties?.videoImage || "");
|
|
||||||
|
|
||||||
// // Split the extracted string into key-value pairs
|
|
||||||
// const keyValuePairs = extractedString.split(";");
|
|
||||||
|
|
||||||
// // Initialize variables to hold the category and subcategory values
|
|
||||||
// let category, subcategory;
|
|
||||||
|
|
||||||
// // Loop through each key-value pair
|
|
||||||
// keyValuePairs.forEach((pair) => {
|
|
||||||
// const [key, value] = pair.split(":");
|
|
||||||
|
|
||||||
// // Check the key and assign the value to the appropriate variable
|
|
||||||
// if (key === "category") {
|
|
||||||
// category = value;
|
|
||||||
// } else if (key === "subcategory") {
|
|
||||||
// subcategory = value;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if(category){
|
|
||||||
// const selectedOption = categories.find((option) => option.id === +category);
|
|
||||||
// setSelectedCategoryVideos(selectedOption || null);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(subcategory){
|
|
||||||
// const selectedOption = categories.find((option) => option.id === +subcategory);
|
|
||||||
// setSelectedCategoryVideos(selectedOption || null);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }, [editVideoProperties]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editVideoProperties) {
|
if (editFileProperties) {
|
||||||
setTitle(editVideoProperties?.title || "");
|
setTitle(editFileProperties?.title || "");
|
||||||
setFiles(editVideoProperties?.files || [])
|
setFiles(editFileProperties?.files || []);
|
||||||
if(editVideoProperties?.htmlDescription){
|
if (editFileProperties?.htmlDescription) {
|
||||||
setDescription(editVideoProperties?.htmlDescription);
|
setDescription(editFileProperties?.htmlDescription);
|
||||||
|
} else if (editFileProperties?.fullDescription) {
|
||||||
} else if(editVideoProperties?.fullDescription) {
|
const paragraph = `<p>${editFileProperties?.fullDescription}</p>`;
|
||||||
const paragraph = `<p>${editVideoProperties?.fullDescription}</p>`
|
|
||||||
setDescription(paragraph);
|
setDescription(paragraph);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
setEditCategories(getCategoriesFromObject(editFileProperties));
|
||||||
if (editVideoProperties?.category) {
|
|
||||||
const selectedOption = categories.find(
|
|
||||||
(option) => option.id === +editVideoProperties.category
|
|
||||||
);
|
|
||||||
setSelectedCategoryVideos(selectedOption || null);
|
|
||||||
}
|
}
|
||||||
if (
|
}, [editFileProperties]);
|
||||||
editVideoProperties?.category &&
|
|
||||||
editVideoProperties?.subcategory &&
|
|
||||||
subCategories[+editVideoProperties?.category]
|
|
||||||
) {
|
|
||||||
const selectedOption = subCategories[
|
|
||||||
+editVideoProperties?.category
|
|
||||||
]?.find((option) => option.id === +editVideoProperties.subcategory);
|
|
||||||
setSelectedSubCategoryVideos(selectedOption || null);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
editVideoProperties?.category &&
|
|
||||||
editVideoProperties?.subcategory2 &&
|
|
||||||
subCategories2[+editVideoProperties?.subcategory]
|
|
||||||
) {
|
|
||||||
const selectedOption = subCategories2[
|
|
||||||
+editVideoProperties?.subcategory
|
|
||||||
]?.find((option) => option.id === +editVideoProperties.subcategory2);
|
|
||||||
setSelectedSubCategoryVideos2(selectedOption || null);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
editVideoProperties?.category &&
|
|
||||||
editVideoProperties?.subcategory3 &&
|
|
||||||
subCategories3[+editVideoProperties?.subcategory2]
|
|
||||||
) {
|
|
||||||
|
|
||||||
const selectedOption = subCategories3[
|
|
||||||
+editVideoProperties?.subcategory2
|
|
||||||
]?.find((option) => option.id === +editVideoProperties.subcategory3);
|
|
||||||
setSelectedSubCategoryVideos3(selectedOption || null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}, [editVideoProperties]);
|
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
dispatch(setEditVideo(null));
|
dispatch(setEditFile(null));
|
||||||
setVideoPropertiesToSetToRedux(null);
|
setVideoPropertiesToSetToRedux(null);
|
||||||
setFile(null);
|
setFile(null);
|
||||||
setTitle("");
|
setTitle("");
|
||||||
@ -232,10 +135,11 @@ export const EditFile = () => {
|
|||||||
|
|
||||||
async function publishQDNResource() {
|
async function publishQDNResource() {
|
||||||
try {
|
try {
|
||||||
|
const categoryList = categoryListRef.current?.getSelectedCategories();
|
||||||
if (!title) throw new Error("Please enter a title");
|
if (!title) throw new Error("Please enter a title");
|
||||||
if (!description) throw new Error("Please enter a description");
|
if (!description) throw new Error("Please enter a description");
|
||||||
if (!selectedCategoryVideos) throw new Error("Please select a category");
|
if (!categoryList[0]) throw new Error("Please select a category");
|
||||||
if (!editVideoProperties) return;
|
if (!editFileProperties) return;
|
||||||
if (!userAddress) throw new Error("Unable to locate user address");
|
if (!userAddress) throw new Error("Unable to locate user address");
|
||||||
if (files.length === 0) throw new Error("Add at least one file");
|
if (files.length === 0) throw new Error("Add at least one file");
|
||||||
|
|
||||||
@ -249,7 +153,7 @@ export const EditFile = () => {
|
|||||||
"Cannot publish without access to your name. Please authenticate.";
|
"Cannot publish without access to your name. Please authenticate.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editVideoProperties?.user !== username) {
|
if (editFileProperties?.user !== username) {
|
||||||
errorMsg = "Cannot publish another user's resource";
|
errorMsg = "Cannot publish another user's resource";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,15 +166,11 @@ export const EditFile = () => {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let fileReferences = []
|
let fileReferences = [];
|
||||||
|
|
||||||
let listOfPublishes = [];
|
let listOfPublishes = [];
|
||||||
const fullDescription = extractTextFromHTML(description);
|
const fullDescription = extractTextFromHTML(description);
|
||||||
|
|
||||||
const category = selectedCategoryVideos.id;
|
|
||||||
const subcategory = selectedSubCategoryVideos?.id || "";
|
|
||||||
const subcategory2 = selectedSubCategoryVideos2?.id || "";
|
|
||||||
const subcategory3 = selectedSubCategoryVideos3?.id || "";
|
|
||||||
const sanitizeTitle = title
|
const sanitizeTitle = title
|
||||||
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
||||||
.replace(/\s+/g, "-")
|
.replace(/\s+/g, "-")
|
||||||
@ -278,25 +178,22 @@ export const EditFile = () => {
|
|||||||
.trim()
|
.trim()
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
||||||
|
|
||||||
for (const publish of files) {
|
for (const publish of files) {
|
||||||
if (publish?.identifier) {
|
if (publish?.identifier) {
|
||||||
fileReferences.push(publish)
|
fileReferences.push(publish);
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
const file = publish.file;
|
const file = publish.file;
|
||||||
const id = uid();
|
const id = uid();
|
||||||
|
|
||||||
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
|
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let fileExtension = "";
|
let fileExtension = "";
|
||||||
const fileExtensionSplit = file?.name?.split(".");
|
const fileExtensionSplit = file?.name?.split(".");
|
||||||
if (fileExtensionSplit?.length > 1) {
|
if (fileExtensionSplit?.length > 1) {
|
||||||
fileExtension = fileExtensionSplit?.pop() || "";
|
fileExtension = fileExtensionSplit?.pop() || "";
|
||||||
}
|
}
|
||||||
let firstPartName = fileExtensionSplit[0]
|
let firstPartName = fileExtensionSplit[0];
|
||||||
|
|
||||||
let filename = firstPartName.slice(0, 15);
|
let filename = firstPartName.slice(0, 15);
|
||||||
|
|
||||||
@ -312,15 +209,13 @@ export const EditFile = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (fileExtension) {
|
if (fileExtension) {
|
||||||
filename = `${alphanumericString.trim()}.${fileExtension}`
|
filename = `${alphanumericString.trim()}.${fileExtension}`;
|
||||||
} else {
|
} else {
|
||||||
filename = alphanumericString
|
filename = alphanumericString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let metadescription =
|
let metadescription =
|
||||||
`**cat:${category};sub:${subcategory};sub2:${subcategory2};sub3:${subcategory3}**` +
|
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
|
||||||
fullDescription.slice(0, 150);
|
fullDescription.slice(0, 150);
|
||||||
|
|
||||||
const requestBodyVideo: any = {
|
const requestBodyVideo: any = {
|
||||||
@ -339,27 +234,24 @@ export const EditFile = () => {
|
|||||||
filename: file.name,
|
filename: file.name,
|
||||||
identifier,
|
identifier,
|
||||||
name,
|
name,
|
||||||
service: 'FILE',
|
service: "FILE",
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
size: file.size
|
size: file.size,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileObject: any = {
|
const fileObject: any = {
|
||||||
title,
|
title,
|
||||||
version: editVideoProperties.version,
|
version: editFileProperties.version,
|
||||||
fullDescription,
|
fullDescription,
|
||||||
htmlDescription: description,
|
htmlDescription: description,
|
||||||
commentsId: editVideoProperties.commentsId,
|
commentsId: editFileProperties.commentsId,
|
||||||
category,
|
...categoryListRef.current?.categoriesToObject(),
|
||||||
subcategory,
|
files: fileReferences,
|
||||||
subcategory2,
|
|
||||||
subcategory3,
|
|
||||||
files: fileReferences
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let metadescription =
|
let metadescription =
|
||||||
`**cat:${category};sub:${subcategory};sub2:${subcategory2}**` +
|
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
|
||||||
fullDescription.slice(0, 150);
|
fullDescription.slice(0, 150);
|
||||||
|
|
||||||
const crowdfundObjectToBase64 = await objectToBase64(fileObject);
|
const crowdfundObjectToBase64 = await objectToBase64(fileObject);
|
||||||
@ -372,7 +264,7 @@ export const EditFile = () => {
|
|||||||
data64: crowdfundObjectToBase64,
|
data64: crowdfundObjectToBase64,
|
||||||
title: title.slice(0, 50),
|
title: title.slice(0, 50),
|
||||||
description: metadescription,
|
description: metadescription,
|
||||||
identifier: editVideoProperties.id,
|
identifier: editFileProperties.id,
|
||||||
tag1: QSHARE_FILE_BASE,
|
tag1: QSHARE_FILE_BASE,
|
||||||
filename: `video_metadata.json`,
|
filename: `video_metadata.json`,
|
||||||
};
|
};
|
||||||
@ -385,7 +277,7 @@ export const EditFile = () => {
|
|||||||
setPublishes(multiplePublish);
|
setPublishes(multiplePublish);
|
||||||
setIsOpenMultiplePublish(true);
|
setIsOpenMultiplePublish(true);
|
||||||
setVideoPropertiesToSetToRedux({
|
setVideoPropertiesToSetToRedux({
|
||||||
...editVideoProperties,
|
...editFileProperties,
|
||||||
...fileObject,
|
...fileObject,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -429,52 +321,10 @@ export const EditFile = () => {
|
|||||||
// });
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOptionCategoryChangeVideos = (
|
|
||||||
event: SelectChangeEvent<string>
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = categories.find((option) => option.id === +optionId);
|
|
||||||
setSelectedCategoryVideos(selectedOption || null);
|
|
||||||
};
|
|
||||||
const handleOptionSubCategoryChangeVideos = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos(selectedOption || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const handleOptionSubCategoryChangeVideos2 = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos2(selectedOption || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOptionSubCategoryChangeVideos3 = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos3(selectedOption || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
open={!!editVideoProperties}
|
open={!!editFileProperties}
|
||||||
aria-labelledby="modal-title"
|
aria-labelledby="modal-title"
|
||||||
aria-describedby="modal-description"
|
aria-describedby="modal-description"
|
||||||
>
|
>
|
||||||
@ -503,7 +353,7 @@ export const EditFile = () => {
|
|||||||
<Typography>Click to add more files</Typography>
|
<Typography>Click to add more files</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{files.map((file, index) => {
|
{files.map((file, index) => {
|
||||||
const isExistingFile = !!file?.identifier
|
const isExistingFile = !!file?.identifier;
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
<Box
|
<Box
|
||||||
@ -513,10 +363,12 @@ export const EditFile = () => {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{isExistingFile? file.filename : file?.file?.name}</Typography>
|
<Typography>
|
||||||
|
{isExistingFile ? file.filename : file?.file?.name}
|
||||||
|
</Typography>
|
||||||
<RemoveIcon
|
<RemoveIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFiles((prev) => {
|
setFiles(prev => {
|
||||||
const copyPrev = [...prev];
|
const copyPrev = [...prev];
|
||||||
copyPrev.splice(index, 1);
|
copyPrev.splice(index, 1);
|
||||||
return copyPrev;
|
return copyPrev;
|
||||||
@ -540,129 +392,21 @@ export const EditFile = () => {
|
|||||||
>
|
>
|
||||||
{files?.length > 0 && (
|
{files?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
flexDirection: 'column',
|
display: "flex",
|
||||||
gap: '20px',
|
flexDirection: "column",
|
||||||
width: '50%'
|
gap: "20px",
|
||||||
}}>
|
width: "100%",
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
}}
|
||||||
<InputLabel id="Category">Select a Category</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Category"
|
|
||||||
input={<OutlinedInput label="Select a Category" />}
|
|
||||||
value={selectedCategoryVideos?.id || ""}
|
|
||||||
onChange={handleOptionCategoryChangeVideos}
|
|
||||||
>
|
>
|
||||||
{categories.map((option) => (
|
<CategoryList
|
||||||
<MenuItem key={option.id} value={option.id}>
|
categoryData={allCategoryData}
|
||||||
{option.name}
|
initialCategories={editCategories}
|
||||||
</MenuItem>
|
columns={3}
|
||||||
))}
|
ref={categoryListRef}
|
||||||
</Select>
|
/>
|
||||||
</FormControl>
|
|
||||||
</Box>
|
</Box>
|
||||||
{selectedCategoryVideos && (
|
|
||||||
<>
|
|
||||||
<Box sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '20px',
|
|
||||||
width: '50%'
|
|
||||||
}}>
|
|
||||||
|
|
||||||
|
|
||||||
{selectedCategoryVideos &&
|
|
||||||
subCategories[selectedCategoryVideos?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">
|
|
||||||
Select a Sub-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={
|
|
||||||
<OutlinedInput label="Select a Sub-Category" />
|
|
||||||
}
|
|
||||||
value={selectedSubCategoryVideos?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos(
|
|
||||||
e,
|
|
||||||
subCategories[selectedCategoryVideos?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{subCategories[selectedCategoryVideos.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
{selectedSubCategoryVideos &&
|
|
||||||
subCategories2[selectedSubCategoryVideos?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">
|
|
||||||
Select a Sub-sub-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={
|
|
||||||
<OutlinedInput label="Select a Sub-sub-Category" />
|
|
||||||
}
|
|
||||||
value={selectedSubCategoryVideos2?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos2(
|
|
||||||
e,
|
|
||||||
subCategories2[selectedSubCategoryVideos?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{subCategories2[selectedSubCategoryVideos.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
{selectedSubCategoryVideos2 &&
|
|
||||||
subCategories3[selectedSubCategoryVideos2?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">
|
|
||||||
Select a Sub-3x-subCategory
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={
|
|
||||||
<OutlinedInput label="Select a Sub-3x-Category" />
|
|
||||||
}
|
|
||||||
value={selectedSubCategoryVideos3?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos3(
|
|
||||||
e,
|
|
||||||
subCategories3[selectedSubCategoryVideos2?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{subCategories3[selectedSubCategoryVideos2.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@ -673,7 +417,7 @@ export const EditFile = () => {
|
|||||||
label="Title of share"
|
label="Title of share"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => {
|
onChange={e => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
const formattedValue = value.replace(titleFormatter, "");
|
const formattedValue = value.replace(titleFormatter, "");
|
||||||
setTitle(formattedValue);
|
setTitle(formattedValue);
|
||||||
@ -690,7 +434,7 @@ export const EditFile = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<TextEditor
|
<TextEditor
|
||||||
inlineContent={description}
|
inlineContent={description}
|
||||||
setInlineContent={(value) => {
|
setInlineContent={value => {
|
||||||
setDescription(value);
|
setDescription(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -730,22 +474,22 @@ export const EditFile = () => {
|
|||||||
{isOpenMultiplePublish && (
|
{isOpenMultiplePublish && (
|
||||||
<MultiplePublish
|
<MultiplePublish
|
||||||
isOpen={isOpenMultiplePublish}
|
isOpen={isOpenMultiplePublish}
|
||||||
onError={(messageNotification)=> {
|
onError={messageNotification => {
|
||||||
setIsOpenMultiplePublish(false);
|
setIsOpenMultiplePublish(false);
|
||||||
setPublishes(null)
|
setPublishes(null);
|
||||||
if (messageNotification) {
|
if (messageNotification) {
|
||||||
dispatch(
|
dispatch(
|
||||||
setNotification({
|
setNotification({
|
||||||
msg: messageNotification,
|
msg: messageNotification,
|
||||||
alertType: 'error'
|
alertType: "error",
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
setIsOpenMultiplePublish(false);
|
setIsOpenMultiplePublish(false);
|
||||||
const clonedCopy = structuredClone(videoPropertiesToSetToRedux);
|
const clonedCopy = structuredClone(videoPropertiesToSetToRedux);
|
||||||
dispatch(updateVideo(clonedCopy));
|
dispatch(updateFile(clonedCopy));
|
||||||
dispatch(updateInHashMap(clonedCopy));
|
dispatch(updateInHashMap(clonedCopy));
|
||||||
dispatch(
|
dispatch(
|
||||||
setNotification({
|
setNotification({
|
||||||
|
@ -34,21 +34,27 @@ import { setNotification } from "../../state/features/notificationsSlice";
|
|||||||
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
|
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import {
|
import {
|
||||||
upsertVideosBeginning,
|
upsertFilesBeginning,
|
||||||
addToHashMap,
|
addToHashMap,
|
||||||
upsertVideos,
|
upsertFiles,
|
||||||
setEditVideo,
|
setEditFile,
|
||||||
updateVideo,
|
updateFile,
|
||||||
updateInHashMap,
|
updateInHashMap,
|
||||||
setEditPlaylist,
|
setEditPlaylist,
|
||||||
} from "../../state/features/videoSlice";
|
} from "../../state/features/fileSlice.ts";
|
||||||
import ImageUploader from "../common/ImageUploader";
|
import ImageUploader from "../common/ImageUploader";
|
||||||
import { QSHARE_PLAYLIST_BASE, QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
import {
|
||||||
|
QSHARE_PLAYLIST_BASE,
|
||||||
|
QSHARE_FILE_BASE,
|
||||||
|
} from "../../constants/Identifiers.ts";
|
||||||
import { Playlists } from "../Playlists/Playlists";
|
import { Playlists } from "../Playlists/Playlists";
|
||||||
import { PlaylistListEdit } from "../PlaylistListEdit/PlaylistListEdit";
|
import { PlaylistListEdit } from "../PlaylistListEdit/PlaylistListEdit";
|
||||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||||
import {categories, subCategories} from "../../constants/Categories.ts";
|
import {
|
||||||
|
firstCategories,
|
||||||
|
secondCategories,
|
||||||
|
} from "../../constants/Categories/1stCategories.ts";
|
||||||
|
|
||||||
const uid = new ShortUniqueId();
|
const uid = new ShortUniqueId();
|
||||||
const shortuid = new ShortUniqueId({ length: 5 });
|
const shortuid = new ShortUniqueId({ length: 5 });
|
||||||
@ -76,7 +82,7 @@ export const EditPlaylist = () => {
|
|||||||
(state: RootState) => state.auth?.user?.address
|
(state: RootState) => state.auth?.user?.address
|
||||||
);
|
);
|
||||||
const editVideoProperties = useSelector(
|
const editVideoProperties = useSelector(
|
||||||
(state: RootState) => state.video.editPlaylistProperties
|
(state: RootState) => state.file.editPlaylistProperties
|
||||||
);
|
);
|
||||||
const [playlistData, setPlaylistData] = useState<any>(null);
|
const [playlistData, setPlaylistData] = useState<any>(null);
|
||||||
const [title, setTitle] = useState<string>("");
|
const [title, setTitle] = useState<string>("");
|
||||||
@ -89,16 +95,16 @@ export const EditPlaylist = () => {
|
|||||||
useState<any>(null);
|
useState<any>(null);
|
||||||
|
|
||||||
const isNew = useMemo(() => {
|
const isNew = useMemo(() => {
|
||||||
return editVideoProperties?.mode === 'new'
|
return editVideoProperties?.mode === "new";
|
||||||
}, [editVideoProperties])
|
}, [editVideoProperties]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
setPlaylistData({
|
setPlaylistData({
|
||||||
videos: []
|
videos: [],
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}, [isNew])
|
}, [isNew]);
|
||||||
|
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
// if (editVideoProperties) {
|
// if (editVideoProperties) {
|
||||||
@ -146,7 +152,7 @@ export const EditPlaylist = () => {
|
|||||||
// }
|
// }
|
||||||
// }, [editVideoProperties]);
|
// }, [editVideoProperties]);
|
||||||
|
|
||||||
const checkforPlaylist = React.useCallback(async (videoList) => {
|
const checkforPlaylist = React.useCallback(async videoList => {
|
||||||
try {
|
try {
|
||||||
const combinedData: any = {};
|
const combinedData: any = {};
|
||||||
const videos = [];
|
const videos = [];
|
||||||
@ -178,18 +184,16 @@ export const EditPlaylist = () => {
|
|||||||
|
|
||||||
if (editVideoProperties?.htmlDescription) {
|
if (editVideoProperties?.htmlDescription) {
|
||||||
setDescription(editVideoProperties?.htmlDescription);
|
setDescription(editVideoProperties?.htmlDescription);
|
||||||
|
|
||||||
} else if (editVideoProperties?.description) {
|
} else if (editVideoProperties?.description) {
|
||||||
const paragraph = `<p>${editVideoProperties?.description}</p>`
|
const paragraph = `<p>${editVideoProperties?.description}</p>`;
|
||||||
setDescription(paragraph);
|
setDescription(paragraph);
|
||||||
|
|
||||||
}
|
}
|
||||||
setCoverImage(editVideoProperties?.image || "");
|
setCoverImage(editVideoProperties?.image || "");
|
||||||
setVideos(editVideoProperties?.videos || []);
|
setVideos(editVideoProperties?.videos || []);
|
||||||
|
|
||||||
if (editVideoProperties?.category) {
|
if (editVideoProperties?.category) {
|
||||||
const selectedOption = categories.find(
|
const selectedOption = firstCategories.find(
|
||||||
(option) => option.id === +editVideoProperties.category
|
option => option.id === +editVideoProperties.category
|
||||||
);
|
);
|
||||||
setSelectedCategoryVideos(selectedOption || null);
|
setSelectedCategoryVideos(selectedOption || null);
|
||||||
}
|
}
|
||||||
@ -197,11 +201,11 @@ export const EditPlaylist = () => {
|
|||||||
if (
|
if (
|
||||||
editVideoProperties?.category &&
|
editVideoProperties?.category &&
|
||||||
editVideoProperties?.subcategory &&
|
editVideoProperties?.subcategory &&
|
||||||
subCategories[+editVideoProperties?.category]
|
secondCategories[+editVideoProperties?.category]
|
||||||
) {
|
) {
|
||||||
const selectedOption = subCategories[
|
const selectedOption = secondCategories[
|
||||||
+editVideoProperties?.category
|
+editVideoProperties?.category
|
||||||
]?.find((option) => option.id === +editVideoProperties.subcategory);
|
]?.find(option => option.id === +editVideoProperties.subcategory);
|
||||||
setSelectedSubCategoryVideos(selectedOption || null);
|
setSelectedSubCategoryVideos(selectedOption || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,24 +216,22 @@ export const EditPlaylist = () => {
|
|||||||
}, [editVideoProperties]);
|
}, [editVideoProperties]);
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setTitle("")
|
setTitle("");
|
||||||
setDescription("")
|
setDescription("");
|
||||||
setVideos([])
|
setVideos([]);
|
||||||
setPlaylistData(null)
|
setPlaylistData(null);
|
||||||
setSelectedCategoryVideos(null)
|
setSelectedCategoryVideos(null);
|
||||||
setSelectedSubCategoryVideos(null)
|
setSelectedSubCategoryVideos(null);
|
||||||
setCoverImage("")
|
setCoverImage("");
|
||||||
dispatch(setEditPlaylist(null));
|
dispatch(setEditPlaylist(null));
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function publishQDNResource() {
|
async function publishQDNResource() {
|
||||||
try {
|
try {
|
||||||
|
if (!title) throw new Error("Please enter a title");
|
||||||
if(!title) throw new Error('Please enter a title')
|
if (!description) throw new Error("Please enter a description");
|
||||||
if(!description) throw new Error('Please enter a description')
|
if (!coverImage) throw new Error("Please select cover image");
|
||||||
if(!coverImage) throw new Error('Please select cover image')
|
if (!selectedCategoryVideos) throw new Error("Please select a category");
|
||||||
if(!selectedCategoryVideos) throw new Error('Please select a category')
|
|
||||||
|
|
||||||
if (!editVideoProperties) return;
|
if (!editVideoProperties) return;
|
||||||
if (!userAddress) throw new Error("Unable to locate user address");
|
if (!userAddress) throw new Error("Unable to locate user address");
|
||||||
@ -259,7 +261,7 @@ export const EditPlaylist = () => {
|
|||||||
const category = selectedCategoryVideos.id;
|
const category = selectedCategoryVideos.id;
|
||||||
const subcategory = selectedSubCategoryVideos?.id || "";
|
const subcategory = selectedSubCategoryVideos?.id || "";
|
||||||
|
|
||||||
const videoStructured = playlistData.videos.map((item) => {
|
const videoStructured = playlistData.videos.map(item => {
|
||||||
const descriptionVid = item?.metadata?.description;
|
const descriptionVid = item?.metadata?.description;
|
||||||
if (!descriptionVid) throw new Error("cannot find video code");
|
if (!descriptionVid) throw new Error("cannot find video code");
|
||||||
|
|
||||||
@ -287,13 +289,12 @@ export const EditPlaylist = () => {
|
|||||||
});
|
});
|
||||||
const id = uid();
|
const id = uid();
|
||||||
|
|
||||||
let commentsId = editVideoProperties?.id
|
let commentsId = editVideoProperties?.id;
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
commentsId = `${QSHARE_PLAYLIST_BASE}_cm_${id}`
|
commentsId = `${QSHARE_PLAYLIST_BASE}_cm_${id}`;
|
||||||
}
|
}
|
||||||
const stringDescription = extractTextFromHTML(description)
|
const stringDescription = extractTextFromHTML(description);
|
||||||
|
|
||||||
|
|
||||||
const playlistObject: any = {
|
const playlistObject: any = {
|
||||||
title,
|
title,
|
||||||
@ -304,10 +305,10 @@ export const EditPlaylist = () => {
|
|||||||
videos: videoStructured,
|
videos: videoStructured,
|
||||||
commentsId: commentsId,
|
commentsId: commentsId,
|
||||||
category,
|
category,
|
||||||
subcategory
|
subcategory,
|
||||||
};
|
};
|
||||||
|
|
||||||
const codes = videoStructured.map((item) => `c:${item.code};`).join("");
|
const codes = videoStructured.map(item => `c:${item.code};`).join("");
|
||||||
let metadescription =
|
let metadescription =
|
||||||
`**category:${category};subcategory:${subcategory};${codes}**` +
|
`**category:${category};subcategory:${subcategory};${codes}**` +
|
||||||
stringDescription.slice(0, 120);
|
stringDescription.slice(0, 120);
|
||||||
@ -315,7 +316,7 @@ export const EditPlaylist = () => {
|
|||||||
const crowdfundObjectToBase64 = await objectToBase64(playlistObject);
|
const crowdfundObjectToBase64 = await objectToBase64(playlistObject);
|
||||||
// Description is obtained from raw data
|
// Description is obtained from raw data
|
||||||
|
|
||||||
let identifier = editVideoProperties?.id
|
let identifier = editVideoProperties?.id;
|
||||||
const sanitizeTitle = title
|
const sanitizeTitle = title
|
||||||
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
||||||
.replace(/\s+/g, "-")
|
.replace(/\s+/g, "-")
|
||||||
@ -344,17 +345,13 @@ export const EditPlaylist = () => {
|
|||||||
id: identifier,
|
id: identifier,
|
||||||
service: "PLAYLIST",
|
service: "PLAYLIST",
|
||||||
name: username,
|
name: username,
|
||||||
...playlistObject
|
...playlistObject,
|
||||||
}
|
};
|
||||||
dispatch(
|
dispatch(updateFile(objectToStore));
|
||||||
updateVideo(objectToStore)
|
dispatch(updateInHashMap(objectToStore));
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
updateInHashMap(objectToStore)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateVideo({
|
updateFile({
|
||||||
...editVideoProperties,
|
...editVideoProperties,
|
||||||
...playlistObject,
|
...playlistObject,
|
||||||
})
|
})
|
||||||
@ -367,8 +364,6 @@ export const EditPlaylist = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
let notificationObj: any = null;
|
let notificationObj: any = null;
|
||||||
@ -415,7 +410,9 @@ export const EditPlaylist = () => {
|
|||||||
event: SelectChangeEvent<string>
|
event: SelectChangeEvent<string>
|
||||||
) => {
|
) => {
|
||||||
const optionId = event.target.value;
|
const optionId = event.target.value;
|
||||||
const selectedOption = categories.find((option) => option.id === +optionId);
|
const selectedOption = firstCategories.find(
|
||||||
|
option => option.id === +optionId
|
||||||
|
);
|
||||||
setSelectedCategoryVideos(selectedOption || null);
|
setSelectedCategoryVideos(selectedOption || null);
|
||||||
};
|
};
|
||||||
const handleOptionSubCategoryChangeVideos = (
|
const handleOptionSubCategoryChangeVideos = (
|
||||||
@ -424,25 +421,26 @@ export const EditPlaylist = () => {
|
|||||||
) => {
|
) => {
|
||||||
const optionId = event.target.value;
|
const optionId = event.target.value;
|
||||||
const selectedOption = subcategories.find(
|
const selectedOption = subcategories.find(
|
||||||
(option) => option.id === +optionId
|
option => option.id === +optionId
|
||||||
);
|
);
|
||||||
setSelectedSubCategoryVideos(selectedOption || null);
|
setSelectedSubCategoryVideos(selectedOption || null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeVideo = index => {
|
||||||
const removeVideo = (index) => {
|
|
||||||
const copyData = structuredClone(playlistData);
|
const copyData = structuredClone(playlistData);
|
||||||
copyData.videos.splice(index, 1);
|
copyData.videos.splice(index, 1);
|
||||||
setPlaylistData(copyData);
|
setPlaylistData(copyData);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addVideo = (data) => {
|
const addVideo = data => {
|
||||||
if (playlistData?.videos?.length > 9) {
|
if (playlistData?.videos?.length > 9) {
|
||||||
dispatch(setNotification({
|
dispatch(
|
||||||
|
setNotification({
|
||||||
msg: "Max 10 videos per playlist",
|
msg: "Max 10 videos per playlist",
|
||||||
alertType: "error",
|
alertType: "error",
|
||||||
}));
|
})
|
||||||
return
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const copyData = structuredClone(playlistData);
|
const copyData = structuredClone(playlistData);
|
||||||
copyData.videos = [...copyData.videos, { ...data }];
|
copyData.videos = [...copyData.videos, { ...data }];
|
||||||
@ -466,10 +464,8 @@ export const EditPlaylist = () => {
|
|||||||
>
|
>
|
||||||
{isNew ? (
|
{isNew ? (
|
||||||
<NewCrowdfundTitle>Create new playlist</NewCrowdfundTitle>
|
<NewCrowdfundTitle>Create new playlist</NewCrowdfundTitle>
|
||||||
|
|
||||||
) : (
|
) : (
|
||||||
<NewCrowdfundTitle>Update Playlist properties</NewCrowdfundTitle>
|
<NewCrowdfundTitle>Update Playlist properties</NewCrowdfundTitle>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<>
|
<>
|
||||||
@ -488,7 +484,7 @@ export const EditPlaylist = () => {
|
|||||||
value={selectedCategoryVideos?.id || ""}
|
value={selectedCategoryVideos?.id || ""}
|
||||||
onChange={handleOptionCategoryChangeVideos}
|
onChange={handleOptionCategoryChangeVideos}
|
||||||
>
|
>
|
||||||
{categories.map((option) => (
|
{firstCategories.map(option => (
|
||||||
<MenuItem key={option.id} value={option.id}>
|
<MenuItem key={option.id} value={option.id}>
|
||||||
{option.name}
|
{option.name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -496,22 +492,22 @@ export const EditPlaylist = () => {
|
|||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
{selectedCategoryVideos &&
|
{selectedCategoryVideos &&
|
||||||
subCategories[selectedCategoryVideos?.id] && (
|
secondCategories[selectedCategoryVideos?.id] && (
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
||||||
<InputLabel id="Category">Select a Sub-Category</InputLabel>
|
<InputLabel id="Category">Select a Sub-Category</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
labelId="Sub-Category"
|
labelId="Sub-Category"
|
||||||
input={<OutlinedInput label="Select a Sub-Category" />}
|
input={<OutlinedInput label="Select a Sub-Category" />}
|
||||||
value={selectedSubCategoryVideos?.id || ""}
|
value={selectedSubCategoryVideos?.id || ""}
|
||||||
onChange={(e) =>
|
onChange={e =>
|
||||||
handleOptionSubCategoryChangeVideos(
|
handleOptionSubCategoryChangeVideos(
|
||||||
e,
|
e,
|
||||||
subCategories[selectedCategoryVideos?.id]
|
secondCategories[selectedCategoryVideos?.id]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{subCategories[selectedCategoryVideos.id].map(
|
{secondCategories[selectedCategoryVideos.id].map(
|
||||||
(option) => (
|
option => (
|
||||||
<MenuItem key={option.id} value={option.id}>
|
<MenuItem key={option.id} value={option.id}>
|
||||||
{option.name}
|
{option.name}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -550,9 +546,12 @@ export const EditPlaylist = () => {
|
|||||||
label="Title of playlist"
|
label="Title of playlist"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => {
|
onChange={e => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
const formattedValue = value.replace(/[^a-zA-Z0-9\s-_!?]/g, "");
|
const formattedValue = value.replace(
|
||||||
|
/[^a-zA-Z0-9\s-_!?]/g,
|
||||||
|
""
|
||||||
|
);
|
||||||
setTitle(formattedValue);
|
setTitle(formattedValue);
|
||||||
}}
|
}}
|
||||||
inputProps={{ maxLength: 180 }}
|
inputProps={{ maxLength: 180 }}
|
||||||
@ -569,12 +568,19 @@ export const EditPlaylist = () => {
|
|||||||
maxRows={3}
|
maxRows={3}
|
||||||
required
|
required
|
||||||
/> */}
|
/> */}
|
||||||
<Typography sx={{
|
<Typography
|
||||||
fontSize: '18px'
|
sx={{
|
||||||
}}>Description of playlist</Typography>
|
fontSize: "18px",
|
||||||
<TextEditor inlineContent={description} setInlineContent={(value)=> {
|
}}
|
||||||
setDescription(value)
|
>
|
||||||
}} />
|
Description of playlist
|
||||||
|
</Typography>
|
||||||
|
<TextEditor
|
||||||
|
inlineContent={description}
|
||||||
|
setInlineContent={value => {
|
||||||
|
setDescription(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
||||||
<PlaylistListEdit
|
<PlaylistListEdit
|
||||||
|
@ -7,8 +7,8 @@ import {
|
|||||||
import { Box, Button, Input, Typography, useTheme } from "@mui/material";
|
import { Box, Button, Input, Typography, useTheme } from "@mui/material";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
||||||
import { removeVideo } from "../../state/features/videoSlice";
|
import { removeFile } from "../../state/features/fileSlice.ts";
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
@ -17,28 +17,29 @@ export const PlaylistListEdit = ({ playlistData, removeVideo, addVideo }) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
||||||
|
|
||||||
const [searchResults, setSearchResults] = useState([])
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
const [filterSearch, setFilterSearch] = useState("")
|
const [filterSearch, setFilterSearch] = useState("");
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
|
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 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, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const responseDataSearchVid = await response.json()
|
const responseDataSearchVid = await response.json();
|
||||||
setSearchResults(responseDataSearchVid)
|
setSearchResults(responseDataSearchVid);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
gap: '10px',
|
display: "flex",
|
||||||
width: '100%',
|
gap: "10px",
|
||||||
justifyContent: 'center'
|
width: "100%",
|
||||||
}}>
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -55,7 +56,7 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
sx={{
|
sx={{
|
||||||
marginTop: "25px",
|
marginTop: "25px",
|
||||||
height: "450px",
|
height: "450px",
|
||||||
overflow: 'auto'
|
overflow: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{playlistData?.videos?.map((vid, index) => {
|
{playlistData?.videos?.map((vid, index) => {
|
||||||
@ -82,7 +83,7 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "18px",
|
fontSize: "18px",
|
||||||
wordBreak: 'break-word'
|
wordBreak: "break-word",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{vid?.metadata?.title}
|
{vid?.metadata?.title}
|
||||||
@ -109,7 +110,6 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<CrowdfundSubTitleRow>
|
<CrowdfundSubTitleRow>
|
||||||
<CrowdfundSubTitle>Add videos to playlist</CrowdfundSubTitle>
|
<CrowdfundSubTitle>Add videos to playlist</CrowdfundSubTitle>
|
||||||
</CrowdfundSubTitleRow>
|
</CrowdfundSubTitleRow>
|
||||||
@ -117,16 +117,18 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
sx={{
|
sx={{
|
||||||
marginTop: "25px",
|
marginTop: "25px",
|
||||||
height: "450px",
|
height: "450px",
|
||||||
overflow: 'auto'
|
overflow: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{
|
|
||||||
display: 'flex',
|
|
||||||
gap: '10px'
|
|
||||||
}}>
|
|
||||||
<Input
|
<Input
|
||||||
id="standard-adornment-name"
|
id="standard-adornment-name"
|
||||||
onChange={(e) => {
|
onChange={e => {
|
||||||
setFilterSearch(e.target.value);
|
setFilterSearch(e.target.value);
|
||||||
}}
|
}}
|
||||||
value={filterSearch}
|
value={filterSearch}
|
||||||
@ -155,7 +157,6 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
search();
|
search();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
@ -186,7 +187,7 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "18px",
|
fontSize: "18px",
|
||||||
wordBreak: 'break-word'
|
wordBreak: "break-word",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{vid?.metadata?.title}
|
{vid?.metadata?.title}
|
||||||
@ -205,6 +206,5 @@ const [filterSearch, setFilterSearch] = useState("")
|
|||||||
</CardContentContainerComment>
|
</CardContentContainerComment>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,66 +1,32 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
AddCoverImageButton,
|
|
||||||
AddLogoIcon,
|
|
||||||
CoverImagePreview,
|
|
||||||
CrowdfundActionButton,
|
CrowdfundActionButton,
|
||||||
CrowdfundActionButtonRow,
|
CrowdfundActionButtonRow,
|
||||||
CustomInputField,
|
CustomInputField,
|
||||||
CustomSelect,
|
|
||||||
LogoPreviewRow,
|
|
||||||
ModalBody,
|
ModalBody,
|
||||||
NewCrowdfundTitle,
|
NewCrowdfundTitle,
|
||||||
StyledButton,
|
StyledButton,
|
||||||
TimesIcon,
|
|
||||||
} from "./Upload-styles";
|
} from "./Upload-styles";
|
||||||
import {
|
import { Box, Modal, Typography, useTheme } from "@mui/material";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
FormControl,
|
|
||||||
Input,
|
|
||||||
InputLabel,
|
|
||||||
MenuItem,
|
|
||||||
Modal,
|
|
||||||
OutlinedInput,
|
|
||||||
Select,
|
|
||||||
SelectChangeEvent,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import RemoveIcon from "@mui/icons-material/Remove";
|
import RemoveIcon from "@mui/icons-material/Remove";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import AddBoxIcon from "@mui/icons-material/AddBox";
|
import AddBoxIcon from "@mui/icons-material/AddBox";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
|
||||||
|
|
||||||
import { setNotification } from "../../state/features/notificationsSlice";
|
import { setNotification } from "../../state/features/notificationsSlice";
|
||||||
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
|
import { objectToBase64 } from "../../utils/toBase64";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import {
|
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
||||||
upsertVideosBeginning,
|
|
||||||
addToHashMap,
|
|
||||||
upsertVideos,
|
|
||||||
} from "../../state/features/videoSlice";
|
|
||||||
import ImageUploader from "../common/ImageUploader";
|
|
||||||
import {
|
|
||||||
QSHARE_PLAYLIST_BASE,
|
|
||||||
QSHARE_FILE_BASE,
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} from "../../constants/Identifiers.ts";
|
|
||||||
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
|
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
|
||||||
import {
|
|
||||||
CrowdfundSubTitle,
|
|
||||||
CrowdfundSubTitleRow,
|
|
||||||
} from "../EditPlaylist/Upload-styles.tsx";
|
|
||||||
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
|
|
||||||
import { TextEditor } from "../common/TextEditor/TextEditor";
|
import { TextEditor } from "../common/TextEditor/TextEditor";
|
||||||
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
import { extractTextFromHTML } from "../common/TextEditor/utils";
|
||||||
import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
|
import { allCategoryData } from "../../constants/Categories/1stCategories.ts";
|
||||||
import { titleFormatter } from "../../constants/Misc.ts";
|
import { titleFormatter } from "../../constants/Misc.ts";
|
||||||
|
import {
|
||||||
|
CategoryList,
|
||||||
|
CategoryListRef,
|
||||||
|
} from "../common/CategoryList/CategoryList.tsx";
|
||||||
|
|
||||||
const uid = new ShortUniqueId();
|
const uid = new ShortUniqueId();
|
||||||
const shortuid = new ShortUniqueId({ length: 5 });
|
const shortuid = new ShortUniqueId({ length: 5 });
|
||||||
@ -104,22 +70,15 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
const [selectedCategory, setSelectedCategory] = useState<any>(null);
|
const [selectedCategory, setSelectedCategory] = useState<any>(null);
|
||||||
const [selectedSubCategory, setSelectedSubCategory] = useState<any>(null);
|
const [selectedSubCategory, setSelectedSubCategory] = useState<any>(null);
|
||||||
|
|
||||||
const [selectedCategoryVideos, setSelectedCategoryVideos] =
|
|
||||||
useState<any>(null);
|
|
||||||
const [selectedSubCategoryVideos, setSelectedSubCategoryVideos] =
|
|
||||||
useState<any>(null);
|
|
||||||
const [selectedSubCategoryVideos2, setSelectedSubCategoryVideos2] =
|
|
||||||
useState<any>(null);
|
|
||||||
const [selectedSubCategoryVideos3, setSelectedSubCategoryVideos3] =
|
|
||||||
useState<any>(null);
|
|
||||||
|
|
||||||
const [playlistSetting, setPlaylistSetting] = useState<null | string>(null);
|
const [playlistSetting, setPlaylistSetting] = useState<null | string>(null);
|
||||||
const [publishes, setPublishes] = useState<any>(null);
|
const [publishes, setPublishes] = useState<any>(null);
|
||||||
|
const categoryListRef = useRef<CategoryListRef>(null);
|
||||||
|
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
maxFiles: 10,
|
maxFiles: 10,
|
||||||
maxSize: 419430400, // 400 MB in bytes
|
maxSize: 419430400, // 400 MB in bytes
|
||||||
onDrop: (acceptedFiles, rejectedFiles) => {
|
onDrop: (acceptedFiles, rejectedFiles) => {
|
||||||
const formatArray = acceptedFiles.map((item) => {
|
const formatArray = acceptedFiles.map(item => {
|
||||||
return {
|
return {
|
||||||
file: item,
|
file: item,
|
||||||
title: "",
|
title: "",
|
||||||
@ -128,11 +87,11 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
setFiles((prev) => [...prev, ...formatArray]);
|
setFiles(prev => [...prev, ...formatArray]);
|
||||||
|
|
||||||
let errorString = null;
|
let errorString = null;
|
||||||
rejectedFiles.forEach(({ file, errors }) => {
|
rejectedFiles.forEach(({ file, errors }) => {
|
||||||
errors.forEach((error) => {
|
errors.forEach(error => {
|
||||||
if (error.code === "file-too-large") {
|
if (error.code === "file-too-large") {
|
||||||
errorString = "File must be under 400mb";
|
errorString = "File must be under 400mb";
|
||||||
}
|
}
|
||||||
@ -159,16 +118,15 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function publishQDNResource() {
|
async function publishQDNResource() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!categoryListRef.current) throw new Error("No CategoryListRef found");
|
||||||
if (!userAddress) throw new Error("Unable to locate user address");
|
if (!userAddress) throw new Error("Unable to locate user address");
|
||||||
|
|
||||||
if (!title) throw new Error("Please enter a title");
|
if (!title) throw new Error("Please enter a title");
|
||||||
if (!description) throw new Error("Please enter a description");
|
if (!description) throw new Error("Please enter a description");
|
||||||
if (!selectedCategoryVideos) throw new Error("Please select a category");
|
if (!categoryListRef.current?.getSelectedCategories()[0])
|
||||||
|
throw new Error("Please select a category");
|
||||||
if (files.length === 0) throw new Error("Add at least one file");
|
if (files.length === 0) throw new Error("Add at least one file");
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
let name = "";
|
let name = "";
|
||||||
@ -194,15 +152,11 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileReferences = []
|
let fileReferences = [];
|
||||||
|
|
||||||
let listOfPublishes = [];
|
let listOfPublishes = [];
|
||||||
|
|
||||||
const fullDescription = extractTextFromHTML(description);
|
const fullDescription = extractTextFromHTML(description);
|
||||||
const category = selectedCategoryVideos.id;
|
|
||||||
const subcategory = selectedSubCategoryVideos?.id || "";
|
|
||||||
const subcategory2 = selectedSubCategoryVideos2?.id || "";
|
|
||||||
const subcategory3 = selectedSubCategoryVideos3?.id || "";
|
|
||||||
|
|
||||||
const sanitizeTitle = title
|
const sanitizeTitle = title
|
||||||
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
||||||
@ -212,20 +166,17 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
||||||
for (const publish of files) {
|
for (const publish of files) {
|
||||||
|
|
||||||
const file = publish.file;
|
const file = publish.file;
|
||||||
const id = uid();
|
const id = uid();
|
||||||
|
|
||||||
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
|
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let fileExtension = "";
|
let fileExtension = "";
|
||||||
const fileExtensionSplit = file?.name?.split(".");
|
const fileExtensionSplit = file?.name?.split(".");
|
||||||
if (fileExtensionSplit?.length > 1) {
|
if (fileExtensionSplit?.length > 1) {
|
||||||
fileExtension = fileExtensionSplit?.pop() || "";
|
fileExtension = fileExtensionSplit?.pop() || "";
|
||||||
}
|
}
|
||||||
let firstPartName = fileExtensionSplit[0]
|
let firstPartName = fileExtensionSplit[0];
|
||||||
|
|
||||||
let filename = firstPartName.slice(0, 15);
|
let filename = firstPartName.slice(0, 15);
|
||||||
|
|
||||||
@ -241,18 +192,15 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (fileExtension) {
|
if (fileExtension) {
|
||||||
filename = `${alphanumericString.trim()}.${fileExtension}`
|
filename = `${alphanumericString.trim()}.${fileExtension}`;
|
||||||
} else {
|
} else {
|
||||||
filename = alphanumericString
|
filename = alphanumericString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let metadescription =
|
let metadescription =
|
||||||
`**cat:${category};sub:${subcategory};sub2:${subcategory2};sub3:${subcategory3}**` +
|
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
|
||||||
fullDescription.slice(0, 150);
|
fullDescription.slice(0, 150);
|
||||||
|
|
||||||
|
|
||||||
const requestBodyVideo: any = {
|
const requestBodyVideo: any = {
|
||||||
action: "PUBLISH_QDN_RESOURCE",
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
name: name,
|
name: name,
|
||||||
@ -269,10 +217,10 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
filename: file.name,
|
filename: file.name,
|
||||||
identifier,
|
identifier,
|
||||||
name,
|
name,
|
||||||
service: 'FILE',
|
service: "FILE",
|
||||||
mimetype: file.type,
|
mimetype: file.type,
|
||||||
size: file.size
|
size: file.size,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const idMeta = uid();
|
const idMeta = uid();
|
||||||
@ -283,15 +231,12 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
fullDescription,
|
fullDescription,
|
||||||
htmlDescription: description,
|
htmlDescription: description,
|
||||||
commentsId: `${QSHARE_FILE_BASE}_cm_${idMeta}`,
|
commentsId: `${QSHARE_FILE_BASE}_cm_${idMeta}`,
|
||||||
category,
|
...categoryListRef.current?.categoriesToObject(),
|
||||||
subcategory,
|
files: fileReferences,
|
||||||
subcategory2,
|
|
||||||
subcategory3,
|
|
||||||
files: fileReferences
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let metadescription =
|
let metadescription =
|
||||||
`**cat:${category};sub:${subcategory};sub2:${subcategory2}**` +
|
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
|
||||||
fullDescription.slice(0, 150);
|
fullDescription.slice(0, 150);
|
||||||
|
|
||||||
const crowdfundObjectToBase64 = await objectToBase64(fileObject);
|
const crowdfundObjectToBase64 = await objectToBase64(fileObject);
|
||||||
@ -315,7 +260,6 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
};
|
};
|
||||||
setPublishes(multiplePublish);
|
setPublishes(multiplePublish);
|
||||||
setIsOpenMultiplePublish(true);
|
setIsOpenMultiplePublish(true);
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
let notificationObj: any = null;
|
let notificationObj: any = null;
|
||||||
if (typeof error === "string") {
|
if (typeof error === "string") {
|
||||||
@ -339,50 +283,6 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleOptionCategoryChangeVideos = (
|
|
||||||
event: SelectChangeEvent<string>
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = categories.find((option) => option.id === +optionId);
|
|
||||||
setSelectedCategoryVideos(selectedOption || null);
|
|
||||||
};
|
|
||||||
const handleOptionSubCategoryChangeVideos = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos(selectedOption || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOptionSubCategoryChangeVideos2 = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos2(selectedOption || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOptionSubCategoryChangeVideos3 = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos3(selectedOption || null);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{username && (
|
{username && (
|
||||||
@ -414,9 +314,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<NewCrowdfundTitle>Share</NewCrowdfundTitle>
|
<NewCrowdfundTitle>Share</NewCrowdfundTitle>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{step === "videos" && (
|
{step === "videos" && (
|
||||||
@ -449,7 +347,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
<Typography>{file?.file?.name}</Typography>
|
<Typography>{file?.file?.name}</Typography>
|
||||||
<RemoveIcon
|
<RemoveIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFiles((prev) => {
|
setFiles(prev => {
|
||||||
const copyPrev = [...prev];
|
const copyPrev = [...prev];
|
||||||
copyPrev.splice(index, 1);
|
copyPrev.splice(index, 1);
|
||||||
return copyPrev;
|
return copyPrev;
|
||||||
@ -463,6 +361,9 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{files?.length > 0 && (
|
||||||
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -470,142 +371,18 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{files?.length > 0 && (
|
<CategoryList
|
||||||
<>
|
categoryData={allCategoryData}
|
||||||
<Box sx={{
|
ref={categoryListRef}
|
||||||
display: 'flex',
|
columns={3}
|
||||||
flexDirection: 'column',
|
/>
|
||||||
gap: '20px',
|
|
||||||
width: '50%'
|
|
||||||
}}>
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">Select a Category</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Category"
|
|
||||||
input={<OutlinedInput label="Select a Category" />}
|
|
||||||
value={selectedCategoryVideos?.id || ""}
|
|
||||||
onChange={handleOptionCategoryChangeVideos}
|
|
||||||
>
|
|
||||||
{categories.map((option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</Box>
|
</Box>
|
||||||
{selectedCategoryVideos && (
|
|
||||||
<>
|
|
||||||
<Box sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '20px',
|
|
||||||
width: '50%'
|
|
||||||
}}>
|
|
||||||
|
|
||||||
|
|
||||||
{selectedCategoryVideos &&
|
|
||||||
subCategories[selectedCategoryVideos?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">
|
|
||||||
Select a Sub-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={
|
|
||||||
<OutlinedInput label="Select a Sub-Category" />
|
|
||||||
}
|
|
||||||
value={selectedSubCategoryVideos?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos(
|
|
||||||
e,
|
|
||||||
subCategories[selectedCategoryVideos?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{subCategories[selectedCategoryVideos.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
{selectedSubCategoryVideos &&
|
|
||||||
subCategories2[selectedSubCategoryVideos?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">
|
|
||||||
Select a Sub-sub-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={
|
|
||||||
<OutlinedInput label="Select a Sub-sub-Category" />
|
|
||||||
}
|
|
||||||
value={selectedSubCategoryVideos2?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos2(
|
|
||||||
e,
|
|
||||||
subCategories2[selectedSubCategoryVideos?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{subCategories2[selectedSubCategoryVideos.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
{selectedSubCategoryVideos2 &&
|
|
||||||
subCategories3[selectedSubCategoryVideos2?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel id="Category">
|
|
||||||
Select a Sub-3x-subCategory
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={
|
|
||||||
<OutlinedInput label="Select a Sub-3x-Category" />
|
|
||||||
}
|
|
||||||
value={selectedSubCategoryVideos3?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos3(
|
|
||||||
e,
|
|
||||||
subCategories3[selectedSubCategoryVideos2?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{subCategories3[selectedSubCategoryVideos2.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
{files?.length > 0 && (
|
|
||||||
<>
|
|
||||||
<CustomInputField
|
<CustomInputField
|
||||||
name="title"
|
name="title"
|
||||||
label="Title of share"
|
label="Title of share"
|
||||||
variant="filled"
|
variant="filled"
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => {
|
onChange={e => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
const formattedValue = value.replace(titleFormatter, "");
|
const formattedValue = value.replace(titleFormatter, "");
|
||||||
setTitle(formattedValue);
|
setTitle(formattedValue);
|
||||||
@ -622,13 +399,12 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<TextEditor
|
<TextEditor
|
||||||
inlineContent={description}
|
inlineContent={description}
|
||||||
setInlineContent={(value) => {
|
setInlineContent={value => {
|
||||||
setDescription(value);
|
setDescription(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<CrowdfundActionButtonRow>
|
<CrowdfundActionButtonRow>
|
||||||
@ -664,16 +440,16 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
{isOpenMultiplePublish && (
|
{isOpenMultiplePublish && (
|
||||||
<MultiplePublish
|
<MultiplePublish
|
||||||
isOpen={isOpenMultiplePublish}
|
isOpen={isOpenMultiplePublish}
|
||||||
onError={(messageNotification)=> {
|
onError={messageNotification => {
|
||||||
setIsOpenMultiplePublish(false);
|
setIsOpenMultiplePublish(false);
|
||||||
setPublishes(null)
|
setPublishes(null);
|
||||||
if (messageNotification) {
|
if (messageNotification) {
|
||||||
dispatch(
|
dispatch(
|
||||||
setNotification({
|
setNotification({
|
||||||
msg: messageNotification,
|
msg: messageNotification,
|
||||||
alertType: 'error'
|
alertType: "error",
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
@ -686,9 +462,8 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
setPlaylistDescription("");
|
setPlaylistDescription("");
|
||||||
setSelectedCategory(null);
|
setSelectedCategory(null);
|
||||||
setSelectedSubCategory(null);
|
setSelectedSubCategory(null);
|
||||||
setSelectedCategoryVideos(null);
|
|
||||||
setSelectedSubCategoryVideos(null);
|
|
||||||
setPlaylistSetting(null);
|
setPlaylistSetting(null);
|
||||||
|
categoryListRef.current?.clearCategories();
|
||||||
dispatch(
|
dispatch(
|
||||||
setNotification({
|
setNotification({
|
||||||
msg: "Files published",
|
msg: "Files published",
|
||||||
|
61
src/components/StatsData.tsx
Normal file
61
src/components/StatsData.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { styled } from "@mui/system";
|
||||||
|
import { Grid } from "@mui/material";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../state/store.ts";
|
||||||
|
import { useFetchFiles } from "../hooks/useFetchFiles.tsx";
|
||||||
|
|
||||||
|
export const StatsData = () => {
|
||||||
|
const StatsCol = styled(Grid)(({ theme }) => ({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "100%",
|
||||||
|
padding: "20px 0px",
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const {
|
||||||
|
getFiles,
|
||||||
|
checkAndUpdateFile,
|
||||||
|
getFile,
|
||||||
|
hashMapFiles,
|
||||||
|
getNewFiles,
|
||||||
|
checkNewFiles,
|
||||||
|
getFilesFiltered,
|
||||||
|
getFilesCount,
|
||||||
|
} = useFetchFiles();
|
||||||
|
|
||||||
|
const totalVideosPublished = useSelector(
|
||||||
|
(state: RootState) => state.global.totalFilesPublished
|
||||||
|
);
|
||||||
|
const totalNamesPublished = useSelector(
|
||||||
|
(state: RootState) => state.global.totalNamesPublished
|
||||||
|
);
|
||||||
|
const videosPerNamePublished = useSelector(
|
||||||
|
(state: RootState) => state.global.filesPerNamePublished
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getFilesCount();
|
||||||
|
}, [getFilesCount]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StatsCol>
|
||||||
|
<div>
|
||||||
|
Shares:{" "}
|
||||||
|
<span style={{ fontWeight: "bold" }}>{totalVideosPublished}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Publishers:{" "}
|
||||||
|
<span style={{ fontWeight: "bold" }}>{totalNamesPublished}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Average:{" "}
|
||||||
|
<span style={{ fontWeight: "bold" }}>
|
||||||
|
{videosPerNamePublished > 0 &&
|
||||||
|
Number(videosPerNamePublished).toFixed(0)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</StatsCol>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,9 @@
|
|||||||
|
import { styled } from "@mui/system";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
|
export const CategoryContainer = styled(Box)(({ theme }) => ({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: "5px",
|
||||||
|
}));
|
284
src/components/common/CategoryList/CategoryList.tsx
Normal file
284
src/components/common/CategoryList/CategoryList.tsx
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
MenuItem,
|
||||||
|
OutlinedInput,
|
||||||
|
Select,
|
||||||
|
SelectChangeEvent,
|
||||||
|
SxProps,
|
||||||
|
Theme,
|
||||||
|
} from "@mui/material";
|
||||||
|
|
||||||
|
import React, { forwardRef, useImperativeHandle, useState } from "react";
|
||||||
|
import { CategoryContainer } from "./CategoryList-styles.tsx";
|
||||||
|
import { allCategoryData } from "../../../constants/Categories/1stCategories.ts";
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Categories {
|
||||||
|
[key: number]: Category[];
|
||||||
|
}
|
||||||
|
export interface CategoryData {
|
||||||
|
category: Category[];
|
||||||
|
subCategories: Categories[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListDirection = "column" | "row";
|
||||||
|
interface CategoryListProps {
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
|
categoryData: CategoryData;
|
||||||
|
initialCategories?: string[];
|
||||||
|
columns?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CategoryListRef = {
|
||||||
|
getSelectedCategories: () => string[];
|
||||||
|
setSelectedCategories: (arr: string[]) => void;
|
||||||
|
clearCategories: () => void;
|
||||||
|
getCategoriesFetchString: () => string;
|
||||||
|
categoriesToObject: () => object;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CategoryList = React.forwardRef<
|
||||||
|
CategoryListRef,
|
||||||
|
CategoryListProps
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{ sx, categoryData, initialCategories, columns = 1 }: CategoryListProps,
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const categoriesLength = categoryData.subCategories.length + 1;
|
||||||
|
|
||||||
|
let emptyCategories: string[] = [];
|
||||||
|
for (let i = 0; i < categoriesLength; i++) emptyCategories.push("");
|
||||||
|
|
||||||
|
const [selectedCategories, setSelectedCategories] = useState<string[]>(
|
||||||
|
initialCategories || emptyCategories
|
||||||
|
);
|
||||||
|
|
||||||
|
const categoriesToObject = () => {
|
||||||
|
let categoriesObject = {};
|
||||||
|
selectedCategories.map((category, index) => {
|
||||||
|
if (index === 0) categoriesObject["category"] = category;
|
||||||
|
else if (index === 1) categoriesObject["subcategory"] = category;
|
||||||
|
else categoriesObject[`subcategory${index}`] = category;
|
||||||
|
});
|
||||||
|
console.log("categoriesObject is: ", categoriesObject);
|
||||||
|
return categoriesObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCategories = () => {
|
||||||
|
setSelectedCategories(emptyCategories);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
getSelectedCategories: () => {
|
||||||
|
return selectedCategories;
|
||||||
|
},
|
||||||
|
setSelectedCategories: categories => {
|
||||||
|
console.log("setSelectedCategories: ", categories);
|
||||||
|
//categories.map((category, index) => selectCategory(category, index));
|
||||||
|
setSelectedCategories(categories);
|
||||||
|
},
|
||||||
|
clearCategories,
|
||||||
|
getCategoriesFetchString: () =>
|
||||||
|
getCategoriesFetchString(selectedCategories),
|
||||||
|
categoriesToObject,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const selectCategory = (optionId: string, index: number) => {
|
||||||
|
const isMainCategory = index === 0;
|
||||||
|
const subCategoryIndex = index - 1;
|
||||||
|
|
||||||
|
const selectedOption = isMainCategory
|
||||||
|
? categoryData.category.find(option => option.id === +optionId)
|
||||||
|
: categoryData.subCategories[subCategoryIndex][
|
||||||
|
selectedCategories[subCategoryIndex]
|
||||||
|
].find(option => option.id === +optionId);
|
||||||
|
|
||||||
|
const newSelectedCategories: string[] = selectedCategories.map(
|
||||||
|
(category, categoryIndex) => {
|
||||||
|
if (index > categoryIndex) return category;
|
||||||
|
else if (index === categoryIndex) return selectedOption.id.toString();
|
||||||
|
else return "";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setSelectedCategories(newSelectedCategories);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectCategoryEvent = (event: SelectChangeEvent, index: number) => {
|
||||||
|
const optionId = event.target.value;
|
||||||
|
selectCategory(optionId, index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const categorySelectSX = {
|
||||||
|
// Target the input field
|
||||||
|
".MuiSelect-select": {
|
||||||
|
fontSize: "16px", // Change font size for the selected value
|
||||||
|
padding: "10px 5px 15px 15px;",
|
||||||
|
},
|
||||||
|
// Target the dropdown icon
|
||||||
|
".MuiSelect-icon": {
|
||||||
|
fontSize: "20px", // Adjust if needed
|
||||||
|
},
|
||||||
|
// Target the dropdown menu
|
||||||
|
"& .MuiMenu-paper": {
|
||||||
|
".MuiMenuItem-root": {
|
||||||
|
fontSize: "14px", // Change font size for the menu items
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fillMenu = (category: Categories, index: number) => {
|
||||||
|
const subCategoryIndex = selectedCategories[index];
|
||||||
|
console.log("selected categories: ", selectedCategories);
|
||||||
|
console.log("index is: ", index);
|
||||||
|
console.log("subCategoryIndex is: ", subCategoryIndex);
|
||||||
|
console.log("category is: ", category);
|
||||||
|
console.log(
|
||||||
|
"subCategoryIndex within category: ",
|
||||||
|
selectedCategories[subCategoryIndex]
|
||||||
|
);
|
||||||
|
console.log("categoryData: ", categoryData);
|
||||||
|
|
||||||
|
const menuToFill = category[subCategoryIndex];
|
||||||
|
if (menuToFill)
|
||||||
|
return menuToFill.map(option => (
|
||||||
|
<MenuItem key={option.id} value={option.id}>
|
||||||
|
{option.name}
|
||||||
|
</MenuItem>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasSubCategory = (category: Categories, index: number) => {
|
||||||
|
const subCategoryIndex = selectedCategories[index];
|
||||||
|
const subCategory = category[subCategoryIndex];
|
||||||
|
return subCategory && subCategoryIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CategoryContainer sx={{ width: "100%", ...sx }}>
|
||||||
|
<FormControl sx={{ width: "100%" }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(" + columns + ", 1fr)",
|
||||||
|
width: "100%",
|
||||||
|
gap: "20px",
|
||||||
|
alignItems: "center",
|
||||||
|
marginTop: "30px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormControl fullWidth sx={{ marginBottom: 1 }}>
|
||||||
|
<InputLabel
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
}}
|
||||||
|
id="Category-1"
|
||||||
|
>
|
||||||
|
Category
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId="Category 1"
|
||||||
|
input={<OutlinedInput label="Category 1" />}
|
||||||
|
value={selectedCategories[0] || ""}
|
||||||
|
onChange={e => {
|
||||||
|
selectCategoryEvent(e, 0);
|
||||||
|
}}
|
||||||
|
sx={categorySelectSX}
|
||||||
|
>
|
||||||
|
{categoryData.category.map(option => (
|
||||||
|
<MenuItem key={option.id} value={option.id}>
|
||||||
|
{option.name}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
{categoryData.subCategories.map(
|
||||||
|
(category, index) =>
|
||||||
|
hasSubCategory(category, index) && (
|
||||||
|
<FormControl
|
||||||
|
fullWidth
|
||||||
|
sx={{
|
||||||
|
marginBottom: 1,
|
||||||
|
}}
|
||||||
|
key={selectedCategories[index] + index}
|
||||||
|
>
|
||||||
|
<InputLabel
|
||||||
|
sx={{
|
||||||
|
fontSize: "16px",
|
||||||
|
}}
|
||||||
|
id={`Category-${index + 2}`}
|
||||||
|
>
|
||||||
|
{`Category-${index + 2}`}
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
labelId={`Category ${index + 2}`}
|
||||||
|
input={<OutlinedInput label={`Category ${index + 2}`} />}
|
||||||
|
value={selectedCategories[index + 1] || ""}
|
||||||
|
onChange={e => {
|
||||||
|
selectCategoryEvent(e, index + 1);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
// Target the input field
|
||||||
|
".MuiSelect-select": {
|
||||||
|
fontSize: "16px", // Change font size for the selected value
|
||||||
|
padding: "10px 5px 15px 15px;",
|
||||||
|
},
|
||||||
|
// Target the dropdown icon
|
||||||
|
".MuiSelect-icon": {
|
||||||
|
fontSize: "20px", // Adjust if needed
|
||||||
|
},
|
||||||
|
// Target the dropdown menu
|
||||||
|
"& .MuiMenu-paper": {
|
||||||
|
".MuiMenuItem-root": {
|
||||||
|
fontSize: "14px", // Change font size for the menu items
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fillMenu(category, index)}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</FormControl>
|
||||||
|
</CategoryContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getCategoriesFetchString = (categories: string[]) => {
|
||||||
|
let fetchString = "";
|
||||||
|
categories.map((category, index) => {
|
||||||
|
if (category) {
|
||||||
|
if (index === 0) fetchString += `cat:${category}`;
|
||||||
|
else if (index === 1) fetchString += `;sub:${category}`;
|
||||||
|
else fetchString += `;sub${index}:${category}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log("categoriesAsDescription: ", fetchString);
|
||||||
|
return fetchString;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCategoriesFromObject = (editFileProperties: any) => {
|
||||||
|
const categoryList: string[] = [];
|
||||||
|
const categoryCount = allCategoryData.subCategories.length + 1;
|
||||||
|
|
||||||
|
for (let i = 0; i < categoryCount; i++) {
|
||||||
|
if (i === 0 && editFileProperties.category)
|
||||||
|
categoryList.push(editFileProperties.category);
|
||||||
|
else if (i === 1 && editFileProperties.subcategory)
|
||||||
|
categoryList.push(editFileProperties.subcategory);
|
||||||
|
else categoryList.push(editFileProperties[`subcategory${i}`] || "");
|
||||||
|
}
|
||||||
|
return categoryList;
|
||||||
|
};
|
@ -20,58 +20,58 @@ interface Publish {
|
|||||||
interface MultiplePublishProps {
|
interface MultiplePublishProps {
|
||||||
publishes: Publish;
|
publishes: Publish;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onSubmit: ()=> void
|
onSubmit: () => void;
|
||||||
onError: (message?: string)=> void
|
onError: (message?: string) => void;
|
||||||
}
|
}
|
||||||
export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: MultiplePublishProps) => {
|
export const MultiplePublish = ({
|
||||||
|
publishes,
|
||||||
|
isOpen,
|
||||||
|
onSubmit,
|
||||||
|
onError,
|
||||||
|
}: MultiplePublishProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const listOfSuccessfulPublishesRef = useRef([])
|
const listOfSuccessfulPublishesRef = useRef([]);
|
||||||
const [listOfSuccessfulPublishes, setListOfSuccessfulPublishes] = useState<
|
const [listOfSuccessfulPublishes, setListOfSuccessfulPublishes] = useState<
|
||||||
any[]
|
any[]
|
||||||
>([]);
|
>([]);
|
||||||
const [listOfUnsuccessfulPublishes, setListOfUnSuccessfulPublishes] = useState<
|
const [listOfUnsuccessfulPublishes, setListOfUnSuccessfulPublishes] =
|
||||||
any[]
|
useState<any[]>([]);
|
||||||
>([]);
|
|
||||||
const [currentlyInPublish, setCurrentlyInPublish] = useState(null);
|
const [currentlyInPublish, setCurrentlyInPublish] = useState(null);
|
||||||
const hasStarted = useRef(false);
|
const hasStarted = useRef(false);
|
||||||
const publish = useCallback(async (pub: any) => {
|
const publish = useCallback(async (pub: any) => {
|
||||||
const lengthOfResources = pub?.resources?.length
|
const lengthOfResources = pub?.resources?.length;
|
||||||
const lengthOfTimeout = lengthOfResources * 30000
|
const lengthOfTimeout = lengthOfResources * 30000;
|
||||||
return await qortalRequestWithTimeout(pub, lengthOfTimeout);
|
return await qortalRequestWithTimeout(pub, lengthOfTimeout);
|
||||||
}, []);
|
}, []);
|
||||||
const [isPublishing, setIsPublishing] = useState(true)
|
const [isPublishing, setIsPublishing] = useState(true);
|
||||||
|
|
||||||
const handlePublish = useCallback(
|
const handlePublish = useCallback(
|
||||||
async (pub: any) => {
|
async (pub: any) => {
|
||||||
try {
|
try {
|
||||||
setCurrentlyInPublish(pub?.identifier);
|
setCurrentlyInPublish(pub?.identifier);
|
||||||
setIsPublishing(true)
|
setIsPublishing(true);
|
||||||
const res = await publish(pub);
|
const res = await publish(pub);
|
||||||
|
|
||||||
onSubmit()
|
onSubmit();
|
||||||
setListOfUnSuccessfulPublishes([])
|
setListOfUnSuccessfulPublishes([]);
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const unsuccessfulPublishes = error?.error?.unsuccessfulPublishes || []
|
const unsuccessfulPublishes = error?.error?.unsuccessfulPublishes || [];
|
||||||
if(error?.error === 'User declined request'){
|
if (error?.error === "User declined request") {
|
||||||
onError()
|
onError();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(error?.error === 'The request timed out'){
|
if (error?.error === "The request timed out") {
|
||||||
onError("The request timed out")
|
onError("The request timed out");
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (unsuccessfulPublishes?.length > 0) {
|
if (unsuccessfulPublishes?.length > 0) {
|
||||||
setListOfUnSuccessfulPublishes(unsuccessfulPublishes)
|
setListOfUnSuccessfulPublishes(unsuccessfulPublishes);
|
||||||
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
setIsPublishing(false);
|
||||||
setIsPublishing(false)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[publish]
|
[publish]
|
||||||
@ -79,18 +79,20 @@ export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: Multip
|
|||||||
|
|
||||||
const retry = () => {
|
const retry = () => {
|
||||||
let newlistOfMultiplePublishes: any[] = [];
|
let newlistOfMultiplePublishes: any[] = [];
|
||||||
listOfUnsuccessfulPublishes?.forEach((item)=> {
|
listOfUnsuccessfulPublishes?.forEach(item => {
|
||||||
const findPub = publishes?.resources.find((res: any)=> res?.identifier === item.identifier)
|
const findPub = publishes?.resources.find(
|
||||||
|
(res: any) => res?.identifier === item.identifier
|
||||||
|
);
|
||||||
if (findPub) {
|
if (findPub) {
|
||||||
newlistOfMultiplePublishes.push(findPub)
|
newlistOfMultiplePublishes.push(findPub);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
const multiplePublish = {
|
const multiplePublish = {
|
||||||
...publishes,
|
...publishes,
|
||||||
resources: newlistOfMultiplePublishes
|
resources: newlistOfMultiplePublishes,
|
||||||
|
};
|
||||||
|
handlePublish(multiplePublish);
|
||||||
};
|
};
|
||||||
handlePublish(multiplePublish)
|
|
||||||
}
|
|
||||||
|
|
||||||
const startPublish = useCallback(
|
const startPublish = useCallback(
|
||||||
async (pubs: any) => {
|
async (pubs: any) => {
|
||||||
@ -106,7 +108,6 @@ export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: Multip
|
|||||||
}
|
}
|
||||||
}, [startPublish, publishes, listOfSuccessfulPublishes]);
|
}, [startPublish, publishes, listOfSuccessfulPublishes]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
@ -119,7 +120,9 @@ export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: Multip
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{publishes?.resources?.map((publish: any) => {
|
{publishes?.resources?.map((publish: any) => {
|
||||||
const unpublished = listOfUnsuccessfulPublishes.map(item => item?.identifier)
|
const unpublished = listOfUnsuccessfulPublishes.map(
|
||||||
|
item => item?.identifier
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -128,6 +131,7 @@ export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: Multip
|
|||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
|
key={publish?.identifier}
|
||||||
>
|
>
|
||||||
<Typography>{publish?.identifier}</Typography>
|
<Typography>{publish?.identifier}</Typography>
|
||||||
{!isPublishing && hasStarted.current ? (
|
{!isPublishing && hasStarted.current ? (
|
||||||
@ -146,29 +150,39 @@ export const MultiplePublish = ({ publishes, isOpen, onSubmit, onError}: Multip
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
): <CircularProgress size={16} color="secondary"/>}
|
) : (
|
||||||
|
<CircularProgress size={16} color="secondary" />
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{!isPublishing && listOfUnsuccessfulPublishes.length > 0 && (
|
{!isPublishing && listOfUnsuccessfulPublishes.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Typography sx={{
|
<Typography
|
||||||
marginTop: '20px',
|
sx={{
|
||||||
fontSize: '16px'
|
marginTop: "20px",
|
||||||
}}>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>
|
fontSize: "16px",
|
||||||
<Button variant="contained" onClick={()=> {
|
}}
|
||||||
retry()
|
>
|
||||||
}}>Try again</Button>
|
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>
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const ModalBody = styled(Box)(({ theme }) => ({
|
export const ModalBody = styled(Box)(({ theme }) => ({
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import React, { useState, useRef } from "react";
|
import React, { useState, useRef } from "react";
|
||||||
import { Box, Button, Input, Popover, Typography, useTheme } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Popover,
|
||||||
|
Typography,
|
||||||
|
useTheme,
|
||||||
|
} from "@mui/material";
|
||||||
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
||||||
import { BlockedNamesModal } from "../../common/BlockedNamesModal/BlockedNamesModal";
|
import { BlockedNamesModal } from "../../common/BlockedNamesModal/BlockedNamesModal";
|
||||||
import AddBoxIcon from "@mui/icons-material/AddBox";
|
import AddBoxIcon from "@mui/icons-material/AddBox";
|
||||||
@ -28,11 +35,11 @@ import { DownloadTaskManager } from "../../common/DownloadTaskManager";
|
|||||||
import QShareLogo from "../../../assets/img/q-share-icon.webp";
|
import QShareLogo from "../../../assets/img/q-share-icon.webp";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
addFilteredVideos,
|
addFilteredFiles,
|
||||||
setEditPlaylist,
|
setEditPlaylist,
|
||||||
setFilterValue,
|
setFilterValue,
|
||||||
setIsFiltering,
|
setIsFiltering,
|
||||||
} from "../../../state/features/videoSlice";
|
} from "../../../state/features/fileSlice.ts";
|
||||||
import { RootState } from "../../../state/store";
|
import { RootState } from "../../../state/store";
|
||||||
import { useWindowSize } from "../../../hooks/useWindowSize";
|
import { useWindowSize } from "../../../hooks/useWindowSize";
|
||||||
import { PublishFile } from "../../PublishFile/PublishFile.tsx";
|
import { PublishFile } from "../../PublishFile/PublishFile.tsx";
|
||||||
@ -67,9 +74,7 @@ const NavBar: React.FC<Props> = ({
|
|||||||
|
|
||||||
const [anchorElNotification, setAnchorElNotification] =
|
const [anchorElNotification, setAnchorElNotification] =
|
||||||
React.useState<HTMLButtonElement | null>(null);
|
React.useState<HTMLButtonElement | null>(null);
|
||||||
const filterValue = useSelector(
|
const filterValue = useSelector((state: RootState) => state.file.filterValue);
|
||||||
(state: RootState) => state.video.filterValue
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
const target = event.currentTarget as unknown as HTMLButtonElement | null;
|
const target = event.currentTarget as unknown as HTMLButtonElement | null;
|
||||||
@ -100,18 +105,20 @@ const NavBar: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<CustomAppBar position="sticky" elevation={2}>
|
<CustomAppBar position="sticky" elevation={2}>
|
||||||
<ThemeSelectRow>
|
<ThemeSelectRow>
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
height: '100%',
|
display: "flex",
|
||||||
alignItems: 'center',
|
height: "100%",
|
||||||
gap: '20px'
|
alignItems: "center",
|
||||||
}}>
|
gap: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<LogoContainer
|
<LogoContainer
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate("/");
|
navigate("/");
|
||||||
dispatch(setIsFiltering(false));
|
dispatch(setIsFiltering(false));
|
||||||
dispatch(setFilterValue(""));
|
dispatch(setFilterValue(""));
|
||||||
dispatch(addFilteredVideos([]));
|
dispatch(addFilteredFiles([]));
|
||||||
searchValRef.current = "";
|
searchValRef.current = "";
|
||||||
if (!inputRef.current) return;
|
if (!inputRef.current) return;
|
||||||
inputRef.current.value = "";
|
inputRef.current.value = "";
|
||||||
@ -126,10 +133,14 @@ const NavBar: React.FC<Props> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</LogoContainer>
|
</LogoContainer>
|
||||||
<Typography sx={{
|
<Typography
|
||||||
fontSize: '16px',
|
sx={{
|
||||||
whiteSpace: 'nowrap'
|
fontSize: "16px",
|
||||||
}}>Sharing is caring</Typography>
|
whiteSpace: "nowrap",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sharing is caring
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</ThemeSelectRow>
|
</ThemeSelectRow>
|
||||||
<Box
|
<Box
|
||||||
@ -289,15 +300,15 @@ const NavBar: React.FC<Props> = ({
|
|||||||
<Input
|
<Input
|
||||||
id="standard-adornment-name"
|
id="standard-adornment-name"
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
onChange={(e) => {
|
onChange={e => {
|
||||||
searchValRef.current = e.target.value;
|
searchValRef.current = e.target.value;
|
||||||
}}
|
}}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={event => {
|
||||||
if (event.key === "Enter" || event.keyCode === 13) {
|
if (event.key === "Enter" || event.keyCode === 13) {
|
||||||
if (!searchValRef.current) {
|
if (!searchValRef.current) {
|
||||||
dispatch(setIsFiltering(false));
|
dispatch(setIsFiltering(false));
|
||||||
dispatch(setFilterValue(""));
|
dispatch(setFilterValue(""));
|
||||||
dispatch(addFilteredVideos([]));
|
dispatch(addFilteredFiles([]));
|
||||||
searchValRef.current = "";
|
searchValRef.current = "";
|
||||||
if (!inputRef.current) return;
|
if (!inputRef.current) return;
|
||||||
inputRef.current.value = "";
|
inputRef.current.value = "";
|
||||||
@ -305,7 +316,7 @@ const NavBar: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
navigate("/");
|
navigate("/");
|
||||||
dispatch(setIsFiltering(true));
|
dispatch(setIsFiltering(true));
|
||||||
dispatch(addFilteredVideos([]));
|
dispatch(addFilteredFiles([]));
|
||||||
dispatch(setFilterValue(searchValRef.current));
|
dispatch(setFilterValue(searchValRef.current));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -338,7 +349,7 @@ const NavBar: React.FC<Props> = ({
|
|||||||
if (!searchValRef.current) {
|
if (!searchValRef.current) {
|
||||||
dispatch(setIsFiltering(false));
|
dispatch(setIsFiltering(false));
|
||||||
dispatch(setFilterValue(""));
|
dispatch(setFilterValue(""));
|
||||||
dispatch(addFilteredVideos([]));
|
dispatch(addFilteredFiles([]));
|
||||||
searchValRef.current = "";
|
searchValRef.current = "";
|
||||||
if (!inputRef.current) return;
|
if (!inputRef.current) return;
|
||||||
inputRef.current.value = "";
|
inputRef.current.value = "";
|
||||||
@ -346,7 +357,7 @@ const NavBar: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
navigate("/");
|
navigate("/");
|
||||||
dispatch(setIsFiltering(true));
|
dispatch(setIsFiltering(true));
|
||||||
dispatch(addFilteredVideos([]));
|
dispatch(addFilteredFiles([]));
|
||||||
dispatch(setFilterValue(searchValRef.current));
|
dispatch(setFilterValue(searchValRef.current));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -357,7 +368,7 @@ const NavBar: React.FC<Props> = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(setIsFiltering(false));
|
dispatch(setIsFiltering(false));
|
||||||
dispatch(setFilterValue(""));
|
dispatch(setFilterValue(""));
|
||||||
dispatch(addFilteredVideos([]));
|
dispatch(addFilteredFiles([]));
|
||||||
searchValRef.current = "";
|
searchValRef.current = "";
|
||||||
if (!inputRef.current) return;
|
if (!inputRef.current) return;
|
||||||
inputRef.current.value = "";
|
inputRef.current.value = "";
|
||||||
@ -403,8 +414,6 @@ const NavBar: React.FC<Props> = ({
|
|||||||
<PublishFile />
|
<PublishFile />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
</AvatarContainer>
|
</AvatarContainer>
|
||||||
|
|
||||||
<Popover
|
<Popover
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
57
src/constants/Categories/1stCategories.ts
Normal file
57
src/constants/Categories/1stCategories.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import audioIcon from "../../assets/icons/audio.webp";
|
||||||
|
import bookIcon from "../../assets/icons/book.webp";
|
||||||
|
import documentIcon from "../../assets/icons/document.webp";
|
||||||
|
import gamingIcon from "../../assets/icons/gaming.webp";
|
||||||
|
import imageIcon from "../../assets/icons/image.webp";
|
||||||
|
import softwareIcon from "../../assets/icons/software.webp";
|
||||||
|
import unknownIcon from "../../assets/icons/unknown.webp";
|
||||||
|
import videoIcon from "../../assets/icons/video.webp";
|
||||||
|
|
||||||
|
import {
|
||||||
|
audioSubCategories,
|
||||||
|
bookSubCategories,
|
||||||
|
documentSubCategories,
|
||||||
|
imageSubCategories,
|
||||||
|
softwareSubCategories,
|
||||||
|
videoSubCategories,
|
||||||
|
} from "./2ndCategories.ts";
|
||||||
|
import { musicSubCategories } from "./3rdCategories.ts";
|
||||||
|
import {
|
||||||
|
Categories,
|
||||||
|
Category,
|
||||||
|
CategoryData,
|
||||||
|
} from "../../components/common/CategoryList/CategoryList.tsx";
|
||||||
|
import {
|
||||||
|
getAllCategoriesWithIcons,
|
||||||
|
sortCategory,
|
||||||
|
} from "./CategoryFunctions.ts";
|
||||||
|
|
||||||
|
export const firstCategories: Category[] = [
|
||||||
|
{ id: 1, name: "Software", icon: softwareIcon },
|
||||||
|
{ id: 2, name: "Gaming", icon: gamingIcon },
|
||||||
|
{ id: 3, name: "Audio", icon: audioIcon },
|
||||||
|
{ id: 4, name: "Video", icon: videoIcon },
|
||||||
|
{ id: 5, name: "Image", icon: imageIcon },
|
||||||
|
{ id: 6, name: "Document", icon: documentIcon },
|
||||||
|
{ id: 7, name: "Book", icon: bookIcon },
|
||||||
|
{ id: 99, name: "Other", icon: unknownIcon },
|
||||||
|
].sort(sortCategory);
|
||||||
|
export const secondCategories: Categories = {
|
||||||
|
1: softwareSubCategories.sort(sortCategory),
|
||||||
|
3: audioSubCategories.sort(sortCategory),
|
||||||
|
4: videoSubCategories.sort(sortCategory),
|
||||||
|
5: imageSubCategories.sort(sortCategory),
|
||||||
|
6: documentSubCategories.sort(sortCategory),
|
||||||
|
7: bookSubCategories.sort(sortCategory),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const thirdCategories: Categories = {
|
||||||
|
301: musicSubCategories,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const allCategoryData: CategoryData = {
|
||||||
|
category: firstCategories,
|
||||||
|
subCategories: [secondCategories, thirdCategories],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const iconCategories = getAllCategoriesWithIcons();
|
88
src/constants/Categories/2ndCategories.ts
Normal file
88
src/constants/Categories/2ndCategories.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
export const softwareSubCategories = [
|
||||||
|
{ id: 101, name: "OS" },
|
||||||
|
{ id: 102, name: "Application" },
|
||||||
|
{ id: 103, name: "Source Code" },
|
||||||
|
{ id: 104, name: "Plugin" },
|
||||||
|
{ id: 199, name: "Other" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const audioSubCategories = [
|
||||||
|
{ id: 301, name: "Music" },
|
||||||
|
{ id: 302, name: "Podcast" },
|
||||||
|
{ id: 303, name: "Audiobook" },
|
||||||
|
{ id: 304, name: "Sound Effect" },
|
||||||
|
{ id: 305, name: "Lecture or Speech" },
|
||||||
|
{ id: 306, name: "Radio Show" },
|
||||||
|
{ id: 307, name: "Ambient Sound" },
|
||||||
|
{ id: 308, name: "Language Learning Material" },
|
||||||
|
{ id: 309, name: "Comedy & Satire" },
|
||||||
|
{ id: 310, name: "Documentary" },
|
||||||
|
{ id: 311, name: "Guided Meditation & Yoga" },
|
||||||
|
{ id: 312, name: "Live Performance" },
|
||||||
|
{ id: 313, name: "Nature Sound" },
|
||||||
|
{ id: 314, name: "Soundtrack" },
|
||||||
|
{ id: 315, name: "Interview" },
|
||||||
|
{ id: 399, name: "Other" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const videoSubCategories = [
|
||||||
|
{ id: 404, name: "Education" },
|
||||||
|
{ id: 405, name: "Lifestyle" },
|
||||||
|
{ id: 406, name: "Gaming" },
|
||||||
|
{ id: 407, name: "Technology" },
|
||||||
|
{ id: 408, name: "Sports" },
|
||||||
|
{ id: 409, name: "News & Politics" },
|
||||||
|
{ id: 410, name: "Cooking & Food" },
|
||||||
|
{ id: 411, name: "Animation" },
|
||||||
|
{ id: 412, name: "Science" },
|
||||||
|
{ id: 413, name: "Health & Wellness" },
|
||||||
|
{ id: 414, name: "DIY & Crafts" },
|
||||||
|
{ id: 415, name: "Kids & Family" },
|
||||||
|
{ id: 416, name: "Comedy" },
|
||||||
|
{ id: 417, name: "Travel & Adventure" },
|
||||||
|
{ id: 418, name: "Art & Design" },
|
||||||
|
{ id: 419, name: "Nature & Environment" },
|
||||||
|
{ id: 420, name: "Business & Finance" },
|
||||||
|
{ id: 421, name: "Personal Development" },
|
||||||
|
{ id: 423, name: "History" },
|
||||||
|
{ id: 499, name: "Other" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const imageSubCategories = [
|
||||||
|
{ id: 501, name: "Nature" },
|
||||||
|
{ id: 502, name: "Urban & Cityscapes" },
|
||||||
|
{ id: 503, name: "People & Portraits" },
|
||||||
|
{ id: 504, name: "Art & Abstract" },
|
||||||
|
{ id: 505, name: "Travel & Adventure" },
|
||||||
|
{ id: 506, name: "Animals & Wildlife" },
|
||||||
|
{ id: 507, name: "Sports & Action" },
|
||||||
|
{ id: 508, name: "Food & Cuisine" },
|
||||||
|
{ id: 509, name: "Fashion & Beauty" },
|
||||||
|
{ id: 510, name: "Technology & Science" },
|
||||||
|
{ id: 511, name: "Historical & Cultural" },
|
||||||
|
{ id: 512, name: "Aerial & Drone" },
|
||||||
|
{ id: 513, name: "Black & White" },
|
||||||
|
{ id: 514, name: "Events & Celebrations" },
|
||||||
|
{ id: 515, name: "Business & Corporate" },
|
||||||
|
{ id: 516, name: "Health & Wellness" },
|
||||||
|
{ id: 517, name: "Transportation & Vehicles" },
|
||||||
|
{ id: 518, name: "Still Life & Objects" },
|
||||||
|
{ id: 519, name: "Architecture & Buildings" },
|
||||||
|
{ id: 520, name: "Landscapes & Seascapes" },
|
||||||
|
{ id: 599, name: "Other" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const documentSubCategories = [
|
||||||
|
{ id: 601, name: "PDF" },
|
||||||
|
{ id: 602, name: "Word Document" },
|
||||||
|
{ id: 603, name: "Spreadsheet" },
|
||||||
|
{ id: 604, name: "Powerpoint" },
|
||||||
|
{ id: 699, name: "Other" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const bookSubCategories = [
|
||||||
|
{ id: 701, name: "Audiobook" },
|
||||||
|
{ id: 702, name: "Comic" },
|
||||||
|
{ id: 703, name: "Magazine" },
|
||||||
|
{ id: 799, name: "Other" },
|
||||||
|
];
|
23
src/constants/Categories/3rdCategories.ts
Normal file
23
src/constants/Categories/3rdCategories.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export const musicSubCategories = [
|
||||||
|
{ id: 30101, name: "Rock" },
|
||||||
|
{ id: 30102, name: "Pop" },
|
||||||
|
{ id: 30103, name: "Classical" },
|
||||||
|
{ id: 30104, name: "Jazz" },
|
||||||
|
{ id: 30105, name: "Electronic" },
|
||||||
|
{ id: 30106, name: "Country" },
|
||||||
|
{ id: 30107, name: "Hip Hop/Rap" },
|
||||||
|
{ id: 30108, name: "Blues" },
|
||||||
|
{ id: 30109, name: "R&B/Soul" },
|
||||||
|
{ id: 30110, name: "Reggae" },
|
||||||
|
{ id: 30111, name: "Folk" },
|
||||||
|
{ id: 30112, name: "Metal" },
|
||||||
|
{ id: 30113, name: "World Music" },
|
||||||
|
{ id: 30114, name: "Latin" },
|
||||||
|
{ id: 30115, name: "Indie" },
|
||||||
|
{ id: 30116, name: "Punk" },
|
||||||
|
{ id: 30117, name: "Soundtracks" },
|
||||||
|
{ id: 30118, name: "Children's Music" },
|
||||||
|
{ id: 30119, name: "New Age" },
|
||||||
|
{ id: 30120, name: "Classical Crossover" },
|
||||||
|
{ id: 30199, name: "Other" },
|
||||||
|
];
|
91
src/constants/Categories/CategoryFunctions.ts
Normal file
91
src/constants/Categories/CategoryFunctions.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
Category,
|
||||||
|
getCategoriesFromObject,
|
||||||
|
} from "../../components/common/CategoryList/CategoryList.tsx";
|
||||||
|
import { allCategoryData, iconCategories } from "./1stCategories.ts";
|
||||||
|
|
||||||
|
export const sortCategory = (a: Category, b: Category) => {
|
||||||
|
if (a.name === "Other") return 1;
|
||||||
|
else if (b.name === "Other") return -1;
|
||||||
|
else return a.name.localeCompare(b.name);
|
||||||
|
};
|
||||||
|
type Direction = "forward" | "backward";
|
||||||
|
const findCategory = (categoryID: number) => {
|
||||||
|
return allCategoryData.category.find(category => {
|
||||||
|
return category.id === categoryID;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const findSubCategory = (
|
||||||
|
categoryID: number,
|
||||||
|
direction: Direction = "forward"
|
||||||
|
) => {
|
||||||
|
const subCategoriesList = allCategoryData.subCategories;
|
||||||
|
if (direction === "backward") subCategoriesList.reverse();
|
||||||
|
|
||||||
|
for (const subCategories of subCategoriesList) {
|
||||||
|
for (const subCategoryID in subCategories) {
|
||||||
|
const returnValue = subCategories[subCategoryID].find(categoryObj => {
|
||||||
|
return categoryObj.id === categoryID;
|
||||||
|
});
|
||||||
|
if (returnValue) return returnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const findCategoryData = (
|
||||||
|
categoryID: number,
|
||||||
|
direction: Direction = "forward"
|
||||||
|
) => {
|
||||||
|
return direction === "forward"
|
||||||
|
? findCategory(categoryID) || findSubCategory(categoryID, "forward")
|
||||||
|
: findSubCategory(categoryID, "backward") || findCategory(categoryID);
|
||||||
|
};
|
||||||
|
export const findAllCategoryData = (
|
||||||
|
categories: string[],
|
||||||
|
direction: Direction = "forward"
|
||||||
|
) => {
|
||||||
|
let foundIcons: Category[] = [];
|
||||||
|
if (direction === "backward") categories.reverse();
|
||||||
|
|
||||||
|
categories.map(category => {
|
||||||
|
if (category) {
|
||||||
|
const icon = findCategoryData(+category, "backward");
|
||||||
|
if (icon) foundIcons.push(icon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return foundIcons;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCategoriesWithIcons = (categories: Category[]) => {
|
||||||
|
return categories.filter(category => {
|
||||||
|
return category.icon;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllCategoriesWithIcons = () => {
|
||||||
|
const categoriesWithIcons: Category[] = [];
|
||||||
|
|
||||||
|
allCategoryData.category.map(category => {
|
||||||
|
if (category.icon) categoriesWithIcons.push(category);
|
||||||
|
});
|
||||||
|
const subCategoriesList = allCategoryData.subCategories;
|
||||||
|
|
||||||
|
for (const subCategories of subCategoriesList) {
|
||||||
|
for (const subCategoryID in subCategories) {
|
||||||
|
const categoryWithIcon = subCategories[subCategoryID].map(categoryObj => {
|
||||||
|
if (categoryObj.icon) categoriesWithIcons.push(categoryObj);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return categoriesWithIcons;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getIconsFromObject = (fileObj: any) => {
|
||||||
|
const categories = getCategoriesFromObject(fileObj);
|
||||||
|
const icons = categories
|
||||||
|
.map(categoryID => {
|
||||||
|
return iconCategories.find(category => category.id === +categoryID)?.icon;
|
||||||
|
})
|
||||||
|
.reverse();
|
||||||
|
|
||||||
|
return icons.find(icon => icon !== undefined);
|
||||||
|
};
|
@ -11,7 +11,3 @@ export const QSHARE_FILE_BASE = useTestIdentifiers
|
|||||||
export const QSHARE_COMMENT_BASE = useTestIdentifiers
|
export const QSHARE_COMMENT_BASE = useTestIdentifiers
|
||||||
? "qcomment_v1_MYTEST_"
|
? "qcomment_v1_MYTEST_"
|
||||||
: "qcomment_v1_qshare_";
|
: "qcomment_v1_qshare_";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1,3 @@
|
|||||||
export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.~;:|]/g;
|
export const minPriceSuperlike = 10;
|
||||||
|
export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.;:|—~@#$%^*+=<>]/g;
|
||||||
|
export const titleFormatterOnSave = /[^a-zA-Z0-9\s-_!()&',.;—~@#$%^+=]/g;
|
@ -1,108 +1,127 @@
|
|||||||
import React from 'react'
|
import React from "react";
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
addVideos,
|
addFiles,
|
||||||
addToHashMap,
|
addToHashMap,
|
||||||
setCountNewVideos,
|
setCountNewFiles,
|
||||||
upsertVideos,
|
upsertFiles,
|
||||||
upsertVideosBeginning,
|
upsertFilesBeginning,
|
||||||
Video,
|
Video,
|
||||||
upsertFilteredVideos
|
upsertFilteredFiles,
|
||||||
} from '../state/features/videoSlice'
|
} from "../state/features/fileSlice.ts";
|
||||||
import {
|
import {
|
||||||
setIsLoadingGlobal, setUserAvatarHash
|
setIsLoadingGlobal,
|
||||||
} from '../state/features/globalSlice'
|
setUserAvatarHash,
|
||||||
import { RootState } from '../state/store'
|
setTotalFilesPublished,
|
||||||
import { fetchAndEvaluateVideos } from '../utils/fetchVideos'
|
setTotalNamesPublished,
|
||||||
import { QSHARE_PLAYLIST_BASE, QSHARE_FILE_BASE } from '../constants/Identifiers.ts'
|
setFilesPerNamePublished,
|
||||||
import { RequestQueue } from '../utils/queue'
|
} from "../state/features/globalSlice";
|
||||||
import { queue } from '../wrappers/GlobalWrapper'
|
import { RootState } from "../state/store";
|
||||||
|
import { fetchAndEvaluateVideos } from "../utils/fetchVideos";
|
||||||
|
import {
|
||||||
|
QSHARE_PLAYLIST_BASE,
|
||||||
|
QSHARE_FILE_BASE,
|
||||||
|
} from "../constants/Identifiers.ts";
|
||||||
|
import { RequestQueue } from "../utils/queue";
|
||||||
|
import { queue } from "../wrappers/GlobalWrapper";
|
||||||
|
import { getCategoriesFetchString } from "../components/common/CategoryList/CategoryList.tsx";
|
||||||
|
|
||||||
export const useFetchFiles = () => {
|
export const useFetchFiles = () => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch();
|
||||||
const hashMapVideos = useSelector(
|
const hashMapFiles = useSelector(
|
||||||
(state: RootState) => state.video.hashMapVideos
|
(state: RootState) => state.file.hashMapFiles
|
||||||
)
|
);
|
||||||
const videos = useSelector((state: RootState) => state.video.videos)
|
const videos = useSelector((state: RootState) => state.file.files);
|
||||||
const userAvatarHash = useSelector(
|
const userAvatarHash = useSelector(
|
||||||
(state: RootState) => state.global.userAvatarHash
|
(state: RootState) => state.global.userAvatarHash
|
||||||
)
|
);
|
||||||
const filteredVideos = useSelector(
|
const filteredVideos = useSelector(
|
||||||
(state: RootState) => state.video.filteredVideos
|
(state: RootState) => state.file.filteredFiles
|
||||||
)
|
);
|
||||||
|
|
||||||
const checkAndUpdateVideo = React.useCallback(
|
const totalFilesPublished = useSelector(
|
||||||
|
(state: RootState) => state.global.totalFilesPublished
|
||||||
|
);
|
||||||
|
const totalNamesPublished = useSelector(
|
||||||
|
(state: RootState) => state.global.totalNamesPublished
|
||||||
|
);
|
||||||
|
const filesPerNamePublished = useSelector(
|
||||||
|
(state: RootState) => state.global.filesPerNamePublished
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkAndUpdateFile = React.useCallback(
|
||||||
(video: Video) => {
|
(video: Video) => {
|
||||||
const existingVideo = hashMapVideos[video.id]
|
const existingVideo = hashMapFiles[video.id];
|
||||||
if (!existingVideo) {
|
if (!existingVideo) {
|
||||||
return true
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
video?.updated &&
|
video?.updated &&
|
||||||
existingVideo?.updated &&
|
existingVideo?.updated &&
|
||||||
(!existingVideo?.updated || video?.updated) > existingVideo?.updated
|
(!existingVideo?.updated || video?.updated) > existingVideo?.updated
|
||||||
) {
|
) {
|
||||||
return true
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[hashMapVideos]
|
[hashMapFiles]
|
||||||
)
|
);
|
||||||
|
|
||||||
const getAvatar = React.useCallback(async (author: string) => {
|
const getAvatar = React.useCallback(async (author: string) => {
|
||||||
try {
|
try {
|
||||||
let url = await qortalRequest({
|
let url = await qortalRequest({
|
||||||
action: 'GET_QDN_RESOURCE_URL',
|
action: "GET_QDN_RESOURCE_URL",
|
||||||
name: author,
|
name: author,
|
||||||
service: 'THUMBNAIL',
|
service: "THUMBNAIL",
|
||||||
identifier: 'qortal_avatar'
|
identifier: "qortal_avatar",
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
setUserAvatarHash({
|
||||||
|
name: author,
|
||||||
|
url,
|
||||||
})
|
})
|
||||||
|
);
|
||||||
dispatch(setUserAvatarHash({
|
|
||||||
name: author,
|
|
||||||
url
|
|
||||||
}))
|
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
const getVideo = async (user: string, videoId: string, content: any, retries: number = 0) => {
|
const getFile = async (
|
||||||
|
user: string,
|
||||||
|
videoId: string,
|
||||||
|
content: any,
|
||||||
|
retries: number = 0
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const res = await fetchAndEvaluateVideos({
|
const res = await fetchAndEvaluateVideos({
|
||||||
user,
|
user,
|
||||||
videoId,
|
videoId,
|
||||||
content
|
content,
|
||||||
})
|
});
|
||||||
|
|
||||||
dispatch(addToHashMap(res))
|
dispatch(addToHashMap(res));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
retries= retries + 1
|
retries = retries + 1;
|
||||||
if (retries < 2) { // 3 is the maximum number of retries here, you can adjust it to your needs
|
if (retries < 2) {
|
||||||
queue.push(() => getVideo(user, videoId, content, retries + 1));
|
// 3 is the maximum number of retries here, you can adjust it to your needs
|
||||||
|
queue.push(() => getFile(user, videoId, content, retries + 1));
|
||||||
} else {
|
} else {
|
||||||
console.error('Failed to get video after 3 attempts', error);
|
console.error("Failed to get video after 3 attempts", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNewFiles = React.useCallback(async () => {
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getNewVideos = React.useCallback(async () => {
|
|
||||||
try {
|
try {
|
||||||
dispatch(setIsLoadingGlobal(true))
|
dispatch(setIsLoadingGlobal(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 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, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const responseData = await response.json()
|
const responseData = await response.json();
|
||||||
|
|
||||||
// const responseData = await qortalRequest({
|
// const responseData = await qortalRequest({
|
||||||
// action: "SEARCH_QDN_RESOURCES",
|
// action: "SEARCH_QDN_RESOURCES",
|
||||||
@ -116,16 +135,16 @@ export const useFetchFiles = () => {
|
|||||||
// exactMatchNames: true,
|
// exactMatchNames: true,
|
||||||
// name: names
|
// name: names
|
||||||
// })
|
// })
|
||||||
const latestVideo = videos[0]
|
const latestVideo = videos[0];
|
||||||
if (!latestVideo) return
|
if (!latestVideo) return;
|
||||||
const findVideo = responseData?.findIndex(
|
const findVideo = responseData?.findIndex(
|
||||||
(item: any) => item?.identifier === latestVideo?.id
|
(item: any) => item?.identifier === latestVideo?.id
|
||||||
)
|
);
|
||||||
let fetchAll = responseData
|
let fetchAll = responseData;
|
||||||
let willFetchAll = true
|
let willFetchAll = true;
|
||||||
if (findVideo !== -1) {
|
if (findVideo !== -1) {
|
||||||
willFetchAll = false
|
willFetchAll = false;
|
||||||
fetchAll = responseData.slice(0, findVideo)
|
fetchAll = responseData.slice(0, findVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const structureData = fetchAll.map((video: any): Video => {
|
const structureData = fetchAll.map((video: any): Video => {
|
||||||
@ -138,98 +157,82 @@ export const useFetchFiles = () => {
|
|||||||
created: video?.created,
|
created: video?.created,
|
||||||
updated: video?.updated,
|
updated: video?.updated,
|
||||||
user: video.name,
|
user: video.name,
|
||||||
videoImage: '',
|
videoImage: "",
|
||||||
id: video.identifier
|
id: video.identifier,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
if (!willFetchAll) {
|
if (!willFetchAll) {
|
||||||
dispatch(upsertVideosBeginning(structureData))
|
dispatch(upsertFilesBeginning(structureData));
|
||||||
}
|
}
|
||||||
if (willFetchAll) {
|
if (willFetchAll) {
|
||||||
dispatch(addVideos(structureData))
|
dispatch(addFiles(structureData));
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dispatch(setCountNewVideos(0))
|
dispatch(setCountNewFiles(0));
|
||||||
}, 1000)
|
}, 1000);
|
||||||
for (const content of structureData) {
|
for (const content of structureData) {
|
||||||
if (content.user && content.id) {
|
if (content.user && content.id) {
|
||||||
const res = checkAndUpdateVideo(content)
|
const res = checkAndUpdateFile(content);
|
||||||
if (res) {
|
if (res) {
|
||||||
queue.push(() => getVideo(content.user, content.id, content));
|
queue.push(() => getFile(content.user, content.id, content));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
} finally {
|
||||||
dispatch(setIsLoadingGlobal(false))
|
dispatch(setIsLoadingGlobal(false));
|
||||||
}
|
}
|
||||||
}, [videos, hashMapVideos])
|
}, [videos, hashMapFiles]);
|
||||||
|
|
||||||
const getVideos = React.useCallback(async (filters = {}, reset?:boolean, resetFilers?: boolean,limit?: number) => {
|
const getFiles = React.useCallback(
|
||||||
|
async (
|
||||||
|
filters = {},
|
||||||
|
reset?: boolean,
|
||||||
|
resetFilers?: boolean,
|
||||||
|
limit?: number
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const {name = '',
|
const {
|
||||||
category = '',
|
name = "",
|
||||||
subcategory = '',
|
categories = [],
|
||||||
subcategory2 = '',
|
keywords = "",
|
||||||
subcategory3 = '',
|
type = "",
|
||||||
keywords = '',
|
}: any = resetFilers ? {} : filters;
|
||||||
type = '' }: any = resetFilers ? {} : filters
|
let offset = videos.length;
|
||||||
let offset = videos.length
|
|
||||||
if (reset) {
|
if (reset) {
|
||||||
offset = 0
|
offset = 0;
|
||||||
}
|
}
|
||||||
const videoLimit = limit || 50
|
const videoLimit = limit || 50;
|
||||||
let defaultUrl = `/arbitrary/resources/search?mode=ALL&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}&limit=${videoLimit}`;
|
let defaultUrl = `/arbitrary/resources/search?mode=ALL&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}&limit=${videoLimit}`;
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
defaultUrl += `&name=${name}`;
|
defaultUrl += `&name=${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (category) {
|
if (categories.length > 0) {
|
||||||
// Start with the category
|
defaultUrl += "&description=" + getCategoriesFetchString(categories);
|
||||||
let description = `cat:${category}`;
|
|
||||||
|
|
||||||
// Check and append subcategory
|
|
||||||
if (subcategory) {
|
|
||||||
description += `;sub:${subcategory}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check and append subcategory2
|
|
||||||
if (subcategory2) {
|
|
||||||
description += `;sub2:${subcategory2}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check and append subcategory3
|
|
||||||
if (subcategory3) {
|
|
||||||
description += `;sub3:${subcategory3}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the description to the URL
|
|
||||||
defaultUrl += `&description=${description}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keywords) {
|
if (keywords) {
|
||||||
defaultUrl = defaultUrl + `&query=${keywords}`
|
defaultUrl = defaultUrl + `&query=${keywords}`;
|
||||||
}
|
}
|
||||||
if(type === 'playlists'){
|
if (type === "playlists") {
|
||||||
defaultUrl = defaultUrl + `&service=PLAYLIST`
|
defaultUrl = defaultUrl + `&service=PLAYLIST`;
|
||||||
defaultUrl = defaultUrl + `&identifier=${QSHARE_PLAYLIST_BASE}`
|
defaultUrl = defaultUrl + `&identifier=${QSHARE_PLAYLIST_BASE}`;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
defaultUrl = defaultUrl + `&service=DOCUMENT`
|
defaultUrl = defaultUrl + `&service=DOCUMENT`;
|
||||||
defaultUrl = defaultUrl + `&identifier=${QSHARE_FILE_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}`
|
// 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}`
|
||||||
const url = defaultUrl
|
const url = defaultUrl;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const responseData = await response.json()
|
const responseData = await response.json();
|
||||||
|
|
||||||
|
|
||||||
// const responseData = await qortalRequest({
|
// const responseData = await qortalRequest({
|
||||||
// action: "SEARCH_QDN_RESOURCES",
|
// action: "SEARCH_QDN_RESOURCES",
|
||||||
@ -255,47 +258,45 @@ export const useFetchFiles = () => {
|
|||||||
created: video?.created,
|
created: video?.created,
|
||||||
updated: video?.updated,
|
updated: video?.updated,
|
||||||
user: video.name,
|
user: video.name,
|
||||||
videoImage: '',
|
videoImage: "",
|
||||||
id: video.identifier
|
id: video.identifier,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
if (reset) {
|
if (reset) {
|
||||||
dispatch(addVideos(structureData))
|
dispatch(addFiles(structureData));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
dispatch(upsertVideos(structureData))
|
dispatch(upsertFiles(structureData));
|
||||||
|
|
||||||
}
|
}
|
||||||
for (const content of structureData) {
|
for (const content of structureData) {
|
||||||
if (content.user && content.id) {
|
if (content.user && content.id) {
|
||||||
const res = checkAndUpdateVideo(content)
|
const res = checkAndUpdateFile(content);
|
||||||
if (res) {
|
if (res) {
|
||||||
queue.push(() => getVideo(content.user, content.id, content));
|
queue.push(() => getFile(content.user, content.id, content));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log({error})
|
console.log({ error });
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [videos, hashMapVideos])
|
},
|
||||||
|
[videos, hashMapFiles]
|
||||||
|
);
|
||||||
|
|
||||||
const getVideosFiltered = React.useCallback(async (filterValue: string) => {
|
const getFilesFiltered = React.useCallback(
|
||||||
|
async (filterValue: string) => {
|
||||||
try {
|
try {
|
||||||
const offset = filteredVideos.length
|
const offset = filteredVideos.length;
|
||||||
const replaceSpacesWithUnderscore = filterValue.replace(/ /g, '_');
|
const replaceSpacesWithUnderscore = filterValue.replace(/ /g, "_");
|
||||||
|
|
||||||
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 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, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const responseData = await response.json()
|
const responseData = await response.json();
|
||||||
|
|
||||||
// const responseData = await qortalRequest({
|
// const responseData = await qortalRequest({
|
||||||
// action: "SEARCH_QDN_RESOURCES",
|
// action: "SEARCH_QDN_RESOURCES",
|
||||||
@ -321,38 +322,37 @@ export const useFetchFiles = () => {
|
|||||||
created: video?.created,
|
created: video?.created,
|
||||||
updated: video?.updated,
|
updated: video?.updated,
|
||||||
user: video.name,
|
user: video.name,
|
||||||
videoImage: '',
|
videoImage: "",
|
||||||
id: video.identifier
|
id: video.identifier,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
dispatch(upsertFilteredVideos(structureData))
|
dispatch(upsertFilteredFiles(structureData));
|
||||||
|
|
||||||
for (const content of structureData) {
|
for (const content of structureData) {
|
||||||
if (content.user && content.id) {
|
if (content.user && content.id) {
|
||||||
const res = checkAndUpdateVideo(content)
|
const res = checkAndUpdateFile(content);
|
||||||
if (res) {
|
if (res) {
|
||||||
queue.push(() => getVideo(content.user, content.id, content));
|
queue.push(() => getFile(content.user, content.id, content));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [filteredVideos, hashMapVideos])
|
},
|
||||||
|
[filteredVideos, hashMapFiles]
|
||||||
|
);
|
||||||
|
|
||||||
const checkNewVideos = React.useCallback(async () => {
|
const checkNewFiles = React.useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
|
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_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, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const responseData = await response.json()
|
const responseData = await response.json();
|
||||||
// const responseData = await qortalRequest({
|
// const responseData = await qortalRequest({
|
||||||
// action: "SEARCH_QDN_RESOURCES",
|
// action: "SEARCH_QDN_RESOURCES",
|
||||||
// mode: "ALL",
|
// mode: "ALL",
|
||||||
@ -365,29 +365,57 @@ export const useFetchFiles = () => {
|
|||||||
// exactMatchNames: true,
|
// exactMatchNames: true,
|
||||||
// name: names
|
// name: names
|
||||||
// })
|
// })
|
||||||
const latestVideo = videos[0]
|
const latestVideo = videos[0];
|
||||||
if (!latestVideo) return
|
if (!latestVideo) return;
|
||||||
const findVideo = responseData?.findIndex(
|
const findVideo = responseData?.findIndex(
|
||||||
(item: any) => item?.identifier === latestVideo?.id
|
(item: any) => item?.identifier === latestVideo?.id
|
||||||
)
|
);
|
||||||
if (findVideo === -1) {
|
if (findVideo === -1) {
|
||||||
dispatch(setCountNewVideos(responseData.length))
|
dispatch(setCountNewFiles(responseData.length));
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const newArray = responseData.slice(0, findVideo)
|
const newArray = responseData.slice(0, findVideo);
|
||||||
dispatch(setCountNewVideos(newArray.length))
|
dispatch(setCountNewFiles(newArray.length));
|
||||||
return
|
return;
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, [videos])
|
}, [videos]);
|
||||||
|
|
||||||
|
const getFilesCount = React.useCallback(async () => {
|
||||||
|
try {
|
||||||
|
let url = `/arbitrary/resources/search?mode=ALL&includemetadata=false&limit=0&service=DOCUMENT&identifier=${QSHARE_FILE_BASE}`;
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const responseData = await response.json();
|
||||||
|
|
||||||
|
const totalFilesPublished = responseData.length;
|
||||||
|
const uniqueNames = new Set(responseData.map(video => video.name));
|
||||||
|
const totalNamesPublished = uniqueNames.size;
|
||||||
|
const filesPerNamePublished = (
|
||||||
|
totalFilesPublished / totalNamesPublished
|
||||||
|
).toFixed(2);
|
||||||
|
|
||||||
|
dispatch(setTotalFilesPublished(totalFilesPublished));
|
||||||
|
dispatch(setTotalNamesPublished(totalNamesPublished));
|
||||||
|
dispatch(setFilesPerNamePublished(filesPerNamePublished));
|
||||||
|
} catch (error) {
|
||||||
|
console.log({ error });
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getFiles: getVideos,
|
getFiles,
|
||||||
checkAndUpdateVideo,
|
checkAndUpdateFile,
|
||||||
getVideo,
|
getFile,
|
||||||
hashMapVideos,
|
hashMapFiles,
|
||||||
getNewFiles: getNewVideos,
|
getNewFiles,
|
||||||
checkNewFiles: checkNewVideos,
|
checkNewFiles,
|
||||||
getFilesFiltered: getVideosFiltered
|
getFilesFiltered,
|
||||||
}
|
getFilesCount,
|
||||||
}
|
};
|
||||||
|
};
|
||||||
|
85
src/pages/FileContent/FileContent-styles.tsx
Normal file
85
src/pages/FileContent/FileContent-styles.tsx
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { styled } from "@mui/system";
|
||||||
|
import { Box, Grid, Typography, Checkbox } from "@mui/material";
|
||||||
|
|
||||||
|
export const FilePlayerContainer = styled(Box)(({ theme }) => ({
|
||||||
|
maxWidth: "95%",
|
||||||
|
width: "1000px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const FileTitle = styled(Typography)(({ theme }) => ({
|
||||||
|
fontFamily: "Raleway",
|
||||||
|
fontSize: "20px",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
userSelect: "none",
|
||||||
|
wordBreak: "break-word",
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const FileDescription = styled(Typography)(({ theme }) => ({
|
||||||
|
fontFamily: "Raleway",
|
||||||
|
fontSize: "16px",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
userSelect: "none",
|
||||||
|
wordBreak: "break-word",
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const Spacer = ({ height }: any) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
height: height,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StyledCardHeaderComment = styled(Box)({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
gap: "5px",
|
||||||
|
padding: "7px 0px",
|
||||||
|
});
|
||||||
|
export const StyledCardCol = styled(Box)({
|
||||||
|
display: "flex",
|
||||||
|
overflow: "hidden",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "2px",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
width: "100%",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const StyledCardColComment = styled(Box)({
|
||||||
|
display: "flex",
|
||||||
|
overflow: "hidden",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "2px",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
width: "100%",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const AuthorTextComment = styled(Typography)({
|
||||||
|
fontFamily: "Raleway, sans-serif",
|
||||||
|
fontSize: "16px",
|
||||||
|
lineHeight: "1.2",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const FileAttachmentContainer = styled(Box)(({ theme }) => ({
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "20px",
|
||||||
|
padding: "5px 10px",
|
||||||
|
border: `1px solid ${theme.palette.text.primary}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const FileAttachmentFont = styled(Typography)(({ theme }) => ({
|
||||||
|
fontFamily: "Mulish",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
fontSize: "16px",
|
||||||
|
letterSpacing: 0,
|
||||||
|
fontWeight: 400,
|
||||||
|
userSelect: "none",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
}));
|
@ -1,73 +1,68 @@
|
|||||||
import React, { useState, useMemo, useRef, useEffect } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { setIsLoadingGlobal } from "../../state/features/globalSlice";
|
import { setIsLoadingGlobal } from "../../state/features/globalSlice";
|
||||||
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
import { Avatar, Box, Typography, useTheme } from "@mui/material";
|
||||||
import { VideoPlayer } from "../../components/common/VideoPlayer";
|
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import { addToHashMap } from "../../state/features/videoSlice";
|
import { addToHashMap } from "../../state/features/fileSlice.ts";
|
||||||
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||||
import DownloadIcon from "@mui/icons-material/Download";
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
|
|
||||||
import mockImg from "../../test/mockimg.jpg";
|
|
||||||
import {
|
import {
|
||||||
AuthorTextComment,
|
AuthorTextComment,
|
||||||
FileAttachmentContainer,
|
FileAttachmentContainer,
|
||||||
FileAttachmentFont,
|
FileAttachmentFont,
|
||||||
|
FileDescription,
|
||||||
|
FilePlayerContainer,
|
||||||
|
FileTitle,
|
||||||
Spacer,
|
Spacer,
|
||||||
StyledCardColComment,
|
StyledCardColComment,
|
||||||
StyledCardHeaderComment,
|
StyledCardHeaderComment,
|
||||||
VideoDescription,
|
} from "./FileContent-styles.tsx";
|
||||||
VideoPlayerContainer,
|
import { formatDate } from "../../utils/time";
|
||||||
VideoTitle,
|
|
||||||
} from "./VideoContent-styles";
|
|
||||||
import { setUserAvatarHash } from "../../state/features/globalSlice";
|
|
||||||
import {
|
|
||||||
formatDate,
|
|
||||||
formatDateSeconds,
|
|
||||||
formatTimestampSeconds,
|
|
||||||
} from "../../utils/time";
|
|
||||||
import { NavbarName } from "../../components/layout/Navbar/Navbar-styles";
|
|
||||||
import { CommentSection } from "../../components/common/Comments/CommentSection";
|
import { CommentSection } from "../../components/common/Comments/CommentSection";
|
||||||
import {
|
|
||||||
CrowdfundSubTitle,
|
|
||||||
CrowdfundSubTitleRow,
|
|
||||||
} from "../../components/PublishFile/Upload-styles.tsx";
|
|
||||||
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
||||||
import { Playlists } from "../../components/Playlists/Playlists";
|
|
||||||
import { DisplayHtml } from "../../components/common/TextEditor/DisplayHtml";
|
import { DisplayHtml } from "../../components/common/TextEditor/DisplayHtml";
|
||||||
import FileElement from "../../components/common/FileElement";
|
import FileElement from "../../components/common/FileElement";
|
||||||
import {categories, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
|
import {
|
||||||
|
allCategoryData,
|
||||||
|
iconCategories,
|
||||||
|
} from "../../constants/Categories/1stCategories.ts";
|
||||||
|
import {
|
||||||
|
Category,
|
||||||
|
getCategoriesFromObject,
|
||||||
|
} from "../../components/common/CategoryList/CategoryList.tsx";
|
||||||
|
import {
|
||||||
|
findAllCategoryData,
|
||||||
|
findCategoryData,
|
||||||
|
getCategoriesWithIcons,
|
||||||
|
getIconsFromObject,
|
||||||
|
} from "../../constants/Categories/CategoryFunctions.ts";
|
||||||
|
|
||||||
export function formatBytes(bytes, decimals = 2) {
|
export function formatBytes(bytes, decimals = 2) {
|
||||||
if (bytes === 0) return '0 Bytes';
|
if (bytes === 0) return "0 Bytes";
|
||||||
|
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
const dm = decimals < 0 ? 0 : decimals;
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FileContent = () => {
|
||||||
|
|
||||||
|
|
||||||
export const VideoContent = () => {
|
|
||||||
const { name, id } = useParams();
|
const { name, id } = useParams();
|
||||||
const [isExpandedDescription, setIsExpandedDescription] =
|
const [isExpandedDescription, setIsExpandedDescription] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [descriptionHeight, setDescriptionHeight] =
|
const [descriptionHeight, setDescriptionHeight] = useState<null | number>(
|
||||||
useState<null | number>(null);
|
null
|
||||||
|
);
|
||||||
|
const [icon, setIcon] = useState<string>("");
|
||||||
const userAvatarHash = useSelector(
|
const userAvatarHash = useSelector(
|
||||||
(state: RootState) => state.global.userAvatarHash
|
(state: RootState) => state.global.userAvatarHash
|
||||||
);
|
);
|
||||||
const contentRef = useRef(null);
|
const contentRef = useRef(null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const avatarUrl = useMemo(() => {
|
const avatarUrl = useMemo(() => {
|
||||||
let url = "";
|
let url = "";
|
||||||
if (name && userAvatarHash[name]) {
|
if (name && userAvatarHash[name]) {
|
||||||
@ -79,15 +74,15 @@ export const VideoContent = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const [videoData, setVideoData] = useState<any>(null);
|
const [fileData, setFileData] = useState<any>(null);
|
||||||
const [playlistData, setPlaylistData] = useState<any>(null);
|
const [playlistData, setPlaylistData] = useState<any>(null);
|
||||||
|
|
||||||
const hashMapVideos = useSelector(
|
const hashMapVideos = useSelector(
|
||||||
(state: RootState) => state.video.hashMapVideos
|
(state: RootState) => state.file.hashMapFiles
|
||||||
);
|
);
|
||||||
const videoReference = useMemo(() => {
|
const videoReference = useMemo(() => {
|
||||||
if (!videoData) return null;
|
if (!fileData) return null;
|
||||||
const { videoReference } = videoData;
|
const { videoReference } = fileData;
|
||||||
if (
|
if (
|
||||||
videoReference?.identifier &&
|
videoReference?.identifier &&
|
||||||
videoReference?.name &&
|
videoReference?.name &&
|
||||||
@ -97,13 +92,13 @@ export const VideoContent = () => {
|
|||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [videoData]);
|
}, [fileData]);
|
||||||
|
|
||||||
const videoCover = useMemo(() => {
|
const videoCover = useMemo(() => {
|
||||||
if (!videoData) return null;
|
if (!fileData) return null;
|
||||||
const { videoImage } = videoData;
|
const { videoImage } = fileData;
|
||||||
return videoImage || null;
|
return videoImage || null;
|
||||||
}, [videoData]);
|
}, [fileData]);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const getVideoData = React.useCallback(async (name: string, id: string) => {
|
const getVideoData = React.useCallback(async (name: string, id: string) => {
|
||||||
@ -147,8 +142,7 @@ export const VideoContent = () => {
|
|||||||
...resourceData,
|
...resourceData,
|
||||||
...responseData,
|
...responseData,
|
||||||
};
|
};
|
||||||
|
setFileData(combinedData);
|
||||||
setVideoData(combinedData);
|
|
||||||
dispatch(addToHashMap(combinedData));
|
dispatch(addToHashMap(combinedData));
|
||||||
checkforPlaylist(name, id, combinedData?.code);
|
checkforPlaylist(name, id, combinedData?.code);
|
||||||
}
|
}
|
||||||
@ -230,7 +224,7 @@ export const VideoContent = () => {
|
|||||||
const existingVideo = hashMapVideos[id];
|
const existingVideo = hashMapVideos[id];
|
||||||
|
|
||||||
if (existingVideo) {
|
if (existingVideo) {
|
||||||
setVideoData(existingVideo);
|
setFileData(existingVideo);
|
||||||
checkforPlaylist(name, id, existingVideo?.code);
|
checkforPlaylist(name, id, existingVideo?.code);
|
||||||
} else {
|
} else {
|
||||||
getVideoData(name, id);
|
getVideoData(name, id);
|
||||||
@ -272,25 +266,51 @@ export const VideoContent = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (contentRef.current) {
|
if (contentRef.current) {
|
||||||
const height = contentRef.current.offsetHeight;
|
const height = contentRef.current.offsetHeight;
|
||||||
if (height > 100) { // Assuming 100px is your threshold
|
if (height > 100) {
|
||||||
setDescriptionHeight(100)
|
// Assuming 100px is your threshold
|
||||||
|
setDescriptionHeight(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [videoData]);
|
if (fileData) {
|
||||||
|
//const icon = getIconsFromObject(fileData)[0]?.icon || null;
|
||||||
|
|
||||||
|
const icon = getIconsFromObject(fileData);
|
||||||
|
setIcon(icon);
|
||||||
|
}
|
||||||
|
}, [fileData]);
|
||||||
|
|
||||||
const categoriesDisplay = useMemo(() => {
|
const categoriesDisplay = useMemo(() => {
|
||||||
const category = categories?.find((item)=> item?.id === videoData?.category)
|
if (fileData) {
|
||||||
if(!category) return null
|
const categoryList = getCategoriesFromObject(fileData);
|
||||||
const subcategory = subCategories[category?.id]?.find(item=> item?.id === videoData?.subcategory)
|
|
||||||
if(!subcategory) return category?.name
|
|
||||||
|
|
||||||
const subcategory2 = subCategories2[subcategory?.id]?.find(item => item.id === videoData?.subcategory2)
|
|
||||||
if(!subcategory2) return `${category?.name} > ${subcategory?.name}`
|
|
||||||
const subcategory3 = subCategories3[subcategory2?.id]?.find(item => item.id === videoData?.subcategory3)
|
|
||||||
if(!subcategory3) return `${category?.name} > ${subcategory?.name} > ${subcategory2?.name}`
|
|
||||||
return `${category?.name} > ${subcategory?.name} > ${subcategory2?.name} > ${subcategory3?.name}`
|
|
||||||
}, [videoData])
|
|
||||||
|
|
||||||
|
const categoryNames = categoryList.map((categoryID, index) => {
|
||||||
|
let categoryName: Category;
|
||||||
|
if (index === 0) {
|
||||||
|
categoryName = allCategoryData.category.find(
|
||||||
|
item => item?.id === +categoryList[0]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const subCategories = allCategoryData.subCategories[index - 1];
|
||||||
|
const selectedSubCategory = subCategories[categoryList[index - 1]];
|
||||||
|
if (selectedSubCategory) {
|
||||||
|
categoryName = selectedSubCategory.find(
|
||||||
|
item => item?.id === +categoryList[index]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return categoryName?.name;
|
||||||
|
});
|
||||||
|
const filteredCategoryNames = categoryNames.filter(name => name);
|
||||||
|
let categoryDisplay = "";
|
||||||
|
const separator = " > ";
|
||||||
|
filteredCategoryNames.map((name, index) => {
|
||||||
|
categoryDisplay +=
|
||||||
|
index !== filteredCategoryNames.length - 1 ? name + separator : name;
|
||||||
|
});
|
||||||
|
return categoryDisplay;
|
||||||
|
}
|
||||||
|
return "no videodata";
|
||||||
|
}, [fileData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -301,24 +321,42 @@ export const VideoContent = () => {
|
|||||||
padding: "20px 10px",
|
padding: "20px 10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<VideoPlayerContainer
|
<FilePlayerContainer
|
||||||
sx={{
|
sx={{
|
||||||
marginBottom: "30px",
|
marginBottom: "30px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
|
<div
|
||||||
<VideoTitle
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{icon ? (
|
||||||
|
<img
|
||||||
|
src={icon}
|
||||||
|
width="50px"
|
||||||
|
style={{
|
||||||
|
borderRadius: "5px",
|
||||||
|
marginRight: "10px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<AttachFileIcon />
|
||||||
|
)}
|
||||||
|
<FileTitle
|
||||||
variant="h1"
|
variant="h1"
|
||||||
color="textPrimary"
|
color="textPrimary"
|
||||||
sx={{
|
sx={{
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoData?.title}
|
{fileData?.title}
|
||||||
</VideoTitle>
|
</FileTitle>
|
||||||
{videoData?.created && (
|
</div>
|
||||||
|
{fileData?.created && (
|
||||||
<Typography
|
<Typography
|
||||||
variant="h6"
|
variant="h6"
|
||||||
sx={{
|
sx={{
|
||||||
@ -326,7 +364,7 @@ export const VideoContent = () => {
|
|||||||
}}
|
}}
|
||||||
color={theme.palette.text.primary}
|
color={theme.palette.text.primary}
|
||||||
>
|
>
|
||||||
{formatDate(videoData.created)}
|
{formatDate(fileData.created)}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -367,11 +405,15 @@ export const VideoContent = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
<Box>
|
<Box>
|
||||||
<Typography sx={{
|
<Typography
|
||||||
fontWeight: 'bold',
|
sx={{
|
||||||
fontSize: '16px',
|
fontWeight: "bold",
|
||||||
userSelect: 'none'
|
fontSize: "16px",
|
||||||
}}>{categoriesDisplay}</Typography>
|
userSelect: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{categoriesDisplay}
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
<Box
|
<Box
|
||||||
@ -380,10 +422,16 @@ export const VideoContent = () => {
|
|||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
padding: "5px",
|
padding: "5px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
cursor: !descriptionHeight ? "default" : isExpandedDescription ? "default" : "pointer",
|
cursor: !descriptionHeight
|
||||||
|
? "default"
|
||||||
|
: isExpandedDescription
|
||||||
|
? "default"
|
||||||
|
: "pointer",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
}}
|
}}
|
||||||
className={!descriptionHeight ? "": isExpandedDescription ? "" : "hover-click"}
|
className={
|
||||||
|
!descriptionHeight ? "" : isExpandedDescription ? "" : "hover-click"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{descriptionHeight && !isExpandedDescription && (
|
{descriptionHeight && !isExpandedDescription && (
|
||||||
<Box
|
<Box
|
||||||
@ -404,24 +452,32 @@ export const VideoContent = () => {
|
|||||||
<Box
|
<Box
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
sx={{
|
sx={{
|
||||||
height: !descriptionHeight ? 'auto' : isExpandedDescription ? "auto" : "100px",
|
height: !descriptionHeight
|
||||||
|
? "auto"
|
||||||
|
: isExpandedDescription
|
||||||
|
? "auto"
|
||||||
|
: "100px",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoData?.htmlDescription ? (
|
{fileData?.htmlDescription ? (
|
||||||
<DisplayHtml html={videoData?.htmlDescription} />
|
<DisplayHtml html={fileData?.htmlDescription} />
|
||||||
) : (
|
) : (
|
||||||
<VideoDescription variant="body1" color="textPrimary" sx={{
|
<FileDescription
|
||||||
cursor: 'default'
|
variant="body1"
|
||||||
}}>
|
color="textPrimary"
|
||||||
{videoData?.fullDescription}
|
sx={{
|
||||||
</VideoDescription>
|
cursor: "default",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fileData?.fullDescription}
|
||||||
|
</FileDescription>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
{descriptionHeight && (
|
{descriptionHeight && (
|
||||||
<Typography
|
<Typography
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsExpandedDescription((prev) => !prev);
|
setIsExpandedDescription(prev => !prev);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
@ -434,44 +490,43 @@ export const VideoContent = () => {
|
|||||||
{isExpandedDescription ? "Show less" : "...more"}
|
{isExpandedDescription ? "Show less" : "...more"}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
<Box
|
||||||
width: '100%',
|
sx={{
|
||||||
display: 'flex',
|
width: "100%",
|
||||||
alignItems: 'flex-start',
|
display: "flex",
|
||||||
flexDirection: 'column',
|
alignItems: "flex-start",
|
||||||
gap: '25px',
|
flexDirection: "column",
|
||||||
marginTop: '25px'
|
gap: "25px",
|
||||||
}}>
|
marginTop: "25px",
|
||||||
{videoData?.files?.map((file)=> {
|
}}
|
||||||
|
>
|
||||||
|
{fileData?.files?.map((file, index) => {
|
||||||
return (
|
return (
|
||||||
<FileAttachmentContainer sx={{
|
<FileAttachmentContainer
|
||||||
width: '100%',
|
sx={{
|
||||||
display: 'flex',
|
width: "100%",
|
||||||
justifyContent: 'space-between'
|
display: "flex",
|
||||||
}}>
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
<FileAttachmentFont>
|
key={file.toString() + index}
|
||||||
{file.filename}
|
>
|
||||||
</FileAttachmentFont>
|
<FileAttachmentFont>{file.filename}</FileAttachmentFont>
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: '25px',
|
gap: "25px",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
|
}}
|
||||||
}}>
|
>
|
||||||
|
|
||||||
|
|
||||||
<FileAttachmentFont>
|
<FileAttachmentFont>
|
||||||
{formatBytes(file?.size || 0)}
|
{formatBytes(file?.size || 0)}
|
||||||
</FileAttachmentFont>
|
</FileAttachmentFont>
|
||||||
<FileElement
|
<FileElement
|
||||||
fileInfo={{...file,
|
fileInfo={{
|
||||||
|
...file,
|
||||||
filename: file?.filename,
|
filename: file?.filename,
|
||||||
mimeType: file?.mimetype
|
mimeType: file?.mimetype,
|
||||||
|
|
||||||
}}
|
}}
|
||||||
jsonId={id}
|
jsonId={id}
|
||||||
title={file?.filename}
|
title={file?.filename}
|
||||||
@ -485,11 +540,10 @@ export const VideoContent = () => {
|
|||||||
</FileElement>
|
</FileElement>
|
||||||
</Box>
|
</Box>
|
||||||
</FileAttachmentContainer>
|
</FileAttachmentContainer>
|
||||||
)
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</VideoPlayerContainer>
|
</FilePlayerContainer>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
@ -1,77 +1,79 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from '../../state/store'
|
import { RootState } from "../../state/store";
|
||||||
|
import { Avatar, Box, Button, Typography, useTheme } from "@mui/material";
|
||||||
|
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
||||||
|
import LazyLoad from "../../components/common/LazyLoad";
|
||||||
import {
|
import {
|
||||||
Avatar,
|
BottomParent,
|
||||||
Box,
|
NameContainer,
|
||||||
Button,
|
VideoCard,
|
||||||
Typography,
|
VideoCardName,
|
||||||
useTheme
|
VideoCardTitle,
|
||||||
} from '@mui/material'
|
FileContainer,
|
||||||
import { useFetchFiles } from '../../hooks/useFetchFiles.tsx'
|
VideoUploadDate,
|
||||||
import LazyLoad from '../../components/common/LazyLoad'
|
} from "./FileList-styles.tsx";
|
||||||
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './FileList-styles.tsx'
|
import ResponsiveImage from "../../components/ResponsiveImage";
|
||||||
import ResponsiveImage from '../../components/ResponsiveImage'
|
import { formatDate, formatTimestampSeconds } from "../../utils/time";
|
||||||
import { formatDate, formatTimestampSeconds } from '../../utils/time'
|
import { ChannelCard, ChannelTitle } from "./Home-styles";
|
||||||
import { ChannelCard, ChannelTitle } from './Home-styles'
|
|
||||||
|
|
||||||
interface VideoListProps {
|
interface VideoListProps {
|
||||||
mode?: string
|
mode?: string;
|
||||||
}
|
}
|
||||||
export const Channels = ({ mode }: VideoListProps) => {
|
export const Channels = ({ mode }: VideoListProps) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme();
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate();
|
||||||
const publishNames = useSelector((state: RootState)=> state.global.publishNames)
|
const publishNames = useSelector(
|
||||||
|
(state: RootState) => state.global.publishNames
|
||||||
|
);
|
||||||
const userAvatarHash = useSelector(
|
const userAvatarHash = useSelector(
|
||||||
(state: RootState) => state.global.userAvatarHash
|
(state: RootState) => state.global.userAvatarHash
|
||||||
)
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
width: '100%',
|
sx={{
|
||||||
display: 'flex',
|
width: "100%",
|
||||||
flexDirection: 'column',
|
display: "flex",
|
||||||
alignItems: 'center',
|
flexDirection: "column",
|
||||||
minHeight: '50vh'
|
alignItems: "center",
|
||||||
}}>
|
minHeight: "50vh",
|
||||||
<VideoContainer>
|
}}
|
||||||
{publishNames && publishNames?.slice(0, 10).map((name)=> {
|
>
|
||||||
let avatarUrl = ''
|
<FileContainer>
|
||||||
|
{publishNames &&
|
||||||
|
publishNames?.slice(0, 10).map(name => {
|
||||||
|
let avatarUrl = "";
|
||||||
if (userAvatarHash[name]) {
|
if (userAvatarHash[name]) {
|
||||||
avatarUrl = userAvatarHash[name]
|
avatarUrl = userAvatarHash[name];
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
flex: 0,
|
flex: 0,
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
width: 'auto',
|
width: "auto",
|
||||||
position: 'relative',
|
position: "relative",
|
||||||
' @media (max-width: 450px)': {
|
" @media (max-width: 450px)": {
|
||||||
width: '100%'
|
width: "100%",
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
key={name}
|
key={name}
|
||||||
>
|
>
|
||||||
<ChannelCard
|
<ChannelCard
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/channel/${name}`)
|
navigate(`/channel/${name}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ChannelTitle>{name}</ChannelTitle>
|
<ChannelTitle>{name}</ChannelTitle>
|
||||||
<ResponsiveImage src={avatarUrl} width={50} height={50} />
|
<ResponsiveImage src={avatarUrl} width={50} height={50} />
|
||||||
</ChannelCard>
|
</ChannelCard>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
})}
|
})}
|
||||||
</VideoContainer>
|
</FileContainer>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
import { styled } from "@mui/system";
|
import { styled } from "@mui/system";
|
||||||
import { Box, Grid, Typography, Checkbox, TextField, InputLabel, Autocomplete } from "@mui/material";
|
import {
|
||||||
|
Box,
|
||||||
|
Grid,
|
||||||
|
Typography,
|
||||||
|
Checkbox,
|
||||||
|
TextField,
|
||||||
|
InputLabel,
|
||||||
|
Autocomplete,
|
||||||
|
} from "@mui/material";
|
||||||
|
|
||||||
export const VideoContainer = styled(Box)(({ theme }) => ({
|
export const FileContainer = styled(Box)(({ theme }) => ({
|
||||||
position: "relative",
|
position: "relative",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
padding: "15px",
|
padding: "15px",
|
||||||
@ -9,7 +17,7 @@ export const VideoContainer = styled(Box)(({ theme }) => ({
|
|||||||
gap: "20px",
|
gap: "20px",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
justifyContent: "flex-start",
|
justifyContent: "flex-start",
|
||||||
width: '100%'
|
width: "100%",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StoresRow = styled(Grid)(({ theme }) => ({
|
export const StoresRow = styled(Grid)(({ theme }) => ({
|
||||||
@ -21,8 +29,8 @@ export const StoresRow = styled(Grid)(({ theme }) => ({
|
|||||||
width: "auto",
|
width: "auto",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
"@media (max-width: 450px)": {
|
"@media (max-width: 450px)": {
|
||||||
width: "100%"
|
width: "100%",
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const VideoCard = styled(Grid)(({ theme }) => ({
|
export const VideoCard = styled(Grid)(({ theme }) => ({
|
||||||
@ -30,7 +38,7 @@ export const VideoCard = styled(Grid)(({ theme }) => ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
height: "320px",
|
height: "320px",
|
||||||
width: '300px',
|
width: "300px",
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
borderRadius: "8px",
|
borderRadius: "8px",
|
||||||
padding: "10px 15px",
|
padding: "10px 15px",
|
||||||
@ -49,8 +57,8 @@ export const VideoCard = styled(Grid)(({ theme }) => ({
|
|||||||
boxShadow:
|
boxShadow:
|
||||||
theme.palette.mode === "dark"
|
theme.palette.mode === "dark"
|
||||||
? "0px 8px 10px 1px hsla(0,0%,0%,0.14), 0px 3px 14px 2px hsla(0,0%,0%,0.12), 0px 5px 5px -3px hsla(0,0%,0%,0.2)"
|
? "0px 8px 10px 1px hsla(0,0%,0%,0.14), 0px 3px 14px 2px hsla(0,0%,0%,0.12), 0px 5px 5px -3px hsla(0,0%,0%,0.2)"
|
||||||
: "rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;"
|
: "rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;",
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StoreCardInfo = styled(Grid)(({ theme }) => ({
|
export const StoreCardInfo = styled(Grid)(({ theme }) => ({
|
||||||
@ -58,7 +66,7 @@ export const StoreCardInfo = styled(Grid)(({ theme }) => ({
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
padding: "5px",
|
padding: "5px",
|
||||||
marginTop: "15px"
|
marginTop: "15px",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const VideoImageContainer = styled(Grid)(({ theme }) => ({}));
|
export const VideoImageContainer = styled(Grid)(({ theme }) => ({}));
|
||||||
@ -67,9 +75,9 @@ export const VideoCardImage = styled("img")(({ theme }) => ({
|
|||||||
maxWidth: "300px",
|
maxWidth: "300px",
|
||||||
minWidth: "150px",
|
minWidth: "150px",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
height: '150px',
|
height: "150px",
|
||||||
objectFit: 'fill',
|
objectFit: "fill",
|
||||||
width: '266px',
|
width: "266px",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const DoubleLine = styled(Typography)`
|
const DoubleLine = styled(Typography)`
|
||||||
@ -77,14 +85,14 @@ const DoubleLine = styled(Typography)`
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`
|
`;
|
||||||
|
|
||||||
export const VideoCardTitle = styled(DoubleLine)(({ theme }) => ({
|
export const VideoCardTitle = styled(DoubleLine)(({ theme }) => ({
|
||||||
fontFamily: "Cairo",
|
fontFamily: "Cairo",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
letterSpacing: "0.4px",
|
letterSpacing: "0.4px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
export const VideoCardName = styled(Typography)(({ theme }) => ({
|
export const VideoCardName = styled(Typography)(({ theme }) => ({
|
||||||
fontFamily: "Cairo",
|
fontFamily: "Cairo",
|
||||||
@ -102,19 +110,19 @@ export const VideoCardName = styled(Typography)(({ theme }) => ({
|
|||||||
fontSize: "12px",
|
fontSize: "12px",
|
||||||
letterSpacing: "0.4px",
|
letterSpacing: "0.4px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
export const BottomParent = styled(Box)(({ theme }) => ({
|
export const BottomParent = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'flex-start',
|
alignItems: "flex-start",
|
||||||
flexDirection: 'column'
|
flexDirection: "column",
|
||||||
}));
|
}));
|
||||||
export const VideoCardDescription = styled(Typography)(({ theme }) => ({
|
export const VideoCardDescription = styled(Typography)(({ theme }) => ({
|
||||||
fontFamily: "Karla",
|
fontFamily: "Karla",
|
||||||
fontSize: "20px",
|
fontSize: "20px",
|
||||||
letterSpacing: "0px",
|
letterSpacing: "0px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StoreCardOwner = styled(Typography)(({ theme }) => ({
|
export const StoreCardOwner = styled(Typography)(({ theme }) => ({
|
||||||
@ -124,7 +132,7 @@ export const StoreCardOwner = styled(Typography)(({ theme }) => ({
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
bottom: "5px",
|
bottom: "5px",
|
||||||
right: "10px",
|
right: "10px",
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const StoreCardYouOwn = styled(Box)(({ theme }) => ({
|
export const StoreCardYouOwn = styled(Box)(({ theme }) => ({
|
||||||
@ -136,7 +144,7 @@ export const StoreCardYouOwn = styled(Box)(({ theme }) => ({
|
|||||||
gap: "5px",
|
gap: "5px",
|
||||||
fontFamily: "Livvic",
|
fontFamily: "Livvic",
|
||||||
fontSize: "15px",
|
fontSize: "15px",
|
||||||
color: theme.palette.text.primary
|
color: theme.palette.text.primary,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const MyStoresRow = styled(Grid)(({ theme }) => ({
|
export const MyStoresRow = styled(Grid)(({ theme }) => ({
|
||||||
@ -144,16 +152,16 @@ export const MyStoresRow = styled(Grid)(({ theme }) => ({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "flex-end",
|
justifyContent: "flex-end",
|
||||||
padding: "5px",
|
padding: "5px",
|
||||||
width: "100%"
|
width: "100%",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const NameContainer = styled(Box)(({ theme }) => ({
|
export const NameContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "flex-start",
|
justifyContent: "flex-start",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
gap: '10px',
|
gap: "10px",
|
||||||
marginBottom: '10px'
|
marginBottom: "10px",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const MyStoresCard = styled(Box)(({ theme }) => ({
|
export const MyStoresCard = styled(Box)(({ theme }) => ({
|
||||||
@ -166,14 +174,14 @@ export const MyStoresCard = styled(Box)(({ theme }) => ({
|
|||||||
padding: "5px 10px",
|
padding: "5px 10px",
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "18px",
|
fontSize: "18px",
|
||||||
color: theme.palette.text.primary
|
color: theme.palette.text.primary,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const MyStoresCheckbox = styled(Checkbox)(({ theme }) => ({
|
export const MyStoresCheckbox = styled(Checkbox)(({ theme }) => ({
|
||||||
color: "#c0d4ff",
|
color: "#c0d4ff",
|
||||||
"&.Mui-checked": {
|
"&.Mui-checked": {
|
||||||
color: "#6596ff"
|
color: "#6596ff",
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FiltersCol = styled(Grid)(({ theme }) => ({
|
export const FiltersCol = styled(Grid)(({ theme }) => ({
|
||||||
@ -183,13 +191,13 @@ export const FiltersCol = styled(Grid)(({ theme }) => ({
|
|||||||
padding: "20px 15px",
|
padding: "20px 15px",
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
borderTop: `1px solid ${theme.palette.background.paper}`,
|
borderTop: `1px solid ${theme.palette.background.paper}`,
|
||||||
borderRight: `1px solid ${theme.palette.background.paper}`
|
borderRight: `1px solid ${theme.palette.background.paper}`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FiltersContainer = styled(Box)(({ theme }) => ({
|
export const FiltersContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between"
|
justifyContent: "space-between",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FiltersRow = styled(Box)(({ theme }) => ({
|
export const FiltersRow = styled(Box)(({ theme }) => ({
|
||||||
@ -199,7 +207,7 @@ export const FiltersRow = styled(Box)(({ theme }) => ({
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "0 15px",
|
padding: "0 15px",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FiltersTitle = styled(Typography)(({ theme }) => ({
|
export const FiltersTitle = styled(Typography)(({ theme }) => ({
|
||||||
@ -210,74 +218,73 @@ export const FiltersTitle = styled(Typography)(({ theme }) => ({
|
|||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "17px",
|
fontSize: "17px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FiltersCheckbox = styled(Checkbox)(({ theme }) => ({
|
export const FiltersCheckbox = styled(Checkbox)(({ theme }) => ({
|
||||||
color: "#c0d4ff",
|
color: "#c0d4ff",
|
||||||
"&.Mui-checked": {
|
"&.Mui-checked": {
|
||||||
color: "#6596ff"
|
color: "#6596ff",
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FilterSelect = styled(Autocomplete)(({ theme }) => ({
|
export const FilterSelect = styled(Autocomplete)(({ theme }) => ({
|
||||||
"& #categories-select": {
|
"& #categories-select": {
|
||||||
padding: "7px"
|
padding: "7px",
|
||||||
},
|
},
|
||||||
"& .MuiSelect-placeholder": {
|
"& .MuiSelect-placeholder": {
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "17px",
|
fontSize: "17px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
},
|
},
|
||||||
"& MuiFormLabel-root": {
|
"& MuiFormLabel-root": {
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "17px",
|
fontSize: "17px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FilterSelectMenuItems = styled(TextField)(({ theme }) => ({
|
export const FilterSelectMenuItems = styled(TextField)(({ theme }) => ({
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "17px",
|
fontSize: "17px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
userSelect: "none"
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
export const FiltersSubContainer = styled(Box)(({ theme }) => ({
|
export const FiltersSubContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: "5px"
|
gap: "5px",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FilterDropdownLabel = styled(InputLabel)(({ theme }) => ({
|
export const FilterDropdownLabel = styled(InputLabel)(({ theme }) => ({
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
color: theme.palette.text.primary
|
color: theme.palette.text.primary,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const IconsBox = styled(Box)({
|
export const IconsBox = styled(Box)({
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: "3px",
|
gap: "3px",
|
||||||
position: 'absolute',
|
position: "absolute",
|
||||||
top: '-20px',
|
top: "-20px",
|
||||||
right: '-5px',
|
right: "-5px",
|
||||||
transition: 'all 0.3s ease-in-out',
|
transition: "all 0.3s ease-in-out",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const BlockIconContainer = styled(Box)({
|
export const BlockIconContainer = styled(Box)({
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;",
|
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px;",
|
||||||
backgroundColor: '#fbfbfb',
|
backgroundColor: "#fbfbfb",
|
||||||
color: "#c25252",
|
color: "#c25252",
|
||||||
padding: '2px',
|
padding: "2px",
|
||||||
borderRadius: '3px',
|
borderRadius: "3px",
|
||||||
transition: 'all 0.3s ease-in-out',
|
transition: "all 0.3s ease-in-out",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
cursor: 'pointer',
|
cursor: "pointer",
|
||||||
transform: "scale(1.1)",
|
transform: "scale(1.1)",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
@ -1,326 +1,44 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import { Avatar, Box, Skeleton, Tooltip } from "@mui/material";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import ReactDOM from "react-dom";
|
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
import { RootState } from "../../state/store";
|
|
||||||
import AttachFileIcon from '@mui/icons-material/AttachFile';
|
|
||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
FormControl,
|
|
||||||
Grid,
|
|
||||||
Input,
|
|
||||||
InputLabel,
|
|
||||||
MenuItem,
|
|
||||||
OutlinedInput,
|
|
||||||
Select,
|
|
||||||
SelectChangeEvent,
|
|
||||||
Skeleton,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
|
||||||
import LazyLoad from "../../components/common/LazyLoad";
|
|
||||||
import {
|
import {
|
||||||
BlockIconContainer,
|
BlockIconContainer,
|
||||||
BottomParent,
|
BottomParent,
|
||||||
FilterSelect,
|
|
||||||
FiltersCheckbox,
|
|
||||||
FiltersCol,
|
|
||||||
FiltersContainer,
|
|
||||||
FiltersRow,
|
|
||||||
FiltersSubContainer,
|
|
||||||
FiltersTitle,
|
|
||||||
IconsBox,
|
IconsBox,
|
||||||
NameContainer,
|
NameContainer,
|
||||||
VideoCard,
|
VideoCard,
|
||||||
VideoCardName,
|
VideoCardName,
|
||||||
VideoCardTitle,
|
VideoCardTitle,
|
||||||
VideoContainer,
|
FileContainer,
|
||||||
VideoUploadDate,
|
VideoUploadDate,
|
||||||
} from "./FileList-styles.tsx";
|
} from "./FileList-styles.tsx";
|
||||||
import ResponsiveImage from "../../components/ResponsiveImage";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import { formatDate, formatTimestampSeconds } from "../../utils/time";
|
|
||||||
import { Subtitle, SubtitleContainer } from "./Home-styles";
|
|
||||||
import { ExpandMoreSVG } from "../../assets/svgs/ExpandMoreSVG";
|
|
||||||
import {
|
import {
|
||||||
addVideos,
|
|
||||||
blockUser,
|
blockUser,
|
||||||
changeFilterType,
|
setEditFile,
|
||||||
changeSelectedCategoryVideos,
|
Video,
|
||||||
changeSelectedSubCategoryVideos,
|
} from "../../state/features/fileSlice.ts";
|
||||||
changeSelectedSubCategoryVideos2,
|
|
||||||
changeSelectedSubCategoryVideos3,
|
|
||||||
changefilterName,
|
|
||||||
changefilterSearch,
|
|
||||||
clearVideoList,
|
|
||||||
setEditPlaylist,
|
|
||||||
setEditVideo,
|
|
||||||
} from "../../state/features/videoSlice";
|
|
||||||
import { Playlists } from "../../components/Playlists/Playlists";
|
|
||||||
import { PlaylistSVG } from "../../assets/svgs/PlaylistSVG";
|
|
||||||
import BlockIcon from "@mui/icons-material/Block";
|
import BlockIcon from "@mui/icons-material/Block";
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||||
import { formatBytes } from "../VideoContent/VideoContent";
|
import { formatBytes } from "../FileContent/FileContent.tsx";
|
||||||
import {categories, icons, subCategories, subCategories2, subCategories3} from "../../constants/Categories.ts";
|
import { formatDate } from "../../utils/time.ts";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../state/store.ts";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { getIconsFromObject } from "../../constants/Categories/CategoryFunctions.ts";
|
||||||
|
|
||||||
interface VideoListProps {
|
interface FileListProps {
|
||||||
mode?: string;
|
files: Video[];
|
||||||
}
|
}
|
||||||
export const FileList = ({ mode }: VideoListProps) => {
|
export const FileList = ({ files }: FileListProps) => {
|
||||||
const theme = useTheme();
|
const hashMapFiles = useSelector(
|
||||||
const prevVal = useRef("");
|
(state: RootState) => state.file.hashMapFiles
|
||||||
const isFiltering = useSelector(
|
|
||||||
(state: RootState) => state.video.isFiltering
|
|
||||||
);
|
);
|
||||||
const filterValue = useSelector(
|
|
||||||
(state: RootState) => state.video.filterValue
|
|
||||||
);
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
||||||
const [showIcons, setShowIcons] = useState(null);
|
const [showIcons, setShowIcons] = useState(null);
|
||||||
|
|
||||||
const filterType = useSelector((state: RootState) => state.video.filterType);
|
|
||||||
|
|
||||||
const setFilterType = (payload) => {
|
|
||||||
dispatch(changeFilterType(payload));
|
|
||||||
};
|
|
||||||
const filterSearch = useSelector(
|
|
||||||
(state: RootState) => state.video.filterSearch
|
|
||||||
);
|
|
||||||
|
|
||||||
const setFilterSearch = (payload) => {
|
|
||||||
dispatch(changefilterSearch(payload));
|
|
||||||
};
|
|
||||||
const filterName = useSelector((state: RootState) => state.video.filterName);
|
|
||||||
|
|
||||||
const setFilterName = (payload) => {
|
|
||||||
dispatch(changefilterName(payload));
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectedCategoryVideos = useSelector(
|
|
||||||
(state: RootState) => state.video.selectedCategoryVideos
|
|
||||||
);
|
|
||||||
|
|
||||||
const setSelectedCategoryVideos = (payload) => {
|
|
||||||
dispatch(changeSelectedCategoryVideos(payload));
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectedSubCategoryVideos = useSelector(
|
|
||||||
(state: RootState) => state.video.selectedSubCategoryVideos
|
|
||||||
);
|
|
||||||
const selectedSubCategoryVideos2 = useSelector(
|
|
||||||
(state: RootState) => state.video.selectedSubCategoryVideos2
|
|
||||||
);
|
|
||||||
const selectedSubCategoryVideos3 = useSelector(
|
|
||||||
(state: RootState) => state.video.selectedSubCategoryVideos3
|
|
||||||
);
|
|
||||||
|
|
||||||
const setSelectedSubCategoryVideos = (payload) => {
|
|
||||||
dispatch(changeSelectedSubCategoryVideos(payload));
|
|
||||||
};
|
|
||||||
const setSelectedSubCategoryVideos2 = (payload) => {
|
|
||||||
dispatch(changeSelectedSubCategoryVideos2(payload));
|
|
||||||
};
|
|
||||||
const setSelectedSubCategoryVideos3 = (payload) => {
|
|
||||||
dispatch(changeSelectedSubCategoryVideos3(payload));
|
|
||||||
};
|
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const filteredVideos = useSelector(
|
|
||||||
(state: RootState) => state.video.filteredVideos
|
|
||||||
);
|
|
||||||
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
const username = useSelector((state: RootState) => state.auth?.user?.name);
|
||||||
const isFilterMode = useRef(false);
|
const dispatch = useDispatch();
|
||||||
const firstFetch = useRef(false);
|
|
||||||
const afterFetch = useRef(false);
|
|
||||||
const isFetchingFiltered = useRef(false);
|
|
||||||
const isFetching = useRef(false);
|
|
||||||
const hashMapVideos = useSelector(
|
|
||||||
(state: RootState) => state.video.hashMapVideos
|
|
||||||
);
|
|
||||||
|
|
||||||
const countNewVideos = useSelector(
|
|
||||||
(state: RootState) => state.video.countNewVideos
|
|
||||||
);
|
|
||||||
const userAvatarHash = useSelector(
|
|
||||||
(state: RootState) => state.global.userAvatarHash
|
|
||||||
);
|
|
||||||
|
|
||||||
const { videos: globalVideos } = useSelector(
|
|
||||||
(state: RootState) => state.video
|
|
||||||
);
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { getFiles, getNewFiles, checkNewFiles, getFilesFiltered } =
|
|
||||||
useFetchFiles();
|
|
||||||
|
|
||||||
const getFilesHandler = React.useCallback(
|
|
||||||
async (reset?: boolean, resetFilers?: boolean) => {
|
|
||||||
|
|
||||||
|
|
||||||
if (!firstFetch.current || !afterFetch.current) return;
|
|
||||||
if (isFetching.current) return;
|
|
||||||
isFetching.current = true;
|
|
||||||
console.log({
|
|
||||||
category: selectedCategoryVideos?.id,
|
|
||||||
subcategory: selectedSubCategoryVideos?.id,
|
|
||||||
subcategory2: selectedSubCategoryVideos2?.id,
|
|
||||||
subcategory3: selectedSubCategoryVideos3?.id,
|
|
||||||
})
|
|
||||||
await getFiles(
|
|
||||||
{
|
|
||||||
name: filterName,
|
|
||||||
category: selectedCategoryVideos?.id,
|
|
||||||
subcategory: selectedSubCategoryVideos?.id,
|
|
||||||
subcategory2: selectedSubCategoryVideos2?.id,
|
|
||||||
subcategory3: selectedSubCategoryVideos3?.id,
|
|
||||||
keywords: filterSearch,
|
|
||||||
type: filterType,
|
|
||||||
},
|
|
||||||
reset ? true : false,
|
|
||||||
resetFilers
|
|
||||||
);
|
|
||||||
isFetching.current = false;
|
|
||||||
},
|
|
||||||
[
|
|
||||||
getFiles,
|
|
||||||
filterValue,
|
|
||||||
getFilesFiltered,
|
|
||||||
isFiltering,
|
|
||||||
filterName,
|
|
||||||
selectedCategoryVideos,
|
|
||||||
selectedSubCategoryVideos,
|
|
||||||
selectedSubCategoryVideos2,
|
|
||||||
selectedSubCategoryVideos3,
|
|
||||||
filterSearch,
|
|
||||||
filterType,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const searchOnEnter = e => {
|
|
||||||
if (e.keyCode == 13) {
|
|
||||||
getFilesHandler(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isFiltering && filterValue !== prevVal?.current) {
|
|
||||||
prevVal.current = filterValue;
|
|
||||||
getFilesHandler();
|
|
||||||
}
|
|
||||||
}, [filterValue, isFiltering, filteredVideos]);
|
|
||||||
|
|
||||||
const getFilesHandlerMount = React.useCallback(async () => {
|
|
||||||
if (firstFetch.current) return;
|
|
||||||
firstFetch.current = true;
|
|
||||||
setIsLoading(true);
|
|
||||||
|
|
||||||
await getFiles();
|
|
||||||
afterFetch.current = true;
|
|
||||||
isFetching.current = false;
|
|
||||||
|
|
||||||
setIsLoading(false);
|
|
||||||
}, [getFiles]);
|
|
||||||
|
|
||||||
let videos = globalVideos;
|
|
||||||
|
|
||||||
if (isFiltering) {
|
|
||||||
videos = filteredVideos;
|
|
||||||
isFilterMode.current = true;
|
|
||||||
} else {
|
|
||||||
isFilterMode.current = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// const interval = useRef<any>(null);
|
|
||||||
|
|
||||||
// const checkNewVideosFunc = useCallback(() => {
|
|
||||||
// let isCalling = false;
|
|
||||||
// interval.current = setInterval(async () => {
|
|
||||||
// if (isCalling || !firstFetch.current) return;
|
|
||||||
// isCalling = true;
|
|
||||||
// await checkNewVideos();
|
|
||||||
// isCalling = false;
|
|
||||||
// }, 30000); // 1 second interval
|
|
||||||
// }, [checkNewVideos]);
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (isFiltering && interval.current) {
|
|
||||||
// clearInterval(interval.current);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// checkNewVideosFunc();
|
|
||||||
|
|
||||||
// return () => {
|
|
||||||
// if (interval?.current) {
|
|
||||||
// clearInterval(interval.current);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// }, [mode, checkNewVideosFunc, isFiltering]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!firstFetch.current &&
|
|
||||||
!isFilterMode.current &&
|
|
||||||
globalVideos.length === 0
|
|
||||||
) {
|
|
||||||
isFetching.current = true;
|
|
||||||
getFilesHandlerMount();
|
|
||||||
} else {
|
|
||||||
firstFetch.current = true;
|
|
||||||
afterFetch.current = true;
|
|
||||||
}
|
|
||||||
}, [getFilesHandlerMount, globalVideos]);
|
|
||||||
|
|
||||||
const filtersToDefault = async () => {
|
|
||||||
setFilterType("videos");
|
|
||||||
setFilterSearch("");
|
|
||||||
setFilterName("");
|
|
||||||
setSelectedCategoryVideos(null);
|
|
||||||
setSelectedSubCategoryVideos(null);
|
|
||||||
|
|
||||||
ReactDOM.flushSync(() => {
|
|
||||||
getFilesHandler(true, true);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOptionCategoryChangeVideos = (
|
|
||||||
event: SelectChangeEvent<string>
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = categories.find((option) => option.id === +optionId);
|
|
||||||
setSelectedCategoryVideos(selectedOption || null);
|
|
||||||
};
|
|
||||||
const handleOptionSubCategoryChangeVideos = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos(selectedOption || null);
|
|
||||||
};
|
|
||||||
const handleOptionSubCategoryChangeVideos2 = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos2(selectedOption || null);
|
|
||||||
};
|
|
||||||
const handleOptionSubCategoryChangeVideos3 = (
|
|
||||||
event: SelectChangeEvent<string>,
|
|
||||||
subcategories: any[]
|
|
||||||
) => {
|
|
||||||
const optionId = event.target.value;
|
|
||||||
const selectedOption = subcategories.find(
|
|
||||||
(option) => option.id === +optionId
|
|
||||||
);
|
|
||||||
setSelectedSubCategoryVideos3(selectedOption || null);
|
|
||||||
};
|
|
||||||
const blockUserFunc = async (user: string) => {
|
const blockUserFunc = async (user: string) => {
|
||||||
if (user === "Q-Share") return;
|
if (user === "Q-Share") return;
|
||||||
|
|
||||||
@ -332,381 +50,22 @@ export const FileList = ({ mode }: VideoListProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response === true) {
|
if (response === true) {
|
||||||
dispatch(blockUser(user))
|
dispatch(blockUser(user));
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container sx={{ width: "100%" }}>
|
<FileContainer>
|
||||||
<FiltersCol item xs={12} md={2} sm={3}>
|
{files.map((file: any, index: number) => {
|
||||||
<FiltersContainer>
|
const existingFile = hashMapFiles[file?.id];
|
||||||
<Input
|
|
||||||
id="standard-adornment-name"
|
|
||||||
onChange={(e) => {
|
|
||||||
setFilterSearch(e.target.value);
|
|
||||||
}}
|
|
||||||
onKeyDown={searchOnEnter}
|
|
||||||
value={filterSearch}
|
|
||||||
placeholder="Search"
|
|
||||||
sx={{
|
|
||||||
borderBottom: "1px solid white",
|
|
||||||
"&&:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&:after": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&:hover:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&.Mui-focused:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&.Mui-focused": {
|
|
||||||
outline: "none",
|
|
||||||
},
|
|
||||||
fontSize: "18px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
id="standard-adornment-name"
|
|
||||||
onChange={(e) => {
|
|
||||||
setFilterName(e.target.value);
|
|
||||||
}}
|
|
||||||
onKeyDown={searchOnEnter}
|
|
||||||
value={filterName}
|
|
||||||
placeholder="User's Name (Exact)"
|
|
||||||
sx={{
|
|
||||||
marginTop: "20px",
|
|
||||||
borderBottom: "1px solid white",
|
|
||||||
"&&:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&:after": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&:hover:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&.Mui-focused:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&.Mui-focused": {
|
|
||||||
outline: "none",
|
|
||||||
},
|
|
||||||
fontSize: "18px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FiltersTitle>
|
|
||||||
Categories
|
|
||||||
<ExpandMoreSVG
|
|
||||||
color={theme.palette.text.primary}
|
|
||||||
height={"22"}
|
|
||||||
width={"22"}
|
|
||||||
/>
|
|
||||||
</FiltersTitle>
|
|
||||||
<FiltersSubContainer>
|
|
||||||
<FormControl sx={{ width: "100%" }}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
gap: "20px",
|
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 1 }}>
|
|
||||||
<InputLabel
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
}}
|
|
||||||
id="Category"
|
|
||||||
>
|
|
||||||
Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Category"
|
|
||||||
input={<OutlinedInput label="Category" />}
|
|
||||||
value={selectedCategoryVideos?.id || ""}
|
|
||||||
onChange={handleOptionCategoryChangeVideos}
|
|
||||||
sx={{
|
|
||||||
// Target the input field
|
|
||||||
".MuiSelect-select": {
|
|
||||||
fontSize: "16px", // Change font size for the selected value
|
|
||||||
padding: "10px 5px 15px 15px;",
|
|
||||||
},
|
|
||||||
// Target the dropdown icon
|
|
||||||
".MuiSelect-icon": {
|
|
||||||
fontSize: "20px", // Adjust if needed
|
|
||||||
},
|
|
||||||
// Target the dropdown menu
|
|
||||||
"& .MuiMenu-paper": {
|
|
||||||
".MuiMenuItem-root": {
|
|
||||||
fontSize: "14px", // Change font size for the menu items
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{categories.map((option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
{selectedCategoryVideos &&
|
|
||||||
subCategories[selectedCategoryVideos?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
}}
|
|
||||||
id="Sub-Category"
|
|
||||||
>
|
|
||||||
Sub-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-Category"
|
|
||||||
input={<OutlinedInput label="Sub-Category" />}
|
|
||||||
value={selectedSubCategoryVideos?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos(
|
|
||||||
e,
|
|
||||||
subCategories[selectedCategoryVideos?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
// Target the input field
|
|
||||||
".MuiSelect-select": {
|
|
||||||
fontSize: "16px", // Change font size for the selected value
|
|
||||||
padding: "10px 5px 15px 15px;",
|
|
||||||
},
|
|
||||||
// Target the dropdown icon
|
|
||||||
".MuiSelect-icon": {
|
|
||||||
fontSize: "20px", // Adjust if needed
|
|
||||||
},
|
|
||||||
// Target the dropdown menu
|
|
||||||
"& .MuiMenu-paper": {
|
|
||||||
".MuiMenuItem-root": {
|
|
||||||
fontSize: "14px", // Change font size for the menu items
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{subCategories[selectedCategoryVideos.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
{selectedSubCategoryVideos &&
|
|
||||||
subCategories2[selectedSubCategoryVideos?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
}}
|
|
||||||
id="Sub-2x-Category"
|
|
||||||
>
|
|
||||||
Sub-2x-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-2x-Category"
|
|
||||||
input={<OutlinedInput label="Sub-2x-Category" />}
|
|
||||||
value={selectedSubCategoryVideos2?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos2(
|
|
||||||
e,
|
|
||||||
subCategories2[selectedSubCategoryVideos?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
// Target the input field
|
|
||||||
".MuiSelect-select": {
|
|
||||||
fontSize: "16px", // Change font size for the selected value
|
|
||||||
padding: "10px 5px 15px 15px;",
|
|
||||||
},
|
|
||||||
// Target the dropdown icon
|
|
||||||
".MuiSelect-icon": {
|
|
||||||
fontSize: "20px", // Adjust if needed
|
|
||||||
},
|
|
||||||
// Target the dropdown menu
|
|
||||||
"& .MuiMenu-paper": {
|
|
||||||
".MuiMenuItem-root": {
|
|
||||||
fontSize: "14px", // Change font size for the menu items
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{subCategories2[selectedSubCategoryVideos.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
{selectedSubCategoryVideos2 &&
|
|
||||||
subCategories3[selectedSubCategoryVideos2?.id] && (
|
|
||||||
<FormControl fullWidth sx={{ marginBottom: 2 }}>
|
|
||||||
<InputLabel
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
}}
|
|
||||||
id="Sub-2x-Category"
|
|
||||||
>
|
|
||||||
Sub-3x-Category
|
|
||||||
</InputLabel>
|
|
||||||
<Select
|
|
||||||
labelId="Sub-3x-Category"
|
|
||||||
input={<OutlinedInput label="Sub-sx-Category" />}
|
|
||||||
value={selectedSubCategoryVideos3?.id || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleOptionSubCategoryChangeVideos3(
|
|
||||||
e,
|
|
||||||
subCategories3[selectedSubCategoryVideos2?.id]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
// Target the input field
|
|
||||||
".MuiSelect-select": {
|
|
||||||
fontSize: "16px", // Change font size for the selected value
|
|
||||||
padding: "10px 5px 15px 15px;",
|
|
||||||
},
|
|
||||||
// Target the dropdown icon
|
|
||||||
".MuiSelect-icon": {
|
|
||||||
fontSize: "20px", // Adjust if needed
|
|
||||||
},
|
|
||||||
// Target the dropdown menu
|
|
||||||
"& .MuiMenu-paper": {
|
|
||||||
".MuiMenuItem-root": {
|
|
||||||
fontSize: "14px", // Change font size for the menu items
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{subCategories3[selectedSubCategoryVideos2.id].map(
|
|
||||||
(option) => (
|
|
||||||
<MenuItem key={option.id} value={option.id}>
|
|
||||||
{option.name}
|
|
||||||
</MenuItem>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</FormControl>
|
|
||||||
</FiltersSubContainer>
|
|
||||||
{/* <FiltersTitle>
|
|
||||||
Type
|
|
||||||
<ExpandMoreSVG
|
|
||||||
color={theme.palette.text.primary}
|
|
||||||
height={"22"}
|
|
||||||
width={"22"}
|
|
||||||
/>
|
|
||||||
</FiltersTitle>
|
|
||||||
<FiltersSubContainer>
|
|
||||||
<FiltersRow>
|
|
||||||
Videos
|
|
||||||
<FiltersCheckbox
|
|
||||||
checked={filterType === "videos"}
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setFilterType("videos");
|
|
||||||
}}
|
|
||||||
inputProps={{ "aria-label": "controlled" }}
|
|
||||||
/>
|
|
||||||
</FiltersRow>
|
|
||||||
<FiltersRow>
|
|
||||||
Playlists
|
|
||||||
<FiltersCheckbox
|
|
||||||
checked={filterType === "playlists"}
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setFilterType("playlists");
|
|
||||||
}}
|
|
||||||
inputProps={{ "aria-label": "controlled" }}
|
|
||||||
/>
|
|
||||||
</FiltersRow>
|
|
||||||
</FiltersSubContainer> */}
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
filtersToDefault();
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
variant="contained"
|
|
||||||
>
|
|
||||||
reset
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
getFilesHandler(true);
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
variant="contained"
|
|
||||||
>
|
|
||||||
Search
|
|
||||||
</Button>
|
|
||||||
</FiltersContainer>
|
|
||||||
</FiltersCol>
|
|
||||||
<Grid item xs={12} md={10} sm={9}>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
marginTop: "20px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SubtitleContainer
|
|
||||||
sx={{
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
paddingLeft: "15px",
|
|
||||||
width: "100%",
|
|
||||||
maxWidth: "1400px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
</SubtitleContainer>
|
|
||||||
|
|
||||||
<VideoContainer>
|
|
||||||
{videos.map((video: any, index: number) => {
|
|
||||||
const existingVideo = hashMapVideos[video?.id];
|
|
||||||
let hasHash = false;
|
let hasHash = false;
|
||||||
let videoObj = video;
|
let fileObj = file;
|
||||||
if (existingVideo) {
|
if (existingFile) {
|
||||||
videoObj = existingVideo;
|
fileObj = existingFile;
|
||||||
hasHash = true;
|
hasHash = true;
|
||||||
}
|
}
|
||||||
|
const icon = getIconsFromObject(fileObj);
|
||||||
const category = categories?.find(item => item?.id === videoObj?.category);
|
|
||||||
const subcategory = subCategories[category?.id]?.find(item => item?.id === videoObj?.subcategory);
|
|
||||||
const subcategory2 = subCategories2[subcategory?.id]?.find(item => item.id === videoObj?.subcategory2);
|
|
||||||
const subcategory3 = subCategories3[subcategory2?.id]?.find(item => item.id === videoObj?.subcategory3);
|
|
||||||
|
|
||||||
const catId = category?.id || null;
|
|
||||||
const subId = subcategory?.id || null;
|
|
||||||
const sub2Id = subcategory2?.id || null;
|
|
||||||
const sub3Id = subcategory3?.id || null;
|
|
||||||
|
|
||||||
const icon = icons[sub3Id] || icons[sub2Id] || icons[subId] || icons[catId] || null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -714,40 +73,37 @@ export const FileList = ({ mode }: VideoListProps) => {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "75px",
|
height: "75px",
|
||||||
position:"relative"
|
position: "relative",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
key={videoObj.id}
|
key={fileObj.id}
|
||||||
onMouseEnter={() => setShowIcons(videoObj.id)}
|
onMouseEnter={() => setShowIcons(fileObj.id)}
|
||||||
onMouseLeave={() => setShowIcons(null)}
|
onMouseLeave={() => setShowIcons(null)}
|
||||||
>
|
>
|
||||||
{hasHash ? (
|
{hasHash ? (
|
||||||
<>
|
<>
|
||||||
<IconsBox
|
<IconsBox
|
||||||
sx={{
|
sx={{
|
||||||
opacity: showIcons === videoObj.id ? 1 : 0,
|
opacity: showIcons === fileObj.id ? 1 : 0,
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoObj?.user === username && (
|
{fileObj?.user === username && (
|
||||||
<Tooltip title="Edit video properties" placement="top">
|
<Tooltip title="Edit video properties" placement="top">
|
||||||
<BlockIconContainer>
|
<BlockIconContainer>
|
||||||
<EditIcon
|
<EditIcon
|
||||||
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(setEditVideo(videoObj));
|
dispatch(setEditFile(fileObj));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</BlockIconContainer>
|
</BlockIconContainer>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Tooltip title="Block user content" placement="top">
|
<Tooltip title="Block user content" placement="top">
|
||||||
<BlockIconContainer>
|
<BlockIconContainer>
|
||||||
<BlockIcon
|
<BlockIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
blockUserFunc(videoObj?.user);
|
blockUserFunc(fileObj?.user);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</BlockIconContainer>
|
</BlockIconContainer>
|
||||||
@ -755,52 +111,61 @@ export const FileList = ({ mode }: VideoListProps) => {
|
|||||||
</IconsBox>
|
</IconsBox>
|
||||||
<VideoCard
|
<VideoCard
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/share/${videoObj?.user}/${videoObj?.id}`);
|
navigate(`/share/${fileObj?.user}/${fileObj?.id}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
height: '100%',
|
height: "100%",
|
||||||
width: '100%',
|
width: "100%",
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: '25px',
|
gap: "25px",
|
||||||
flexDirection: 'row',
|
flexDirection: "row",
|
||||||
justifyContent: 'space-between'
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box
|
||||||
<Box sx={{
|
sx={{
|
||||||
|
display: "flex",
|
||||||
display: 'flex',
|
gap: "25px",
|
||||||
gap: '25px',
|
alignItems: "center",
|
||||||
alignItems: 'center'
|
}}
|
||||||
}}>
|
>
|
||||||
{icon ? <img src={icon} width="50px" style={{
|
{icon ? (
|
||||||
borderRadius: '5px'
|
<img
|
||||||
}}/> : (
|
src={icon}
|
||||||
|
width="50px"
|
||||||
|
style={{
|
||||||
|
borderRadius: "5px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<AttachFileIcon />
|
<AttachFileIcon />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<VideoCardTitle sx={{
|
<VideoCardTitle
|
||||||
width: '100px'
|
sx={{
|
||||||
}}>
|
width: "100px",
|
||||||
{formatBytes(videoObj?.files.reduce((acc, cur) => acc + (cur?.size || 0), 0))}
|
}}
|
||||||
|
>
|
||||||
|
{formatBytes(
|
||||||
|
fileObj?.files.reduce(
|
||||||
|
(acc, cur) => acc + (cur?.size || 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)}
|
||||||
</VideoCardTitle>
|
</VideoCardTitle>
|
||||||
<VideoCardTitle>{videoObj.title}</VideoCardTitle>
|
<VideoCardTitle>{fileObj.title}</VideoCardTitle>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<BottomParent>
|
<BottomParent>
|
||||||
<NameContainer
|
<NameContainer
|
||||||
onClick={(e) => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
navigate(`/channel/${videoObj?.user}`);
|
navigate(`/channel/${fileObj?.user}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{ height: 24, width: 24 }}
|
sx={{ height: 24, width: 24 }}
|
||||||
src={`/arbitrary/THUMBNAIL/${videoObj?.user}/qortal_avatar`}
|
src={`/arbitrary/THUMBNAIL/${fileObj?.user}/qortal_avatar`}
|
||||||
alt={`${videoObj?.user}'s avatar`}
|
alt={`${fileObj?.user}'s avatar`}
|
||||||
/>
|
/>
|
||||||
<VideoCardName
|
<VideoCardName
|
||||||
sx={{
|
sx={{
|
||||||
@ -809,13 +174,13 @@ export const FileList = ({ mode }: VideoListProps) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoObj?.user}
|
{fileObj?.user}
|
||||||
</VideoCardName>
|
</VideoCardName>
|
||||||
</NameContainer>
|
</NameContainer>
|
||||||
|
|
||||||
{videoObj?.created && (
|
{fileObj?.created && (
|
||||||
<VideoUploadDate>
|
<VideoUploadDate>
|
||||||
{formatDate(videoObj.created)}
|
{formatDate(fileObj.created)}
|
||||||
</VideoUploadDate>
|
</VideoUploadDate>
|
||||||
)}
|
)}
|
||||||
</BottomParent>
|
</BottomParent>
|
||||||
@ -834,18 +199,9 @@ export const FileList = ({ mode }: VideoListProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</VideoContainer>
|
</FileContainer>
|
||||||
|
|
||||||
<LazyLoad
|
|
||||||
onLoadMore={getFilesHandler}
|
|
||||||
isLoading={isLoading}
|
|
||||||
></LazyLoad>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,70 +1,59 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from '../../state/store'
|
import { RootState } from "../../state/store";
|
||||||
import AttachFileIcon from '@mui/icons-material/AttachFile';
|
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||||
|
|
||||||
|
import { Avatar, Box, Skeleton, useTheme } from "@mui/material";
|
||||||
|
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
||||||
|
import LazyLoad from "../../components/common/LazyLoad";
|
||||||
import {
|
import {
|
||||||
Avatar,
|
BottomParent,
|
||||||
Box,
|
FileContainer,
|
||||||
Button,
|
NameContainer,
|
||||||
Skeleton,
|
VideoCard,
|
||||||
Typography,
|
VideoCardName,
|
||||||
useTheme
|
VideoCardTitle,
|
||||||
} from '@mui/material'
|
VideoUploadDate,
|
||||||
import { useFetchFiles } from '../../hooks/useFetchFiles.tsx'
|
} from "./FileList-styles.tsx";
|
||||||
import LazyLoad from '../../components/common/LazyLoad'
|
import { formatDate } from "../../utils/time";
|
||||||
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './FileList-styles.tsx'
|
import { Video } from "../../state/features/fileSlice.ts";
|
||||||
import ResponsiveImage from '../../components/ResponsiveImage'
|
import { queue } from "../../wrappers/GlobalWrapper";
|
||||||
import { formatDate, formatTimestampSeconds } from '../../utils/time'
|
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
|
||||||
import { Video } from '../../state/features/videoSlice'
|
import { formatBytes } from "../FileContent/FileContent.tsx";
|
||||||
import { queue } from '../../wrappers/GlobalWrapper'
|
import { getIconsFromObject } from "../../constants/Categories/CategoryFunctions.ts";
|
||||||
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 {
|
interface VideoListProps {
|
||||||
mode?: string
|
mode?: string;
|
||||||
}
|
}
|
||||||
export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
||||||
const { name: paramName } = useParams()
|
const { name: paramName } = useParams();
|
||||||
const theme = useTheme()
|
const theme = useTheme();
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(true)
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
const firstFetch = useRef(false)
|
const firstFetch = useRef(false);
|
||||||
const afterFetch = useRef(false)
|
const afterFetch = useRef(false);
|
||||||
const hashMapVideos = useSelector(
|
const hashMapVideos = useSelector(
|
||||||
(state: RootState) => state.video.hashMapVideos
|
(state: RootState) => state.file.hashMapFiles
|
||||||
)
|
);
|
||||||
|
|
||||||
const countNewVideos = useSelector(
|
const [videos, setVideos] = React.useState<Video[]>([]);
|
||||||
(state: RootState) => state.video.countNewVideos
|
|
||||||
)
|
|
||||||
const userAvatarHash = useSelector(
|
|
||||||
(state: RootState) => state.global.userAvatarHash
|
|
||||||
)
|
|
||||||
|
|
||||||
const [videos, setVideos] = React.useState<Video[]>([])
|
const navigate = useNavigate();
|
||||||
|
const { getFile, getNewFiles, checkNewFiles, checkAndUpdateFile } =
|
||||||
const navigate = useNavigate()
|
useFetchFiles();
|
||||||
const {
|
|
||||||
getVideo,
|
|
||||||
getNewFiles,
|
|
||||||
checkNewFiles,
|
|
||||||
checkAndUpdateVideo
|
|
||||||
} = useFetchFiles()
|
|
||||||
|
|
||||||
const getVideos = React.useCallback(async () => {
|
const getVideos = React.useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const offset = videos.length
|
const offset = videos.length;
|
||||||
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 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, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const responseData = await response.json()
|
const responseData = await response.json();
|
||||||
|
|
||||||
const structureData = responseData.map((video: any): Video => {
|
const structureData = responseData.map((video: any): Video => {
|
||||||
return {
|
return {
|
||||||
@ -76,98 +65,74 @@ export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
|||||||
created: video?.created,
|
created: video?.created,
|
||||||
updated: video?.updated,
|
updated: video?.updated,
|
||||||
user: video.name,
|
user: video.name,
|
||||||
videoImage: '',
|
videoImage: "",
|
||||||
id: video.identifier
|
id: video.identifier,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
const copiedVideos: Video[] = [...videos]
|
const copiedVideos: Video[] = [...videos];
|
||||||
structureData.forEach((video: Video) => {
|
structureData.forEach((video: Video) => {
|
||||||
const index = videos.findIndex((p) => p.id === video.id)
|
const index = videos.findIndex(p => p.id === video.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
copiedVideos[index] = video
|
copiedVideos[index] = video;
|
||||||
} else {
|
} else {
|
||||||
copiedVideos.push(video)
|
copiedVideos.push(video);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
setVideos(copiedVideos)
|
setVideos(copiedVideos);
|
||||||
|
|
||||||
for (const content of structureData) {
|
for (const content of structureData) {
|
||||||
if (content.user && content.id) {
|
if (content.user && content.id) {
|
||||||
const res = checkAndUpdateVideo(content)
|
const res = checkAndUpdateFile(content);
|
||||||
if (res) {
|
if (res) {
|
||||||
queue.push(() => getVideo(content.user, content.id, content));
|
queue.push(() => getFile(content.user, content.id, content));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [videos, hashMapVideos])
|
}, [videos, hashMapVideos]);
|
||||||
|
|
||||||
|
|
||||||
const getVideosHandler = React.useCallback(async () => {
|
const getVideosHandler = React.useCallback(async () => {
|
||||||
if(!firstFetch.current || !afterFetch.current) return
|
if (!firstFetch.current || !afterFetch.current) return;
|
||||||
await getVideos()
|
await getVideos();
|
||||||
}, [getVideos])
|
}, [getVideos]);
|
||||||
|
|
||||||
|
|
||||||
const getVideosHandlerMount = React.useCallback(async () => {
|
const getVideosHandlerMount = React.useCallback(async () => {
|
||||||
if(firstFetch.current) return
|
if (firstFetch.current) return;
|
||||||
firstFetch.current = true
|
firstFetch.current = true;
|
||||||
await getVideos()
|
await getVideos();
|
||||||
afterFetch.current = true
|
afterFetch.current = true;
|
||||||
setIsLoading(false)
|
setIsLoading(false);
|
||||||
}, [getVideos])
|
}, [getVideos]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!firstFetch.current) {
|
if (!firstFetch.current) {
|
||||||
getVideosHandlerMount()
|
getVideosHandlerMount();
|
||||||
}
|
}
|
||||||
|
}, [getVideosHandlerMount]);
|
||||||
}, [getVideosHandlerMount ])
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
width: '100%',
|
sx={{
|
||||||
display: 'flex',
|
width: "100%",
|
||||||
flexDirection: 'column',
|
display: "flex",
|
||||||
alignItems: 'center'
|
flexDirection: "column",
|
||||||
}}>
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<VideoContainer>
|
<FileContainer>
|
||||||
{videos.map((video: any, index: number) => {
|
{videos.map((file: any, index: number) => {
|
||||||
const existingVideo = hashMapVideos[video?.id];
|
const existingFile = hashMapVideos[file?.id];
|
||||||
let hasHash = false;
|
let hasHash = false;
|
||||||
let videoObj = video;
|
let fileObj = file;
|
||||||
if (existingVideo) {
|
if (existingFile) {
|
||||||
videoObj = existingVideo;
|
fileObj = existingFile;
|
||||||
hasHash = true;
|
hasHash = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const icon = getIconsFromObject(fileObj);
|
||||||
const category = categories?.find(item => item?.id === videoObj?.category);
|
|
||||||
const subcategory = subCategories[category?.id]?.find(item => item?.id === videoObj?.subcategory);
|
|
||||||
const subcategory2 = subCategories2[subcategory?.id]?.find(item => item.id === videoObj?.subcategory2);
|
|
||||||
const subcategory3 = subCategories3[subcategory2?.id]?.find(item => item.id === videoObj?.subcategory3);
|
|
||||||
|
|
||||||
const catId = category?.id || null;
|
|
||||||
const subId = subcategory?.id || null;
|
|
||||||
const sub2Id = subcategory2?.id || null;
|
|
||||||
const sub3Id = subcategory3?.id || null;
|
|
||||||
|
|
||||||
const icon = icons[sub3Id] || icons[sub2Id] || icons[subId] || icons[catId] || null;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -176,61 +141,68 @@ export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "75px",
|
height: "75px",
|
||||||
position:"relative"
|
position: "relative",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
key={videoObj.id}
|
key={fileObj.id}
|
||||||
|
|
||||||
>
|
>
|
||||||
{hasHash ? (
|
{hasHash ? (
|
||||||
<>
|
<>
|
||||||
<VideoCard
|
<VideoCard
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/share/${videoObj?.user}/${videoObj?.id}`);
|
navigate(`/share/${fileObj?.user}/${fileObj?.id}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
height: '100%',
|
height: "100%",
|
||||||
width: '100%',
|
width: "100%",
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
gap: '25px',
|
gap: "25px",
|
||||||
flexDirection: 'row',
|
flexDirection: "row",
|
||||||
justifyContent: 'space-between'
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box
|
||||||
<Box sx={{
|
sx={{
|
||||||
|
display: "flex",
|
||||||
display: 'flex',
|
gap: "25px",
|
||||||
gap: '25px',
|
alignItems: "center",
|
||||||
alignItems: 'center'
|
}}
|
||||||
}}>
|
>
|
||||||
{icon ? <img src={icon} width="50px" style={{
|
{icon ? (
|
||||||
borderRadius: '5px'
|
<img
|
||||||
}}/> : (
|
src={icon}
|
||||||
|
width="50px"
|
||||||
|
style={{
|
||||||
|
borderRadius: "5px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<AttachFileIcon />
|
<AttachFileIcon />
|
||||||
)}
|
)}
|
||||||
<VideoCardTitle sx={{
|
<VideoCardTitle
|
||||||
width: '100px'
|
sx={{
|
||||||
}}>
|
width: "100px",
|
||||||
{formatBytes(videoObj?.files.reduce((acc, cur) => acc + (cur?.size || 0), 0))}
|
}}
|
||||||
|
>
|
||||||
|
{formatBytes(
|
||||||
|
fileObj?.files.reduce(
|
||||||
|
(acc, cur) => acc + (cur?.size || 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)}
|
||||||
</VideoCardTitle>
|
</VideoCardTitle>
|
||||||
<VideoCardTitle>{videoObj.title}</VideoCardTitle>
|
<VideoCardTitle>{fileObj.title}</VideoCardTitle>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
<BottomParent>
|
<BottomParent>
|
||||||
<NameContainer
|
<NameContainer
|
||||||
onClick={(e) => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
navigate(`/channel/${videoObj?.user}`);
|
navigate(`/channel/${fileObj?.user}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{ height: 24, width: 24 }}
|
sx={{ height: 24, width: 24 }}
|
||||||
src={`/arbitrary/THUMBNAIL/${videoObj?.user}/qortal_avatar`}
|
src={`/arbitrary/THUMBNAIL/${fileObj?.user}/qortal_avatar`}
|
||||||
alt={`${videoObj?.user}'s avatar`}
|
alt={`${fileObj?.user}'s avatar`}
|
||||||
/>
|
/>
|
||||||
<VideoCardName
|
<VideoCardName
|
||||||
sx={{
|
sx={{
|
||||||
@ -239,13 +211,13 @@ export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoObj?.user}
|
{fileObj?.user}
|
||||||
</VideoCardName>
|
</VideoCardName>
|
||||||
</NameContainer>
|
</NameContainer>
|
||||||
|
|
||||||
{videoObj?.created && (
|
{fileObj?.created && (
|
||||||
<VideoUploadDate>
|
<VideoUploadDate>
|
||||||
{formatDate(videoObj.created)}
|
{formatDate(fileObj.created)}
|
||||||
</VideoUploadDate>
|
</VideoUploadDate>
|
||||||
)}
|
)}
|
||||||
</BottomParent>
|
</BottomParent>
|
||||||
@ -264,14 +236,11 @@ export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</VideoContainer>
|
</FileContainer>
|
||||||
<LazyLoad onLoadMore={getVideosHandler} isLoading={isLoading}></LazyLoad>
|
<LazyLoad onLoadMore={getVideosHandler} isLoading={isLoading}></LazyLoad>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,15 +1,323 @@
|
|||||||
import React from 'react'
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { FileList } from './FileList.tsx'
|
import ReactDOM from "react-dom";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../state/store";
|
||||||
|
import { FileList } from "./FileList.tsx";
|
||||||
|
import { Box, Button, Grid, Input, useTheme } from "@mui/material";
|
||||||
|
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
||||||
|
import LazyLoad from "../../components/common/LazyLoad";
|
||||||
|
import { FiltersCol, FiltersContainer } from "./FileList-styles.tsx";
|
||||||
|
import { SubtitleContainer } from "./Home-styles";
|
||||||
|
import {
|
||||||
|
changefilterName,
|
||||||
|
changefilterSearch,
|
||||||
|
changeFilterType,
|
||||||
|
} from "../../state/features/fileSlice.ts";
|
||||||
|
import { allCategoryData } from "../../constants/Categories/1stCategories.ts";
|
||||||
|
import {
|
||||||
|
CategoryList,
|
||||||
|
CategoryListRef,
|
||||||
|
} from "../../components/common/CategoryList/CategoryList.tsx";
|
||||||
|
import { StatsData } from "../../components/StatsData.tsx";
|
||||||
|
|
||||||
import { useSelector } from 'react-redux'
|
interface HomeProps {
|
||||||
import { RootState } from '../../state/store'
|
mode?: string;
|
||||||
|
}
|
||||||
|
export const Home = ({ mode }: HomeProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const prevVal = useRef("");
|
||||||
|
const categoryListRef = useRef<CategoryListRef>(null);
|
||||||
|
const isFiltering = useSelector((state: RootState) => state.file.isFiltering);
|
||||||
|
const filterValue = useSelector((state: RootState) => state.file.filterValue);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const filterType = useSelector((state: RootState) => state.file.filterType);
|
||||||
|
const totalFilesPublished = useSelector(
|
||||||
|
(state: RootState) => state.global.totalFilesPublished
|
||||||
|
);
|
||||||
|
const totalNamesPublished = useSelector(
|
||||||
|
(state: RootState) => state.global.totalNamesPublished
|
||||||
|
);
|
||||||
|
const filesPerNamePublished = useSelector(
|
||||||
|
(state: RootState) => state.global.filesPerNamePublished
|
||||||
|
);
|
||||||
|
const setFilterType = payload => {
|
||||||
|
dispatch(changeFilterType(payload));
|
||||||
|
};
|
||||||
|
const filterSearch = useSelector(
|
||||||
|
(state: RootState) => state.file.filterSearch
|
||||||
|
);
|
||||||
|
|
||||||
export const Home = () => {
|
const setFilterSearch = payload => {
|
||||||
|
dispatch(changefilterSearch(payload));
|
||||||
|
};
|
||||||
|
const filterName = useSelector((state: RootState) => state.file.filterName);
|
||||||
|
|
||||||
|
const setFilterName = payload => {
|
||||||
|
dispatch(changefilterName(payload));
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFilterMode = useRef(false);
|
||||||
|
const firstFetch = useRef(false);
|
||||||
|
const afterFetch = useRef(false);
|
||||||
|
const isFetchingFiltered = useRef(false);
|
||||||
|
const isFetching = useRef(false);
|
||||||
|
|
||||||
|
const countNewFiles = useSelector(
|
||||||
|
(state: RootState) => state.file.countNewFiles
|
||||||
|
);
|
||||||
|
const userAvatarHash = useSelector(
|
||||||
|
(state: RootState) => state.global.userAvatarHash
|
||||||
|
);
|
||||||
|
|
||||||
|
const { files: globalVideos } = useSelector((state: RootState) => state.file);
|
||||||
|
|
||||||
|
const setSelectedCategoryFiles = payload => {};
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const filteredFiles = useSelector(
|
||||||
|
(state: RootState) => state.file.filteredFiles
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getFiles,
|
||||||
|
checkAndUpdateFile,
|
||||||
|
getFile,
|
||||||
|
hashMapFiles,
|
||||||
|
getNewFiles,
|
||||||
|
checkNewFiles,
|
||||||
|
getFilesFiltered,
|
||||||
|
getFilesCount,
|
||||||
|
} = useFetchFiles();
|
||||||
|
|
||||||
|
const getFilesHandler = React.useCallback(
|
||||||
|
async (reset?: boolean, resetFilers?: boolean) => {
|
||||||
|
if (!firstFetch.current || !afterFetch.current) return;
|
||||||
|
if (isFetching.current) return;
|
||||||
|
isFetching.current = true;
|
||||||
|
const selectedCategories =
|
||||||
|
categoryListRef.current.getSelectedCategories() || [];
|
||||||
|
|
||||||
|
await getFiles(
|
||||||
|
{
|
||||||
|
name: filterName,
|
||||||
|
categories: selectedCategories,
|
||||||
|
keywords: filterSearch,
|
||||||
|
type: filterType,
|
||||||
|
},
|
||||||
|
reset,
|
||||||
|
resetFilers
|
||||||
|
);
|
||||||
|
isFetching.current = false;
|
||||||
|
},
|
||||||
|
[
|
||||||
|
getFiles,
|
||||||
|
filterValue,
|
||||||
|
getFilesFiltered,
|
||||||
|
isFiltering,
|
||||||
|
filterName,
|
||||||
|
filterSearch,
|
||||||
|
filterType,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const searchOnEnter = e => {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
getFilesHandler(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFiltering && filterValue !== prevVal?.current) {
|
||||||
|
prevVal.current = filterValue;
|
||||||
|
getFilesHandler();
|
||||||
|
}
|
||||||
|
}, [filterValue, isFiltering, filteredFiles, getFilesCount]);
|
||||||
|
|
||||||
|
const getFilesHandlerMount = React.useCallback(async () => {
|
||||||
|
if (firstFetch.current) return;
|
||||||
|
firstFetch.current = true;
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
await getFiles();
|
||||||
|
afterFetch.current = true;
|
||||||
|
isFetching.current = false;
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
}, [getFiles]);
|
||||||
|
|
||||||
|
let videos = globalVideos;
|
||||||
|
|
||||||
|
if (isFiltering) {
|
||||||
|
videos = filteredFiles;
|
||||||
|
isFilterMode.current = true;
|
||||||
|
} else {
|
||||||
|
isFilterMode.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const interval = useRef<any>(null);
|
||||||
|
|
||||||
|
// const checkNewVideosFunc = useCallback(() => {
|
||||||
|
// let isCalling = false;
|
||||||
|
// interval.current = setInterval(async () => {
|
||||||
|
// if (isCalling || !firstFetch.current) return;
|
||||||
|
// isCalling = true;
|
||||||
|
// await checkNewVideos();
|
||||||
|
// isCalling = false;
|
||||||
|
// }, 30000); // 1 second interval
|
||||||
|
// }, [checkNewVideos]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (isFiltering && interval.current) {
|
||||||
|
// clearInterval(interval.current);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// checkNewVideosFunc();
|
||||||
|
|
||||||
|
// return () => {
|
||||||
|
// if (interval?.current) {
|
||||||
|
// clearInterval(interval.current);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// }, [mode, checkNewVideosFunc, isFiltering]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!firstFetch.current &&
|
||||||
|
!isFilterMode.current &&
|
||||||
|
globalVideos.length === 0
|
||||||
|
) {
|
||||||
|
isFetching.current = true;
|
||||||
|
getFilesHandlerMount();
|
||||||
|
} else {
|
||||||
|
firstFetch.current = true;
|
||||||
|
afterFetch.current = true;
|
||||||
|
}
|
||||||
|
}, [getFilesHandlerMount, globalVideos]);
|
||||||
|
|
||||||
|
const filtersToDefault = async () => {
|
||||||
|
setFilterType("videos");
|
||||||
|
setFilterSearch("");
|
||||||
|
setFilterName("");
|
||||||
|
categoryListRef.current?.clearCategories();
|
||||||
|
|
||||||
|
ReactDOM.flushSync(() => {
|
||||||
|
getFilesHandler(true, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Grid container sx={{ width: "100%" }}>
|
||||||
<FileList />
|
<FiltersCol item xs={12} md={2} sm={3}>
|
||||||
</>
|
<FiltersContainer>
|
||||||
|
<StatsData />
|
||||||
|
<Input
|
||||||
|
id="standard-adornment-name"
|
||||||
|
onChange={e => {
|
||||||
|
setFilterSearch(e.target.value);
|
||||||
|
}}
|
||||||
|
onKeyDown={searchOnEnter}
|
||||||
|
value={filterSearch}
|
||||||
|
placeholder="Search"
|
||||||
|
sx={{
|
||||||
|
borderBottom: "1px solid white",
|
||||||
|
"&&:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&:after": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&:hover:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&.Mui-focused:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&.Mui-focused": {
|
||||||
|
outline: "none",
|
||||||
|
},
|
||||||
|
fontSize: "18px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
id="standard-adornment-name"
|
||||||
|
onChange={e => {
|
||||||
|
setFilterName(e.target.value);
|
||||||
|
}}
|
||||||
|
onKeyDown={searchOnEnter}
|
||||||
|
value={filterName}
|
||||||
|
placeholder="User's Name (Exact)"
|
||||||
|
sx={{
|
||||||
|
marginTop: "20px",
|
||||||
|
borderBottom: "1px solid white",
|
||||||
|
"&&:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&:after": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&:hover:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&.Mui-focused:before": {
|
||||||
|
borderBottom: "none",
|
||||||
|
},
|
||||||
|
"&&.Mui-focused": {
|
||||||
|
outline: "none",
|
||||||
|
},
|
||||||
|
fontSize: "18px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<CategoryList categoryData={allCategoryData} ref={categoryListRef} />
|
||||||
|
|
||||||
)
|
<Button
|
||||||
}
|
onClick={() => {
|
||||||
|
filtersToDefault();
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
marginTop: "20px",
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
reset
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
getFilesHandler(true);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
marginTop: "20px",
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Search
|
||||||
|
</Button>
|
||||||
|
</FiltersContainer>
|
||||||
|
</FiltersCol>
|
||||||
|
<Grid item xs={12} md={10} sm={9}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
marginTop: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SubtitleContainer
|
||||||
|
sx={{
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
paddingLeft: "15px",
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "1400px",
|
||||||
|
}}
|
||||||
|
></SubtitleContainer>
|
||||||
|
<FileList files={videos} />
|
||||||
|
<LazyLoad
|
||||||
|
onLoadMore={getFilesHandler}
|
||||||
|
isLoading={isLoading}
|
||||||
|
></LazyLoad>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -1,64 +1,69 @@
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from "react";
|
||||||
import { FileListComponentLevel } from '../Home/FileListComponentLevel.tsx'
|
import { FileListComponentLevel } from "../Home/FileListComponentLevel.tsx";
|
||||||
import { HeaderContainer, ProfileContainer } from './Profile-styles'
|
import { HeaderContainer, ProfileContainer } from "./Profile-styles";
|
||||||
import { AuthorTextComment, StyledCardColComment, StyledCardHeaderComment } from '../VideoContent/VideoContent-styles'
|
import {
|
||||||
import { Avatar, Box, useTheme } from '@mui/material'
|
AuthorTextComment,
|
||||||
import { useParams } from 'react-router-dom'
|
StyledCardColComment,
|
||||||
import { useSelector } from 'react-redux'
|
StyledCardHeaderComment,
|
||||||
import { setUserAvatarHash } from '../../state/features/globalSlice'
|
} from "../FileContent/FileContent-styles.tsx";
|
||||||
import { RootState } from '../../state/store'
|
import { Avatar, Box, useTheme } from "@mui/material";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { setUserAvatarHash } from "../../state/features/globalSlice";
|
||||||
|
import { RootState } from "../../state/store";
|
||||||
|
|
||||||
export const IndividualProfile = () => {
|
export const IndividualProfile = () => {
|
||||||
const { name: paramName } = useParams()
|
const { name: paramName } = useParams();
|
||||||
|
|
||||||
const userAvatarHash = useSelector(
|
const userAvatarHash = useSelector(
|
||||||
(state: RootState) => state.global.userAvatarHash
|
(state: RootState) => state.global.userAvatarHash
|
||||||
)
|
);
|
||||||
const theme = useTheme()
|
const theme = useTheme();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const avatarUrl = useMemo(() => {
|
const avatarUrl = useMemo(() => {
|
||||||
let url = ''
|
let url = "";
|
||||||
if (paramName && userAvatarHash[paramName]) {
|
if (paramName && userAvatarHash[paramName]) {
|
||||||
url = userAvatarHash[paramName]
|
url = userAvatarHash[paramName];
|
||||||
}
|
}
|
||||||
|
|
||||||
return url
|
return url;
|
||||||
}, [userAvatarHash, paramName])
|
}, [userAvatarHash, paramName]);
|
||||||
return (
|
return (
|
||||||
<ProfileContainer>
|
<ProfileContainer>
|
||||||
<HeaderContainer>
|
<HeaderContainer>
|
||||||
<Box sx={{
|
<Box
|
||||||
cursor: 'pointer'
|
sx={{
|
||||||
}} >
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<StyledCardHeaderComment
|
<StyledCardHeaderComment
|
||||||
sx={{
|
sx={{
|
||||||
'& .MuiCardHeader-content': {
|
"& .MuiCardHeader-content": {
|
||||||
overflow: 'hidden'
|
overflow: "hidden",
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Avatar src={`/arbitrary/THUMBNAIL/${paramName}/qortal_avatar`} alt={`${paramName}'s avatar`} />
|
<Avatar
|
||||||
|
src={`/arbitrary/THUMBNAIL/${paramName}/qortal_avatar`}
|
||||||
|
alt={`${paramName}'s avatar`}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<StyledCardColComment>
|
<StyledCardColComment>
|
||||||
<AuthorTextComment
|
<AuthorTextComment
|
||||||
color={
|
color={
|
||||||
theme.palette.mode === 'light'
|
theme.palette.mode === "light"
|
||||||
? theme.palette.text.secondary
|
? theme.palette.text.secondary
|
||||||
: '#d6e8ff'
|
: "#d6e8ff"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{paramName}
|
{paramName}
|
||||||
</AuthorTextComment>
|
</AuthorTextComment>
|
||||||
</StyledCardColComment>
|
</StyledCardColComment>
|
||||||
|
|
||||||
</StyledCardHeaderComment>
|
</StyledCardHeaderComment>
|
||||||
</Box>
|
</Box>
|
||||||
</HeaderContainer>
|
</HeaderContainer>
|
||||||
<FileListComponentLevel />
|
<FileListComponentLevel />
|
||||||
</ProfileContainer>
|
</ProfileContainer>
|
||||||
|
);
|
||||||
)
|
};
|
||||||
}
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
import { styled } from "@mui/system";
|
|
||||||
import { Box, Grid, Typography, Checkbox } from "@mui/material";
|
|
||||||
|
|
||||||
export const VideoPlayerContainer = styled(Box)(({ theme }) => ({
|
|
||||||
maxWidth: '95%',
|
|
||||||
width: '1000px',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const VideoTitle = styled(Typography)(({ theme }) => ({
|
|
||||||
fontFamily: "Raleway",
|
|
||||||
fontSize: "20px",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
userSelect: "none",
|
|
||||||
wordBreak: "break-word"
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const VideoDescription = styled(Typography)(({ theme }) => ({
|
|
||||||
fontFamily: "Raleway",
|
|
||||||
fontSize: "16px",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
userSelect: "none",
|
|
||||||
wordBreak: "break-word"
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const Spacer = ({height}: any)=> {
|
|
||||||
return <Box sx={{
|
|
||||||
height: height
|
|
||||||
}} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export const StyledCardHeaderComment = styled(Box)({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
gap: '5px',
|
|
||||||
padding: '7px 0px'
|
|
||||||
})
|
|
||||||
export const StyledCardCol = styled(Box)({
|
|
||||||
display: 'flex',
|
|
||||||
overflow: 'hidden',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '2px',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: '100%'
|
|
||||||
})
|
|
||||||
|
|
||||||
export const StyledCardColComment = styled(Box)({
|
|
||||||
display: 'flex',
|
|
||||||
overflow: 'hidden',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '2px',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: '100%'
|
|
||||||
})
|
|
||||||
|
|
||||||
export const AuthorTextComment = styled(Typography)({
|
|
||||||
fontFamily: 'Raleway, sans-serif',
|
|
||||||
fontSize: '16px',
|
|
||||||
lineHeight: '1.2'
|
|
||||||
})
|
|
||||||
|
|
||||||
export const FileAttachmentContainer = styled(Box)(({ theme }) =>({
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "20px",
|
|
||||||
padding: "5px 10px",
|
|
||||||
border: `1px solid ${theme.palette.text.primary}`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const FileAttachmentFont = styled(Typography)(({ theme }) => ({
|
|
||||||
fontFamily: "Mulish",
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
fontSize: "16px",
|
|
||||||
letterSpacing: 0,
|
|
||||||
fontWeight: 400,
|
|
||||||
userSelect: "none",
|
|
||||||
whiteSpace: 'nowrap'
|
|
||||||
}));
|
|
188
src/state/features/fileSlice.ts
Normal file
188
src/state/features/fileSlice.ts
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
import { RootState } from "../store";
|
||||||
|
|
||||||
|
interface GlobalState {
|
||||||
|
files: Video[];
|
||||||
|
filteredFiles: Video[];
|
||||||
|
hashMapFiles: Record<string, Video>;
|
||||||
|
countNewFiles: number;
|
||||||
|
isFiltering: boolean;
|
||||||
|
filterValue: string;
|
||||||
|
filterType: string;
|
||||||
|
filterSearch: string;
|
||||||
|
filterName: string;
|
||||||
|
selectedCategoryFiles: any[];
|
||||||
|
editFileProperties: any;
|
||||||
|
editPlaylistProperties: any;
|
||||||
|
}
|
||||||
|
const initialState: GlobalState = {
|
||||||
|
files: [],
|
||||||
|
filteredFiles: [],
|
||||||
|
hashMapFiles: {},
|
||||||
|
countNewFiles: 0,
|
||||||
|
isFiltering: false,
|
||||||
|
filterValue: "",
|
||||||
|
filterType: "videos",
|
||||||
|
filterSearch: "",
|
||||||
|
filterName: "",
|
||||||
|
selectedCategoryFiles: [null, null, null, null],
|
||||||
|
editFileProperties: null,
|
||||||
|
editPlaylistProperties: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Video {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
created: number | string;
|
||||||
|
user: string;
|
||||||
|
service?: string;
|
||||||
|
videoImage?: string;
|
||||||
|
id: string;
|
||||||
|
category?: string;
|
||||||
|
categoryName?: string;
|
||||||
|
tags?: string[];
|
||||||
|
updated?: number | string;
|
||||||
|
isValid?: boolean;
|
||||||
|
code?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fileSlice = createSlice({
|
||||||
|
name: "file",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setEditFile: (state, action) => {
|
||||||
|
state.editFileProperties = action.payload;
|
||||||
|
},
|
||||||
|
setEditPlaylist: (state, action) => {
|
||||||
|
state.editPlaylistProperties = action.payload;
|
||||||
|
},
|
||||||
|
changeFilterType: (state, action) => {
|
||||||
|
state.filterType = action.payload;
|
||||||
|
},
|
||||||
|
changefilterSearch: (state, action) => {
|
||||||
|
state.filterSearch = action.payload;
|
||||||
|
},
|
||||||
|
changefilterName: (state, action) => {
|
||||||
|
state.filterName = action.payload;
|
||||||
|
},
|
||||||
|
setCountNewFiles: (state, action) => {
|
||||||
|
state.countNewFiles = action.payload;
|
||||||
|
},
|
||||||
|
addFiles: (state, action) => {
|
||||||
|
state.files = action.payload;
|
||||||
|
},
|
||||||
|
addFilteredFiles: (state, action) => {
|
||||||
|
state.filteredFiles = action.payload;
|
||||||
|
},
|
||||||
|
removeFile: (state, action) => {
|
||||||
|
const idToDelete = action.payload;
|
||||||
|
state.files = state.files.filter(item => item.id !== idToDelete);
|
||||||
|
state.filteredFiles = state.filteredFiles.filter(
|
||||||
|
item => item.id !== idToDelete
|
||||||
|
);
|
||||||
|
},
|
||||||
|
addFileToBeginning: (state, action) => {
|
||||||
|
state.files.unshift(action.payload);
|
||||||
|
},
|
||||||
|
clearFileList: state => {
|
||||||
|
state.files = [];
|
||||||
|
},
|
||||||
|
updateFile: (state, action) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const index = state.files.findIndex(video => video.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
state.files[index] = { ...action.payload };
|
||||||
|
}
|
||||||
|
const index2 = state.filteredFiles.findIndex(video => video.id === id);
|
||||||
|
if (index2 !== -1) {
|
||||||
|
state.filteredFiles[index2] = { ...action.payload };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addToHashMap: (state, action) => {
|
||||||
|
const video = action.payload;
|
||||||
|
state.hashMapFiles[video.id] = video;
|
||||||
|
},
|
||||||
|
updateInHashMap: (state, action) => {
|
||||||
|
const { id } = action.payload;
|
||||||
|
const video = action.payload;
|
||||||
|
state.hashMapFiles[id] = { ...video };
|
||||||
|
},
|
||||||
|
removeFromHashMap: (state, action) => {
|
||||||
|
const idToDelete = action.payload;
|
||||||
|
delete state.hashMapFiles[idToDelete];
|
||||||
|
},
|
||||||
|
addArrayToHashMap: (state, action) => {
|
||||||
|
const videos = action.payload;
|
||||||
|
videos.forEach((video: Video) => {
|
||||||
|
state.hashMapFiles[video.id] = video;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
upsertFiles: (state, action) => {
|
||||||
|
action.payload.forEach((video: Video) => {
|
||||||
|
const index = state.files.findIndex(p => p.id === video.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
state.files[index] = video;
|
||||||
|
} else {
|
||||||
|
state.files.push(video);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
upsertFilteredFiles: (state, action) => {
|
||||||
|
action.payload.forEach((video: Video) => {
|
||||||
|
const index = state.filteredFiles.findIndex(p => p.id === video.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
state.filteredFiles[index] = video;
|
||||||
|
} else {
|
||||||
|
state.filteredFiles.push(video);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
upsertFilesBeginning: (state, action) => {
|
||||||
|
action.payload.reverse().forEach((video: Video) => {
|
||||||
|
const index = state.files.findIndex(p => p.id === video.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
state.files[index] = video;
|
||||||
|
} else {
|
||||||
|
state.files.unshift(video);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setIsFiltering: (state, action) => {
|
||||||
|
state.isFiltering = action.payload;
|
||||||
|
},
|
||||||
|
setFilterValue: (state, action) => {
|
||||||
|
state.filterValue = action.payload;
|
||||||
|
},
|
||||||
|
blockUser: (state, action) => {
|
||||||
|
const username = action.payload;
|
||||||
|
state.files = state.files.filter(item => item.user !== username);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
setCountNewFiles,
|
||||||
|
addFiles,
|
||||||
|
addFilteredFiles,
|
||||||
|
removeFile,
|
||||||
|
addFileToBeginning,
|
||||||
|
updateFile,
|
||||||
|
addToHashMap,
|
||||||
|
updateInHashMap,
|
||||||
|
removeFromHashMap,
|
||||||
|
addArrayToHashMap,
|
||||||
|
upsertFiles,
|
||||||
|
upsertFilteredFiles,
|
||||||
|
upsertFilesBeginning,
|
||||||
|
setIsFiltering,
|
||||||
|
setFilterValue,
|
||||||
|
clearFileList,
|
||||||
|
changeFilterType,
|
||||||
|
changefilterSearch,
|
||||||
|
changefilterName,
|
||||||
|
blockUser,
|
||||||
|
setEditFile,
|
||||||
|
setEditPlaylist,
|
||||||
|
} = fileSlice.actions;
|
||||||
|
|
||||||
|
export default fileSlice.reducer;
|
@ -1,54 +1,68 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit'
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
|
||||||
interface GlobalState {
|
interface GlobalState {
|
||||||
isLoadingGlobal: boolean
|
isLoadingGlobal: boolean;
|
||||||
downloads: any
|
downloads: any;
|
||||||
userAvatarHash: Record<string, string>
|
userAvatarHash: Record<string, string>;
|
||||||
publishNames: string[] | null
|
publishNames: string[] | null;
|
||||||
videoPlaying: any | null
|
videoPlaying: any | null;
|
||||||
|
totalFilesPublished: number;
|
||||||
|
totalNamesPublished: number;
|
||||||
|
filesPerNamePublished: number;
|
||||||
}
|
}
|
||||||
const initialState: GlobalState = {
|
const initialState: GlobalState = {
|
||||||
isLoadingGlobal: false,
|
isLoadingGlobal: false,
|
||||||
downloads: {},
|
downloads: {},
|
||||||
userAvatarHash: {},
|
userAvatarHash: {},
|
||||||
publishNames: null,
|
publishNames: null,
|
||||||
videoPlaying: null
|
videoPlaying: null,
|
||||||
}
|
totalFilesPublished: null,
|
||||||
|
totalNamesPublished: null,
|
||||||
|
filesPerNamePublished: null,
|
||||||
|
};
|
||||||
|
|
||||||
export const globalSlice = createSlice({
|
export const globalSlice = createSlice({
|
||||||
name: 'global',
|
name: "global",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setIsLoadingGlobal: (state, action) => {
|
setIsLoadingGlobal: (state, action) => {
|
||||||
state.isLoadingGlobal = action.payload
|
state.isLoadingGlobal = action.payload;
|
||||||
},
|
},
|
||||||
setAddToDownloads: (state, action) => {
|
setAddToDownloads: (state, action) => {
|
||||||
const download = action.payload
|
const download = action.payload;
|
||||||
state.downloads[download.identifier] = download
|
state.downloads[download.identifier] = download;
|
||||||
},
|
},
|
||||||
updateDownloads: (state, action) => {
|
updateDownloads: (state, action) => {
|
||||||
const { identifier } = action.payload
|
const { identifier } = action.payload;
|
||||||
const download = action.payload
|
const download = action.payload;
|
||||||
state.downloads[identifier] = {
|
state.downloads[identifier] = {
|
||||||
...state.downloads[identifier],
|
...state.downloads[identifier],
|
||||||
...download
|
...download,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
setUserAvatarHash: (state, action) => {
|
setUserAvatarHash: (state, action) => {
|
||||||
const avatar = action.payload
|
const avatar = action.payload;
|
||||||
if (avatar?.name && avatar?.url) {
|
if (avatar?.name && avatar?.url) {
|
||||||
state.userAvatarHash[avatar?.name] = avatar?.url
|
state.userAvatarHash[avatar?.name] = avatar?.url;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addPublishNames: (state, action) => {
|
addPublishNames: (state, action) => {
|
||||||
state.publishNames = action.payload
|
state.publishNames = action.payload;
|
||||||
},
|
},
|
||||||
setVideoPlaying: (state, action) => {
|
setVideoPlaying: (state, action) => {
|
||||||
state.videoPlaying = action.payload
|
state.videoPlaying = action.payload;
|
||||||
},
|
},
|
||||||
}
|
setTotalFilesPublished: (state, action) => {
|
||||||
})
|
state.totalFilesPublished = action.payload;
|
||||||
|
},
|
||||||
|
setTotalNamesPublished: (state, action) => {
|
||||||
|
state.totalNamesPublished = action.payload;
|
||||||
|
},
|
||||||
|
setFilesPerNamePublished: (state, action) => {
|
||||||
|
state.filesPerNamePublished = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
setIsLoadingGlobal,
|
setIsLoadingGlobal,
|
||||||
@ -56,7 +70,10 @@ export const {
|
|||||||
updateDownloads,
|
updateDownloads,
|
||||||
setUserAvatarHash,
|
setUserAvatarHash,
|
||||||
addPublishNames,
|
addPublishNames,
|
||||||
setVideoPlaying
|
setVideoPlaying,
|
||||||
} = globalSlice.actions
|
setTotalFilesPublished,
|
||||||
|
setTotalNamesPublished,
|
||||||
|
setFilesPerNamePublished,
|
||||||
|
} = globalSlice.actions;
|
||||||
|
|
||||||
export default globalSlice.reducer
|
export default globalSlice.reducer;
|
||||||
|
@ -1,216 +0,0 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
import { RootState } from '../store'
|
|
||||||
|
|
||||||
|
|
||||||
interface GlobalState {
|
|
||||||
videos: Video[]
|
|
||||||
filteredVideos: Video[]
|
|
||||||
hashMapVideos: Record<string, Video>
|
|
||||||
countNewVideos: number
|
|
||||||
isFiltering: boolean
|
|
||||||
filterValue: string
|
|
||||||
filterType: string
|
|
||||||
filterSearch: string
|
|
||||||
filterName: string
|
|
||||||
selectedCategoryVideos: any
|
|
||||||
selectedSubCategoryVideos: any
|
|
||||||
selectedSubCategoryVideos2: any
|
|
||||||
selectedSubCategoryVideos3: any
|
|
||||||
editVideoProperties: any
|
|
||||||
editPlaylistProperties: any
|
|
||||||
}
|
|
||||||
const initialState: GlobalState = {
|
|
||||||
videos: [],
|
|
||||||
filteredVideos: [],
|
|
||||||
hashMapVideos: {},
|
|
||||||
countNewVideos: 0,
|
|
||||||
isFiltering: false,
|
|
||||||
filterValue: '',
|
|
||||||
filterType: 'videos',
|
|
||||||
filterSearch: '',
|
|
||||||
filterName: '',
|
|
||||||
selectedCategoryVideos: null,
|
|
||||||
selectedSubCategoryVideos: null,
|
|
||||||
selectedSubCategoryVideos2: null,
|
|
||||||
selectedSubCategoryVideos3: null,
|
|
||||||
editVideoProperties: null,
|
|
||||||
editPlaylistProperties: null
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Video {
|
|
||||||
title: string
|
|
||||||
description: string
|
|
||||||
created: number | string
|
|
||||||
user: string
|
|
||||||
service?: string
|
|
||||||
videoImage?: string
|
|
||||||
id: string
|
|
||||||
category?: string
|
|
||||||
categoryName?: string
|
|
||||||
tags?: string[]
|
|
||||||
updated?: number | string
|
|
||||||
isValid?: boolean
|
|
||||||
code?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const videoSlice = createSlice({
|
|
||||||
name: 'video',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
setEditVideo: (state, action) => {
|
|
||||||
state.editVideoProperties = action.payload
|
|
||||||
},
|
|
||||||
setEditPlaylist: (state, action) => {
|
|
||||||
state.editPlaylistProperties = action.payload
|
|
||||||
},
|
|
||||||
changeFilterType: (state, action) => {
|
|
||||||
state.filterType = action.payload
|
|
||||||
},
|
|
||||||
changefilterSearch: (state, action) => {
|
|
||||||
state.filterSearch = action.payload
|
|
||||||
},
|
|
||||||
changefilterName: (state, action) => {
|
|
||||||
state.filterName = action.payload
|
|
||||||
},
|
|
||||||
changeSelectedCategoryVideos: (state, action) => {
|
|
||||||
state.selectedCategoryVideos = action.payload
|
|
||||||
},
|
|
||||||
changeSelectedSubCategoryVideos: (state, action) => {
|
|
||||||
state.selectedSubCategoryVideos = action.payload
|
|
||||||
},
|
|
||||||
changeSelectedSubCategoryVideos2: (state, action) => {
|
|
||||||
state.selectedSubCategoryVideos2 = action.payload
|
|
||||||
},
|
|
||||||
changeSelectedSubCategoryVideos3: (state, action) => {
|
|
||||||
state.selectedSubCategoryVideos3 = action.payload
|
|
||||||
},
|
|
||||||
setCountNewVideos: (state, action) => {
|
|
||||||
state.countNewVideos = action.payload
|
|
||||||
},
|
|
||||||
addVideos: (state, action) => {
|
|
||||||
state.videos = action.payload
|
|
||||||
},
|
|
||||||
addFilteredVideos: (state, action) => {
|
|
||||||
state.filteredVideos = action.payload
|
|
||||||
},
|
|
||||||
removeVideo: (state, action) => {
|
|
||||||
const idToDelete = action.payload
|
|
||||||
state.videos = state.videos.filter((item) => item.id !== idToDelete)
|
|
||||||
state.filteredVideos = state.filteredVideos.filter(
|
|
||||||
(item) => item.id !== idToDelete
|
|
||||||
)
|
|
||||||
},
|
|
||||||
addVideoToBeginning: (state, action) => {
|
|
||||||
state.videos.unshift(action.payload)
|
|
||||||
},
|
|
||||||
clearVideoList: (state) => {
|
|
||||||
state.videos = []
|
|
||||||
},
|
|
||||||
updateVideo: (state, action) => {
|
|
||||||
const { id } = action.payload
|
|
||||||
const index = state.videos.findIndex((video) => video.id === id)
|
|
||||||
if (index !== -1) {
|
|
||||||
state.videos[index] = { ...action.payload }
|
|
||||||
}
|
|
||||||
const index2 = state.filteredVideos.findIndex((video) => video.id === id)
|
|
||||||
if (index2 !== -1) {
|
|
||||||
state.filteredVideos[index2] = { ...action.payload }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addToHashMap: (state, action) => {
|
|
||||||
const video = action.payload
|
|
||||||
state.hashMapVideos[video.id] = video
|
|
||||||
},
|
|
||||||
updateInHashMap: (state, action) => {
|
|
||||||
const { id } = action.payload
|
|
||||||
const video = action.payload
|
|
||||||
state.hashMapVideos[id] = { ...video }
|
|
||||||
},
|
|
||||||
removeFromHashMap: (state, action) => {
|
|
||||||
const idToDelete = action.payload
|
|
||||||
delete state.hashMapVideos[idToDelete]
|
|
||||||
},
|
|
||||||
addArrayToHashMap: (state, action) => {
|
|
||||||
const videos = action.payload
|
|
||||||
videos.forEach((video: Video) => {
|
|
||||||
state.hashMapVideos[video.id] = video
|
|
||||||
})
|
|
||||||
},
|
|
||||||
upsertVideos: (state, action) => {
|
|
||||||
action.payload.forEach((video: Video) => {
|
|
||||||
const index = state.videos.findIndex((p) => p.id === video.id)
|
|
||||||
if (index !== -1) {
|
|
||||||
state.videos[index] = video
|
|
||||||
} else {
|
|
||||||
state.videos.push(video)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
upsertFilteredVideos: (state, action) => {
|
|
||||||
action.payload.forEach((video: Video) => {
|
|
||||||
const index = state.filteredVideos.findIndex((p) => p.id === video.id)
|
|
||||||
if (index !== -1) {
|
|
||||||
state.filteredVideos[index] = video
|
|
||||||
} else {
|
|
||||||
state.filteredVideos.push(video)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
upsertVideosBeginning: (state, action) => {
|
|
||||||
action.payload.reverse().forEach((video: Video) => {
|
|
||||||
const index = state.videos.findIndex((p) => p.id === video.id)
|
|
||||||
if (index !== -1) {
|
|
||||||
state.videos[index] = video
|
|
||||||
} else {
|
|
||||||
state.videos.unshift(video)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
setIsFiltering: (state, action) => {
|
|
||||||
state.isFiltering = action.payload
|
|
||||||
},
|
|
||||||
setFilterValue: (state, action) => {
|
|
||||||
state.filterValue = action.payload
|
|
||||||
},
|
|
||||||
blockUser: (state, action) => {
|
|
||||||
const username = action.payload
|
|
||||||
|
|
||||||
state.videos = state.videos.filter((item) => item.user !== username)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const {
|
|
||||||
setCountNewVideos,
|
|
||||||
addVideos,
|
|
||||||
addFilteredVideos,
|
|
||||||
removeVideo,
|
|
||||||
addVideoToBeginning,
|
|
||||||
updateVideo,
|
|
||||||
addToHashMap,
|
|
||||||
updateInHashMap,
|
|
||||||
removeFromHashMap,
|
|
||||||
addArrayToHashMap,
|
|
||||||
upsertVideos,
|
|
||||||
upsertFilteredVideos,
|
|
||||||
upsertVideosBeginning,
|
|
||||||
setIsFiltering,
|
|
||||||
setFilterValue,
|
|
||||||
clearVideoList,
|
|
||||||
changeFilterType,
|
|
||||||
changefilterSearch,
|
|
||||||
changefilterName,
|
|
||||||
changeSelectedCategoryVideos,
|
|
||||||
changeSelectedSubCategoryVideos,
|
|
||||||
changeSelectedSubCategoryVideos2,
|
|
||||||
changeSelectedSubCategoryVideos3,
|
|
||||||
blockUser,
|
|
||||||
setEditVideo,
|
|
||||||
setEditPlaylist
|
|
||||||
} = videoSlice.actions
|
|
||||||
|
|
||||||
export default videoSlice.reducer
|
|
||||||
|
|
@ -1,27 +1,27 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit'
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
import notificationsReducer from './features/notificationsSlice'
|
import notificationsReducer from "./features/notificationsSlice";
|
||||||
import authReducer from './features/authSlice'
|
import authReducer from "./features/authSlice";
|
||||||
import globalReducer from './features/globalSlice'
|
import globalReducer from "./features/globalSlice";
|
||||||
import videoReducer from './features/videoSlice'
|
import fileReducer from "./features/fileSlice.ts";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
notifications: notificationsReducer,
|
notifications: notificationsReducer,
|
||||||
auth: authReducer,
|
auth: authReducer,
|
||||||
global: globalReducer,
|
global: globalReducer,
|
||||||
video: videoReducer,
|
file: fileReducer,
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: getDefaultMiddleware =>
|
||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
serializableCheck: false
|
serializableCheck: false,
|
||||||
}),
|
}),
|
||||||
preloadedState: undefined // optional, can be any valid state object
|
preloadedState: undefined, // optional, can be any valid state object
|
||||||
})
|
});
|
||||||
|
|
||||||
// Define the RootState type, which is the type of the entire Redux state tree.
|
// Define the RootState type, which is the type of the entire Redux state tree.
|
||||||
// This is useful when you need to access the state in a component or elsewhere.
|
// This is useful when you need to access the state in a component or elsewhere.
|
||||||
export type RootState = ReturnType<typeof store.getState>
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
|
|
||||||
// Define the AppDispatch type, which is the type of the Redux store's dispatch function.
|
// Define the AppDispatch type, which is the type of the Redux store's dispatch function.
|
||||||
// This is useful when you need to dispatch an action in a component or elsewhere.
|
// This is useful when you need to dispatch an action in a component or elsewhere.
|
||||||
export type AppDispatch = typeof store.dispatch
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
20
src/utils/utilFunctions.ts
Normal file
20
src/utils/utilFunctions.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const objectIsNull = (variable: object) => {
|
||||||
|
return Object.is(variable, null);
|
||||||
|
};
|
||||||
|
export const objectIsUndefined = (variable: object) => {
|
||||||
|
return Object.is(variable, undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const printVar = (variable: object) => {
|
||||||
|
if (objectIsNull(variable)) {
|
||||||
|
console.log("variable is NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (objectIsUndefined(variable)) {
|
||||||
|
console.log("variable is UNDEFINED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [key, value] = Object.entries(variable)[0];
|
||||||
|
console.log(key, " is: ", value);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user