mirror of https://github.com/Qortal/q-share
40 changed files with 3113 additions and 3402 deletions
@ -0,0 +1,10 @@
|
||||
{ |
||||
"printWidth": 80, |
||||
"singleQuote": false, |
||||
"trailingComma": "es5", |
||||
"bracketSpacing": true, |
||||
"jsxBracketSameLine": false, |
||||
"arrowParens": "avoid", |
||||
"tabWidth": 2, |
||||
"semi": true |
||||
} |
After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 21 KiB |
@ -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", |
||||
})); |
@ -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; |
||||
}; |
@ -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 |
||||
} |
@ -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(); |
@ -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" }, |
||||
]; |
@ -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" }, |
||||
]; |
@ -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); |
||||
}; |
@ -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; |
@ -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,77 +1,79 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react' |
||||
import { useNavigate } from 'react-router-dom' |
||||
import { useSelector } from 'react-redux' |
||||
import { RootState } from '../../state/store' |
||||
import React, { useCallback, useEffect, useRef, useState } from "react"; |
||||
import { useNavigate } from "react-router-dom"; |
||||
import { useSelector } from "react-redux"; |
||||
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 { |
||||
Avatar, |
||||
Box, |
||||
Button, |
||||
Typography, |
||||
useTheme |
||||
} from '@mui/material' |
||||
import { useFetchFiles } from '../../hooks/useFetchFiles.tsx' |
||||
import LazyLoad from '../../components/common/LazyLoad' |
||||
import { BottomParent, NameContainer, VideoCard, VideoCardName, VideoCardTitle, VideoContainer, VideoUploadDate } from './FileList-styles.tsx' |
||||
import ResponsiveImage from '../../components/ResponsiveImage' |
||||
import { formatDate, formatTimestampSeconds } from '../../utils/time' |
||||
import { ChannelCard, ChannelTitle } from './Home-styles' |
||||
BottomParent, |
||||
NameContainer, |
||||
VideoCard, |
||||
VideoCardName, |
||||
VideoCardTitle, |
||||
FileContainer, |
||||
VideoUploadDate, |
||||
} from "./FileList-styles.tsx"; |
||||
import ResponsiveImage from "../../components/ResponsiveImage"; |
||||
import { formatDate, formatTimestampSeconds } from "../../utils/time"; |
||||
import { ChannelCard, ChannelTitle } from "./Home-styles"; |
||||
|
||||
interface VideoListProps { |
||||
mode?: string |
||||
mode?: string; |
||||
} |
||||
export const Channels = ({ mode }: VideoListProps) => { |
||||
const theme = useTheme() |
||||
const navigate = useNavigate() |
||||
const publishNames = useSelector((state: RootState)=> state.global.publishNames) |
||||
const theme = useTheme(); |
||||
const navigate = useNavigate(); |
||||
const publishNames = useSelector( |
||||
(state: RootState) => state.global.publishNames |
||||
); |
||||
const userAvatarHash = useSelector( |
||||
(state: RootState) => state.global.userAvatarHash |
||||
) |
||||
|
||||
|
||||
|
||||
); |
||||
|
||||
return ( |
||||
<Box sx={{ |
||||
width: '100%', |
||||
display: 'flex', |
||||
flexDirection: 'column', |
||||
alignItems: 'center', |
||||
minHeight: '50vh' |
||||
}}> |
||||
<VideoContainer> |
||||
{publishNames && publishNames?.slice(0, 10).map((name)=> { |
||||
let avatarUrl = '' |
||||
if(userAvatarHash[name]){ |
||||
avatarUrl = userAvatarHash[name] |
||||
} |
||||
return ( |
||||
<Box |
||||
<Box |
||||
sx={{ |
||||
display: 'flex', |
||||
flex: 0, |
||||
alignItems: 'center', |
||||
width: 'auto', |
||||
position: 'relative', |
||||
' @media (max-width: 450px)': { |
||||
width: '100%' |
||||
} |
||||
width: "100%", |
||||
display: "flex", |
||||
flexDirection: "column", |
||||
alignItems: "center", |
||||
minHeight: "50vh", |
||||
}} |
||||
key={name} |
||||
> |
||||
<ChannelCard |
||||
<FileContainer> |
||||
{publishNames && |
||||
publishNames?.slice(0, 10).map(name => { |
||||
let avatarUrl = ""; |
||||
if (userAvatarHash[name]) { |
||||
avatarUrl = userAvatarHash[name]; |
||||
} |
||||
return ( |
||||
<Box |
||||
sx={{ |
||||
display: "flex", |
||||
flex: 0, |
||||
alignItems: "center", |
||||
width: "auto", |
||||
position: "relative", |
||||
" @media (max-width: 450px)": { |
||||
width: "100%", |
||||
}, |
||||
}} |
||||
key={name} |
||||
> |
||||
<ChannelCard |
||||
onClick={() => { |
||||
navigate(`/channel/${name}`) |
||||
navigate(`/channel/${name}`); |
||||
}} |
||||
> |
||||
<ChannelTitle>{name}</ChannelTitle> |
||||
<ResponsiveImage src={avatarUrl} width={50} height={50}/> |
||||
</ChannelCard> |
||||
</Box> |
||||
) |
||||
})} |
||||
</VideoContainer> |
||||
> |
||||
<ChannelTitle>{name}</ChannelTitle> |
||||
<ResponsiveImage src={avatarUrl} width={50} height={50} /> |
||||
</ChannelCard> |
||||
</Box> |
||||
); |
||||
})} |
||||
</FileContainer> |
||||
</Box> |
||||
) |
||||
} |
||||
|
||||
|
||||
); |
||||
}; |
||||
|
@ -1,15 +1,323 @@
|
||||
import React from 'react' |
||||
import { FileList } from './FileList.tsx' |
||||
import React, { useEffect, useRef, useState } from "react"; |
||||
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' |
||||
import { RootState } from '../../state/store' |
||||
interface HomeProps { |
||||
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 |
||||
); |
||||
|
||||
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]); |
||||
|
||||
export const Home = () => { |
||||
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 ( |
||||
<> |
||||
<FileList /> |
||||
</> |
||||
|
||||
) |
||||
} |
||||
<Grid container sx={{ width: "100%" }}> |
||||
<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 { FileListComponentLevel } from '../Home/FileListComponentLevel.tsx' |
||||
import { HeaderContainer, ProfileContainer } from './Profile-styles' |
||||
import { AuthorTextComment, StyledCardColComment, StyledCardHeaderComment } from '../VideoContent/VideoContent-styles' |
||||
import { Avatar, Box, useTheme } from '@mui/material' |
||||
import { useParams } from 'react-router-dom' |
||||
import { useSelector } from 'react-redux' |
||||
import { setUserAvatarHash } from '../../state/features/globalSlice' |
||||
import { RootState } from '../../state/store' |
||||
import React, { useMemo } from "react"; |
||||
import { FileListComponentLevel } from "../Home/FileListComponentLevel.tsx"; |
||||
import { HeaderContainer, ProfileContainer } from "./Profile-styles"; |
||||
import { |
||||
AuthorTextComment, |
||||
StyledCardColComment, |
||||
StyledCardHeaderComment, |
||||
} from "../FileContent/FileContent-styles.tsx"; |
||||
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 = () => { |
||||
const { name: paramName } = useParams() |
||||
const { name: paramName } = useParams(); |
||||
|
||||
const userAvatarHash = useSelector( |
||||
(state: RootState) => state.global.userAvatarHash |
||||
) |
||||
const theme = useTheme() |
||||
); |
||||
const theme = useTheme(); |
||||
|
||||
|
||||
|
||||
const avatarUrl = useMemo(()=> { |
||||
let url = '' |
||||
if(paramName && userAvatarHash[paramName]){ |
||||
url = userAvatarHash[paramName] |
||||
const avatarUrl = useMemo(() => { |
||||
let url = ""; |
||||
if (paramName && userAvatarHash[paramName]) { |
||||
url = userAvatarHash[paramName]; |
||||
} |
||||
|
||||
return url |
||||
}, [userAvatarHash, paramName]) |
||||
return url; |
||||
}, [userAvatarHash, paramName]); |
||||
return ( |
||||
<ProfileContainer> |
||||
<HeaderContainer> |
||||
<Box sx={{ |
||||
cursor: 'pointer' |
||||
}} > |
||||
<Box |
||||
sx={{ |
||||
cursor: "pointer", |
||||
}} |
||||
> |
||||
<StyledCardHeaderComment |
||||
sx={{ |
||||
'& .MuiCardHeader-content': { |
||||
overflow: 'hidden' |
||||
} |
||||
"& .MuiCardHeader-content": { |
||||
overflow: "hidden", |
||||
}, |
||||
}} |
||||
> |
||||
<Box> |
||||
<Avatar src={`/arbitrary/THUMBNAIL/${paramName}/qortal_avatar`} alt={`${paramName}'s avatar`} /> |
||||
<Avatar |
||||
src={`/arbitrary/THUMBNAIL/${paramName}/qortal_avatar`} |
||||
alt={`${paramName}'s avatar`} |
||||
/> |
||||
</Box> |
||||
<StyledCardColComment> |
||||
<AuthorTextComment |
||||
color={ |
||||
theme.palette.mode === 'light' |
||||
theme.palette.mode === "light" |
||||
? theme.palette.text.secondary |
||||
: '#d6e8ff' |
||||
: "#d6e8ff" |
||||
} |
||||
> |
||||
{paramName} |
||||
</AuthorTextComment> |
||||
</StyledCardColComment> |
||||
|
||||
</StyledCardHeaderComment> |
||||
</Box> |
||||
</HeaderContainer> |
||||
<FileListComponentLevel /> |
||||
</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' |
||||
})); |
@ -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,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 notificationsReducer from './features/notificationsSlice' |
||||
import authReducer from './features/authSlice' |
||||
import globalReducer from './features/globalSlice' |
||||
import videoReducer from './features/videoSlice' |
||||
import { configureStore } from "@reduxjs/toolkit"; |
||||
import notificationsReducer from "./features/notificationsSlice"; |
||||
import authReducer from "./features/authSlice"; |
||||
import globalReducer from "./features/globalSlice"; |
||||
import fileReducer from "./features/fileSlice.ts"; |
||||
|
||||
export const store = configureStore({ |
||||
reducer: { |
||||
notifications: notificationsReducer, |
||||
auth: authReducer, |
||||
global: globalReducer, |
||||
video: videoReducer, |
||||
file: fileReducer, |
||||
}, |
||||
middleware: (getDefaultMiddleware) => |
||||
middleware: 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.
|
||||
// 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.
|
||||
// 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; |
||||
|
@ -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