From 041727aee04619002ffb60efcea446374471d3b8 Mon Sep 17 00:00:00 2001 From: greenflame089 Date: Thu, 24 Jul 2025 16:49:30 +0000 Subject: [PATCH] Upload files to "src/pages/Home" --- src/pages/Home/Home.tsx | 352 +++++++++++++++++++++++++++++++++++ src/pages/Home/IssueList.tsx | 244 ++++++++++++++++++++++++ 2 files changed, 596 insertions(+) create mode 100644 src/pages/Home/Home.tsx create mode 100644 src/pages/Home/IssueList.tsx diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx new file mode 100644 index 0000000..d1e52d1 --- /dev/null +++ b/src/pages/Home/Home.tsx @@ -0,0 +1,352 @@ +import { Box, Grid, Input, useTheme } from "@mui/material"; +import React, { useEffect, useRef, useState } from "react"; +import ReactDOM from "react-dom"; +import { useDispatch, useSelector } from "react-redux"; +import { + AutocompleteQappNames, + getPublishedQappNames, + QappNamesRef, +} from "../../components/common/AutocompleteQappNames.tsx"; +import { + CategoryList, + CategoryListRef, + getCategoriesFetchString, +} from "../../components/common/CategoryList/CategoryList.tsx"; +import { + CategorySelect, + CategorySelectRef, +} from "../../components/common/CategoryList/CategorySelect.tsx"; +import LazyLoad from "../../components/common/LazyLoad"; +import { StatsData } from "../../components/StatsData.tsx"; +import { + allCategories, + allCategoryData, +} from "../../constants/Categories/Categories.ts"; +import { useFetchIssues } from "../../hooks/useFetchIssues.tsx"; +import { + changefilterName, + changefilterSearch, + changeFilterType, + setQappNames, +} from "../../state/features/fileSlice.ts"; +import { RootState } from "../../state/store"; +import { SubtitleContainer, ThemeButton } from "./Home-styles"; +import { FiltersCol, FiltersContainer } from "./IssueList-styles.tsx"; +import { IssueList } from "./IssueList.tsx"; + +interface HomeProps { + mode?: string; +} +export const Home = ({ mode }: HomeProps) => { + const theme = useTheme(); + const isFiltering = useSelector((state: RootState) => state.file.isFiltering); + const filterValue = useSelector((state: RootState) => state.file.filterValue); + const [isLoading, setIsLoading] = useState(false); + const filterType = useSelector((state: RootState) => state.file.filterType); + + const setFilterType = payload => { + dispatch(changeFilterType(payload)); + }; + const filterSearch = useSelector( + (state: RootState) => state.file.filterSearch + ); + const QappNames = useSelector( + (state: RootState) => state.file.publishedQappNames + ); + const autocompleteRef = useRef(null); + + 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 isFetching = useRef(false); + const prevVal = useRef(""); + const categoryListRef = useRef(null); + const categorySelectRef = useRef(null); + + const [showCategoryList, setShowCategoryList] = useState(true); + const [showCategorySelect, setShowCategorySelect] = useState(true); + const { files: globalVideos } = useSelector((state: RootState) => state.file); + + const dispatch = useDispatch(); + const filteredFiles = useSelector( + (state: RootState) => state.file.filteredFiles + ); + + const [QappNamesParam, setQappNamesParam] = useState([]); + + useEffect(() => { + getPublishedQappNames().then(QappNamesResult => { + dispatch(setQappNames(QappNamesResult)); + setQappNamesParam(QappNamesResult); + }); + }, []); + + const { + getIssues, + checkAndUpdateIssue, + getIssue, + hashMapFiles, + getNewIssues, + checkNewIssues, + getIssuesFiltered, + getIssuesCount, + } = useFetchIssues(); + + const getIssuesHandler = React.useCallback( + async (reset?: boolean, resetFilters?: boolean) => { + if (!firstFetch.current || !afterFetch.current) return; + if (isFetching.current) return; + isFetching.current = true; + const selectedCategories = + categoryListRef.current?.getSelectedCategories() || []; + const issueType = categorySelectRef?.current?.getSelectedCategory(); + let categoriesString = getCategoriesFetchString(selectedCategories); + if (issueType) categoriesString = ":" + issueType + ";"; + await getIssues( + { + name: filterName, + categories: categoriesString, + QappName: autocompleteRef?.current?.getQappNameFetchString(), + keywords: filterSearch, + type: filterType, + }, + reset, + resetFilters + ); + isFetching.current = false; + }, + [ + getIssues, + filterValue, + getIssuesFiltered, + isFiltering, + filterName, + filterSearch, + filterType, + ] + ); + + const searchOnEnter = e => { + if (e.keyCode == 13) { + getIssuesHandler(true); + } + }; + + useEffect(() => { + if (isFiltering && filterValue !== prevVal?.current) { + prevVal.current = filterValue; + getIssuesHandler(); + } + }, [filterValue, isFiltering, filteredFiles, getIssuesCount]); + + const getFilesHandlerMount = React.useCallback(async () => { + if (firstFetch.current) return; + firstFetch.current = true; + setIsLoading(true); + + await getIssues(); + afterFetch.current = true; + isFetching.current = false; + + setIsLoading(false); + }, [getIssues]); + + let issues = globalVideos; + + if (isFiltering) { + issues = filteredFiles; + isFilterMode.current = true; + } else { + isFilterMode.current = false; + } + + 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(); + categorySelectRef.current?.clearCategory(); + autocompleteRef.current?.setSelectedValue(null); + ReactDOM.flushSync(() => { + getIssuesHandler(true, true); + }); + }; + + return ( + + + + + { + setFilterSearch(e.target.value); + }} + onKeyDown={searchOnEnter} + value={filterSearch} + placeholder="Search" + sx={{ + color: theme.palette.text.primary, + borderBottom: `1px solid ${theme.palette.text.primary}`, + "&&:before": { + borderBottom: "none", + }, + "&&:after": { + borderBottom: "none", + }, + "&&:hover:before": { + borderBottom: "none", + }, + "&&.Mui-focused:before": { + borderBottom: "none", + }, + "&&.Mui-focused": { + outline: "none", + }, + fontSize: "20px", + }} + /> + { + setFilterName(e.target.value); + }} + onKeyDown={searchOnEnter} + value={filterName} + placeholder="User's Name (Exact)" + sx={{ + marginTop: "20px", + borderBottom: `1px solid ${theme.palette.text.primary}`, + "&&:before": { + borderBottom: "none", + }, + "&&:after": { + borderBottom: "none", + }, + "&&:hover:before": { + borderBottom: "none", + }, + "&&.Mui-focused:before": { + borderBottom: "none", + }, + "&&.Mui-focused": { + outline: "none", + }, + fontSize: "20px", + }} + /> + {showCategoryList && ( + { + setShowCategorySelect(!value[0]); + }} + /> + )} + {showCategorySelect && ( + { + setShowCategoryList(!value); + }} + /> + )} + + {QappNamesParam.length > 0 && ( + { + const currentSelectedCategories = + categoryListRef?.current?.getSelectedCategories(); + categoryListRef?.current?.setSelectedCategories([ + "3", + currentSelectedCategories[1], + currentSelectedCategories[2], + ]); + }} + /> + )} + + { + filtersToDefault(); + }} + sx={{ + marginTop: "20px", + fontWeight: 1000, + }} + variant="contained" + > + reset + + { + getIssuesHandler(true); + }} + sx={{ + marginTop: "20px", + fontWeight: 1000, + }} + variant="contained" + > + Search + + + + + + + + + + + + ); +}; diff --git a/src/pages/Home/IssueList.tsx b/src/pages/Home/IssueList.tsx new file mode 100644 index 0000000..6f20f68 --- /dev/null +++ b/src/pages/Home/IssueList.tsx @@ -0,0 +1,244 @@ +import BlockIcon from "@mui/icons-material/Block"; +import EditIcon from "@mui/icons-material/Edit"; +import { Avatar, Box, Skeleton, useTheme } from "@mui/material"; +import React, { useMemo, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; + +import QORTicon from "../../assets/icons/CoinIcons/qort.png"; +import { BountyDisplay } from "../../components/common/BountyDisplay.tsx"; +import { IssueIcon, IssueIcons } from "../../components/common/IssueIcon.tsx"; +import { + getIconsFromObject, + getnamesFromObject, +} from "../../constants/Categories/CategoryFunctions.ts"; +import { fontSizeExLarge } from "../../constants/Misc.ts"; +import { + blockUser, + Issue, + setEditFile, +} from "../../state/features/fileSlice.ts"; +import { RootState } from "../../state/store.ts"; +import { BountyData } from "../../utils/qortalRequests.ts"; +import { formatDate } from "../../utils/time.ts"; +import { + BlockIconContainer, + IconsBox, + IssueCard, + IssueContainer, + NameAndDateContainer, + VideoCardName, + VideoCardTitle, + VideoUploadDate, +} from "./IssueList-styles.tsx"; + +interface FileListProps { + issues: Issue[]; +} +export const IssueList = ({ issues }: FileListProps) => { + const hashMapIssues = useSelector( + (state: RootState) => state.file.hashMapFiles + ); + const theme = useTheme(); + const [showIcons, setShowIcons] = useState(null); + const username = useSelector((state: RootState) => state.auth?.user?.name); + const dispatch = useDispatch(); + const navigate = useNavigate(); + + const blockUserFunc = async (user: string) => { + if (user === "Q-Support") return; + + try { + const response = await qortalRequest({ + action: "ADD_LIST_ITEMS", + list_name: "blockedNames", + items: [user], + }); + + if (response === true) { + dispatch(blockUser(user)); + } + } catch (error) { + console.log(error); + } + }; + + const filteredIssues = useMemo(() => { + return issues.filter((issue: any) => hashMapIssues[issue.id]?.isValid); + }, [issues, hashMapIssues]); + + return ( + + {filteredIssues.map((issue: any, index: number) => { + const existingFile = hashMapIssues[issue?.id]; + let hasHash = false; + let issueObj = issue; + if (existingFile) { + issueObj = existingFile; + hasHash = true; + } + const bountyData: BountyData = { + ...issueObj.bountyData, + ...issue.bountyData, + }; + + return ( + setShowIcons(issueObj.id)} + onMouseLeave={() => setShowIcons(null)} + > + {hasHash ? ( + <> + + {issueObj?.user === username && ( + { + dispatch(setEditFile(issueObj)); + }} + > + + Edit Issue + + )} + + {issueObj?.user !== username && ( + { + blockUserFunc(issueObj?.user); + }} + > + + Block User + + )} + + { + navigate(`/issue/${issueObj?.user}/${issueObj?.id}`); + }} + sx={{ + height: "100%", + width: "100%", + display: "flex", + flexDirection: "row", + justifyContent: "space-between", + }} + > + +
+ +
+ + + + + {issueObj.title} + +
+ + {issue?.feeData?.isPaid && ( + + )} + + { + e.stopPropagation(); + navigate(`/channel/${issueObj?.user}`); + }} + > +
+ + + {issueObj?.user} + +
+ + {issueObj?.created && ( + + {formatDate(issueObj.created)} + + )} +
+
+ + ) : ( + + )} +
+ ); + })} +
+ ); +};