mirror of https://github.com/Qortal/q-share
Browse Source
Many new categories added, All categories have an "Other" field that is sorted last Media category removed, its subcategories are now main categories. WARNING: This is a breaking change that will disrupt categories for some currently published files Categories associated with Copyright Infringement have been removed. Categories are sorted by name, "Other" is always last The FileList component now only stores the file list. The rest of it was moved to Home.tsx Category <select> components moved into a CategoryList.tsx component Icons now load before file title in FileContent.tsx Icons are an optional field of each category and loaded dynamically from Category Data to improve performancepull/2/head
Qortal Dev
6 months ago
40 changed files with 3131 additions and 3382 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