Files
q-support/src/pages/Home/IssueList.tsx
Qortal Seth f53ac4bad4 Added features from Q-Support 1.1.0 posted on Q-Share: (#9)
Bounties can be added to an Issue. They can be either a direct payment from the publisher in ANY supported coin, or they can be a link to a Q-Fund.

Q-Funds that are still in progress have a donate button so users can support it without having to leave Q-Support and open the Q-Fund.

Any category can be searched for individually using the "Single Category" Combobox.

user can add source code to their Issue, so it is easier to see.

IssueIcons have a tooltip that displays the name of its category. If the category is Q-Apps/Websites, then the icon of its owner will also be displayed.
2024-06-14 15:36:06 -06:00

245 lines
7.7 KiB
TypeScript

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 (
<IssueContainer>
{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 (
<Box
sx={{
display: "flex",
alignItems: "center",
width: "100%",
height: "75px",
position: "relative",
}}
key={issueObj.id}
onMouseEnter={() => setShowIcons(issueObj.id)}
onMouseLeave={() => setShowIcons(null)}
>
{hasHash ? (
<>
<IconsBox
sx={{
opacity: showIcons === issueObj.id ? 1 : 0,
zIndex: 2,
}}
>
{issueObj?.user === username && (
<BlockIconContainer
onClick={() => {
dispatch(setEditFile(issueObj));
}}
>
<EditIcon />
Edit Issue
</BlockIconContainer>
)}
{issueObj?.user !== username && (
<BlockIconContainer
onClick={() => {
blockUserFunc(issueObj?.user);
}}
>
<BlockIcon />
Block User
</BlockIconContainer>
)}
</IconsBox>
<IssueCard
onClick={() => {
navigate(`/issue/${issueObj?.user}/${issueObj?.id}`);
}}
sx={{
height: "100%",
width: "100%",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
width: "280px",
}}
>
<IssueIcons
issueData={issueObj}
style={{ marginRight: "20px" }}
showBackupIcon={true}
/>
</div>
<Box
sx={{
display: "flex",
justifyContent: "left",
alignItems: "center",
width: "250px",
fontSize: fontSizeExLarge,
fontFamily: "Cairo",
letterSpacing: "0.4px",
color: theme.palette.text.primary,
userSelect: "none",
}}
>
<BountyDisplay
bountyData={bountyData}
divStyle={{ marginLeft: "20px" }}
/>
</Box>
<VideoCardTitle sx={{ fontWeight: "bold", width: "400px" }}>
{issueObj.title}
</VideoCardTitle>
</Box>
{issue?.feeData?.isPaid && (
<IssueIcon
iconSrc={QORTicon}
style={{ marginRight: "20px" }}
/>
)}
<NameAndDateContainer
sx={{ width: "200px", height: "100%" }}
onClick={e => {
e.stopPropagation();
navigate(`/channel/${issueObj?.user}`);
}}
>
<div
style={{
display: "flex",
width: "200px",
}}
>
<Avatar
sx={{ height: 24, width: 24, marginRight: "10px" }}
src={`/arbitrary/THUMBNAIL/${issueObj?.user}/qortal_avatar`}
alt={`${issueObj?.user}'s avatar`}
/>
<VideoCardName
sx={{
":hover": {
textDecoration: "underline",
},
}}
>
{issueObj?.user}
</VideoCardName>
</div>
{issueObj?.created && (
<VideoUploadDate>
{formatDate(issueObj.created)}
</VideoUploadDate>
)}
</NameAndDateContainer>
</IssueCard>
</>
) : (
<Skeleton
variant="rectangular"
style={{
width: "100%",
height: "100%",
paddingBottom: "10px",
objectFit: "contain",
visibility: "visible",
borderRadius: "8px",
}}
/>
)}
</Box>
);
})}
</IssueContainer>
);
};