mirror of
https://github.com/Qortal/q-support.git
synced 2025-02-11 17:55:50 +00:00
Fixed Bug that allowed publishing issue with non-unique title if the title consisted only of characters that are filtered out when sanitized.
More references to Q-Share removed. Issue page has its routing change from /share to /issue Consent modal changed app name from Q-Share to Q-Support User can no longer block themselves Edit and Block buttons in IssueList.tsx no longer have tooltips. Instead, a description is in text visible when the button is loaded to make it easier to see both the button as a whole as well as what it does.
This commit is contained in:
parent
7d700ccc74
commit
92f5c236b6
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Q-Share</title>
|
<title>Q-Support</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
@ -26,7 +26,7 @@ function App() {
|
|||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/share/:name/:id" element={<IssueContent />} />
|
<Route path="/issue/:name/:id" element={<IssueContent />} />
|
||||||
<Route path="/channel/:name" element={<IndividualProfile />} />
|
<Route path="/channel/:name" element={<IndividualProfile />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</GlobalWrapper>
|
</GlobalWrapper>
|
||||||
|
@ -144,7 +144,6 @@ export const EditIssue = () => {
|
|||||||
async function publishQDNResource() {
|
async function publishQDNResource() {
|
||||||
try {
|
try {
|
||||||
const categoryList = categoryListRef.current?.getSelectedCategories();
|
const categoryList = categoryListRef.current?.getSelectedCategories();
|
||||||
if (!title) throw new Error("Please enter a title");
|
|
||||||
if (!description) throw new Error("Please enter a description");
|
if (!description) throw new Error("Please enter a description");
|
||||||
if (!categoryList[0]) throw new Error("Please select a category");
|
if (!categoryList[0]) throw new Error("Please select a category");
|
||||||
if (!editFileProperties) return;
|
if (!editFileProperties) return;
|
||||||
@ -173,10 +172,6 @@ export const EditIssue = () => {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let fileReferences = [];
|
|
||||||
|
|
||||||
let listOfPublishes = [];
|
|
||||||
const fullDescription = extractTextFromHTML(description);
|
|
||||||
|
|
||||||
const sanitizeTitle = title
|
const sanitizeTitle = title
|
||||||
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
||||||
@ -185,6 +180,12 @@ export const EditIssue = () => {
|
|||||||
.trim()
|
.trim()
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
|
||||||
|
if (!sanitizeTitle) throw new Error("Please enter a title");
|
||||||
|
let fileReferences = [];
|
||||||
|
|
||||||
|
let listOfPublishes = [];
|
||||||
|
const fullDescription = extractTextFromHTML(description);
|
||||||
|
|
||||||
for (const publish of files) {
|
for (const publish of files) {
|
||||||
if (publish?.identifier) {
|
if (publish?.identifier) {
|
||||||
fileReferences.push(publish);
|
fileReferences.push(publish);
|
||||||
|
@ -219,7 +219,6 @@ export const EditPlaylist = () => {
|
|||||||
|
|
||||||
async function publishQDNResource() {
|
async function publishQDNResource() {
|
||||||
try {
|
try {
|
||||||
if (!title) throw new Error("Please enter a title");
|
|
||||||
if (!description) throw new Error("Please enter a description");
|
if (!description) throw new Error("Please enter a description");
|
||||||
if (!coverImage) throw new Error("Please select cover image");
|
if (!coverImage) throw new Error("Please select cover image");
|
||||||
if (!selectedCategoryVideos) throw new Error("Please select a category");
|
if (!selectedCategoryVideos) throw new Error("Please select a category");
|
||||||
@ -249,6 +248,16 @@ export const EditPlaylist = () => {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sanitizeTitle = title
|
||||||
|
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
||||||
|
.replace(/\s+/g, "-")
|
||||||
|
.replace(/-+/g, "-")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
if (!sanitizeTitle) throw new Error("Please enter a title");
|
||||||
|
|
||||||
const category = selectedCategoryVideos.id;
|
const category = selectedCategoryVideos.id;
|
||||||
const subcategory = selectedSubCategoryVideos?.id || "";
|
const subcategory = selectedSubCategoryVideos?.id || "";
|
||||||
|
|
||||||
@ -308,12 +317,7 @@ export const EditPlaylist = () => {
|
|||||||
// Description is obtained from raw data
|
// Description is obtained from raw data
|
||||||
|
|
||||||
let identifier = editVideoProperties?.id;
|
let identifier = editVideoProperties?.id;
|
||||||
const sanitizeTitle = title
|
|
||||||
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
|
||||||
.replace(/\s+/g, "-")
|
|
||||||
.replace(/-+/g, "-")
|
|
||||||
.trim()
|
|
||||||
.toLowerCase();
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
identifier = `${QSUPPORT_PLAYLIST_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
|
identifier = `${QSUPPORT_PLAYLIST_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
|
||||||
}
|
}
|
||||||
|
@ -128,8 +128,6 @@ export const PublishIssue = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
try {
|
try {
|
||||||
if (!categoryListRef.current) throw new Error("No CategoryListRef found");
|
if (!categoryListRef.current) throw new Error("No CategoryListRef found");
|
||||||
if (!userAddress) throw new Error("Unable to locate user address");
|
if (!userAddress) throw new Error("Unable to locate user address");
|
||||||
|
|
||||||
if (!title) throw new Error("Please enter a title");
|
|
||||||
if (!description) throw new Error("Please enter a description");
|
if (!description) throw new Error("Please enter a description");
|
||||||
if (!categoryListRef.current?.getSelectedCategories()[0])
|
if (!categoryListRef.current?.getSelectedCategories()[0])
|
||||||
throw new Error("Please select a category");
|
throw new Error("Please select a category");
|
||||||
@ -157,18 +155,19 @@ export const PublishIssue = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileReferences = [];
|
|
||||||
|
|
||||||
let listOfPublishes = [];
|
|
||||||
|
|
||||||
const fullDescription = extractTextFromHTML(description);
|
|
||||||
|
|
||||||
const sanitizeTitle = title
|
const sanitizeTitle = title
|
||||||
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
.replace(/[^a-zA-Z0-9\s-]/g, "")
|
||||||
.replace(/\s+/g, "-")
|
.replace(/\s+/g, "-")
|
||||||
.replace(/-+/g, "-")
|
.replace(/-+/g, "-")
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
|
if (!sanitizeTitle) throw new Error("Please enter a title");
|
||||||
|
|
||||||
|
let fileReferences = [];
|
||||||
|
|
||||||
|
let listOfPublishes = [];
|
||||||
|
|
||||||
|
const fullDescription = extractTextFromHTML(description);
|
||||||
|
|
||||||
for (const publish of files) {
|
for (const publish of files) {
|
||||||
const file = publish.file;
|
const file = publish.file;
|
||||||
|
@ -1,28 +1,24 @@
|
|||||||
import { styled } from '@mui/system';
|
import { styled } from "@mui/system";
|
||||||
import {
|
import { Box, Modal, Typography } from "@mui/material";
|
||||||
Box,
|
|
||||||
Modal,
|
|
||||||
Typography
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
export const StyledModal = styled(Modal)(({ theme }) => ({
|
export const StyledModal = styled(Modal)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: "flex",
|
||||||
alignItems: 'center',
|
alignItems: "center",
|
||||||
justifyContent: 'center'
|
justifyContent: "center",
|
||||||
}))
|
}));
|
||||||
|
|
||||||
export const ModalContent = styled(Box)(({ theme }) => ({
|
export const ModalContent = styled(Box)(({ theme }) => ({
|
||||||
backgroundColor: theme.palette.primary.main,
|
backgroundColor: theme.palette.background.default,
|
||||||
padding: theme.spacing(4),
|
padding: theme.spacing(4),
|
||||||
borderRadius: theme.spacing(1),
|
borderRadius: theme.spacing(1),
|
||||||
width: '40%',
|
width: "40%",
|
||||||
'&:focus': {
|
"&:focus": {
|
||||||
outline: 'none'
|
outline: "none",
|
||||||
}
|
},
|
||||||
}))
|
}));
|
||||||
|
|
||||||
export const ModalText = styled(Typography)(({ theme }) => ({
|
export const ModalText = styled(Typography)(({ theme }) => ({
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "25px",
|
fontSize: "25px",
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
}));
|
}));
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { Button, List, ListItem, Typography, useTheme } from "@mui/material";
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Modal,
|
|
||||||
Typography,
|
|
||||||
SelectChangeEvent,
|
|
||||||
ListItem,
|
|
||||||
List,
|
|
||||||
useTheme
|
|
||||||
} from "@mui/material";
|
|
||||||
import {
|
|
||||||
StyledModal,
|
|
||||||
ModalContent,
|
ModalContent,
|
||||||
ModalText
|
ModalText,
|
||||||
|
StyledModal,
|
||||||
} from "./BlockedNamesModal-styles";
|
} from "./BlockedNamesModal-styles";
|
||||||
|
|
||||||
interface PostModalProps {
|
interface PostModalProps {
|
||||||
@ -22,7 +13,7 @@ interface PostModalProps {
|
|||||||
|
|
||||||
export const BlockedNamesModal: React.FC<PostModalProps> = ({
|
export const BlockedNamesModal: React.FC<PostModalProps> = ({
|
||||||
open,
|
open,
|
||||||
onClose
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const [blockedNames, setBlockedNames] = useState<string[]>([]);
|
const [blockedNames, setBlockedNames] = useState<string[]>([]);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -31,7 +22,7 @@ export const BlockedNamesModal: React.FC<PostModalProps> = ({
|
|||||||
const listName = `blockedNames`;
|
const listName = `blockedNames`;
|
||||||
const response = await qortalRequest({
|
const response = await qortalRequest({
|
||||||
action: "GET_LIST_ITEMS",
|
action: "GET_LIST_ITEMS",
|
||||||
list_name: listName
|
list_name: listName,
|
||||||
});
|
});
|
||||||
setBlockedNames(response);
|
setBlockedNames(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -48,11 +39,11 @@ export const BlockedNamesModal: React.FC<PostModalProps> = ({
|
|||||||
const response = await qortalRequest({
|
const response = await qortalRequest({
|
||||||
action: "DELETE_LIST_ITEM",
|
action: "DELETE_LIST_ITEM",
|
||||||
list_name: "blockedNames",
|
list_name: "blockedNames",
|
||||||
item: name
|
item: name,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response === true) {
|
if (response === true) {
|
||||||
setBlockedNames((prev) => prev.filter((n) => n !== name));
|
setBlockedNames(prev => prev.filter(n => n !== name));
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
@ -67,22 +58,22 @@ export const BlockedNamesModal: React.FC<PostModalProps> = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
flex: "1",
|
flex: "1",
|
||||||
overflow: "auto"
|
overflow: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{blockedNames.map((name, index) => (
|
{blockedNames.map((name, index) => (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={name + index}
|
key={name + index}
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex"
|
display: "flex",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{name}</Typography>
|
<Typography>{name}</Typography>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.primary.light,
|
backgroundColor: theme.palette.primary.main,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
fontFamily: "Raleway"
|
fontFamily: "Raleway",
|
||||||
}}
|
}}
|
||||||
onClick={() => removeFromBlockList(name)}
|
onClick={() => removeFromBlockList(name)}
|
||||||
>
|
>
|
||||||
@ -91,7 +82,15 @@ export const BlockedNamesModal: React.FC<PostModalProps> = ({
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
<Button variant="contained" color="primary" onClick={onClose}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={onClose}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: theme.palette.primary.light,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
fontFamily: "Raleway",
|
||||||
|
}}
|
||||||
|
>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
@ -7,8 +7,9 @@ import DialogContentText from "@mui/material/DialogContentText";
|
|||||||
import DialogTitle from "@mui/material/DialogTitle";
|
import DialogTitle from "@mui/material/DialogTitle";
|
||||||
import localForage from "localforage";
|
import localForage from "localforage";
|
||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material";
|
||||||
|
|
||||||
const generalLocal = localForage.createInstance({
|
const generalLocal = localForage.createInstance({
|
||||||
name: "q-share-general",
|
name: "q-support-general",
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function ConsentModal() {
|
export default function ConsentModal() {
|
||||||
@ -44,13 +45,15 @@ export default function ConsentModal() {
|
|||||||
<DialogTitle id="alert-dialog-title">Welcome</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Welcome</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
Q-Share is currently in its first version and as such there could be
|
Q-Support is currently in its first version and as such there could
|
||||||
some bugs. The Qortal community, along with its development team and
|
be some bugs. The Qortal community, along with its development team
|
||||||
the creators of this application, cannot be held accountable for any
|
and the creators of this application, cannot be held accountable for
|
||||||
content published or displayed. Also, they are not responsible for
|
any content published or displayed. Also, they are not responsible
|
||||||
any loss of coin due to either bad actors or bugs in the
|
for any loss of coin due to either bad actors or bugs in the
|
||||||
application. Furthermore, they bear no responsibility for any data
|
application. Furthermore, they bear no responsibility for any data
|
||||||
loss that may occur as a result of using this application. Finally, they bear no responsibility for any of the content uploaded by users.
|
loss that may occur as a result of using this application. Finally,
|
||||||
|
they bear no responsibility for any of the content uploaded by
|
||||||
|
users.
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
|
||||||
AccordionDetails,
|
|
||||||
AccordionSummary,
|
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
LinearProgress,
|
LinearProgress,
|
||||||
@ -11,30 +8,26 @@ import {
|
|||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
Popover,
|
Popover,
|
||||||
Typography,
|
Typography,
|
||||||
useTheme
|
useTheme,
|
||||||
} from '@mui/material'
|
} from "@mui/material";
|
||||||
import { Movie } from '@mui/icons-material'
|
import { useSelector } from "react-redux";
|
||||||
import { useSelector } from 'react-redux'
|
import { RootState } from "../../state/store";
|
||||||
import { RootState } from '../../state/store'
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
import { DownloadingLight } from "../../assets/svgs/DownloadingLight";
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { DownloadedLight } from "../../assets/svgs/DownloadedLight";
|
||||||
import { DownloadingLight } from '../../assets/svgs/DownloadingLight'
|
|
||||||
import { DownloadedLight } from '../../assets/svgs/DownloadedLight'
|
|
||||||
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
import AttachFileIcon from "@mui/icons-material/AttachFile";
|
||||||
|
|
||||||
export const DownloadTaskManager: React.FC = () => {
|
export const DownloadTaskManager: React.FC = () => {
|
||||||
const { downloads } = useSelector((state: RootState) => state.global)
|
const { downloads } = useSelector((state: RootState) => state.global);
|
||||||
const location = useLocation()
|
const location = useLocation();
|
||||||
const theme = useTheme()
|
const theme = useTheme();
|
||||||
const [visible, setVisible] = useState(false)
|
const [visible, setVisible] = useState(false);
|
||||||
const [hidden, setHidden] = useState(true)
|
const [hidden, setHidden] = useState(true);
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate();
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
|
||||||
const [openDownload, setOpenDownload] = useState<boolean>(false);
|
const [openDownload, setOpenDownload] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
const handleClick = (event?: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = (event?: React.MouseEvent<HTMLDivElement>) => {
|
||||||
const target = event?.currentTarget as unknown as HTMLButtonElement | null;
|
const target = event?.currentTarget as unknown as HTMLButtonElement | null;
|
||||||
setAnchorEl(target);
|
setAnchorEl(target);
|
||||||
@ -50,155 +43,150 @@ export const DownloadTaskManager: React.FC = () => {
|
|||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setHidden(true)
|
setHidden(true);
|
||||||
setVisible(false)
|
setVisible(false);
|
||||||
}, 3000)
|
}, 3000);
|
||||||
}
|
}
|
||||||
}, [visible])
|
}, [visible]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(downloads).length === 0) return
|
if (Object.keys(downloads).length === 0) return;
|
||||||
setVisible(true)
|
setVisible(true);
|
||||||
setHidden(false)
|
setHidden(false);
|
||||||
}, [downloads])
|
}, [downloads]);
|
||||||
|
|
||||||
|
if (!downloads || Object.keys(downloads).length === 0) return null;
|
||||||
|
|
||||||
|
let downloadInProgress = false;
|
||||||
if (
|
if (
|
||||||
!downloads ||
|
Object.keys(downloads).find(
|
||||||
Object.keys(downloads).length === 0
|
key =>
|
||||||
)
|
downloads[key]?.status?.status !== "READY" &&
|
||||||
return null
|
downloads[key]?.status?.status !== "DOWNLOADED"
|
||||||
|
)
|
||||||
|
) {
|
||||||
let downloadInProgress = false
|
downloadInProgress = true;
|
||||||
if(Object.keys(downloads).find((key)=> (downloads[key]?.status?.status !== 'READY' && downloads[key]?.status?.status !== 'DOWNLOADED'))){
|
|
||||||
downloadInProgress = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Button onClick={(e: any) => {
|
<Button
|
||||||
handleClick(e);
|
onClick={(e: any) => {
|
||||||
setOpenDownload(true);
|
handleClick(e);
|
||||||
}}>
|
setOpenDownload(true);
|
||||||
{downloadInProgress ? (
|
}}
|
||||||
<DownloadingLight height='24px' width='24px' className='download-icon' />
|
>
|
||||||
) : (
|
{downloadInProgress ? (
|
||||||
<DownloadedLight height='24px' width='24px' />
|
<DownloadingLight
|
||||||
)}
|
height="24px"
|
||||||
|
width="24px"
|
||||||
</Button>
|
className="download-icon"
|
||||||
|
/>
|
||||||
<Popover
|
) : (
|
||||||
id={"download-popover"}
|
<DownloadedLight height="24px" width="24px" />
|
||||||
open={openDownload}
|
)}
|
||||||
anchorEl={anchorEl}
|
</Button>
|
||||||
onClose={handleCloseDownload}
|
|
||||||
anchorOrigin={{
|
<Popover
|
||||||
vertical: "bottom",
|
id={"download-popover"}
|
||||||
horizontal: "left"
|
open={openDownload}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
onClose={handleCloseDownload}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "left",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<List
|
||||||
|
sx={{
|
||||||
|
maxHeight: "50vh",
|
||||||
|
overflow: "auto",
|
||||||
|
width: "250px",
|
||||||
|
gap: "5px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<List
|
{Object.keys(downloads).map((download: any) => {
|
||||||
sx={{
|
const downloadObj = downloads[download];
|
||||||
maxHeight: '50vh',
|
const progress = downloads[download]?.status?.percentLoaded || 0;
|
||||||
overflow: 'auto',
|
const status = downloads[download]?.status?.status;
|
||||||
width: '250px',
|
const service = downloads[download]?.service;
|
||||||
gap: '5px',
|
return (
|
||||||
display: 'flex',
|
<ListItem
|
||||||
flexDirection: 'column',
|
key={downloadObj?.identifier}
|
||||||
|
sx={{
|
||||||
}}
|
display: "flex",
|
||||||
>
|
flexDirection: "column",
|
||||||
{Object.keys(downloads)
|
width: "100%",
|
||||||
.map((download: any) => {
|
justifyContent: "center",
|
||||||
const downloadObj = downloads[download]
|
background: theme.palette.primary.main,
|
||||||
const progress = downloads[download]?.status?.percentLoaded || 0
|
color: theme.palette.text.primary,
|
||||||
const status = downloads[download]?.status?.status
|
cursor: "pointer",
|
||||||
const service = downloads[download]?.service
|
padding: "2px",
|
||||||
return (
|
}}
|
||||||
<ListItem
|
onClick={() => {
|
||||||
key={downloadObj?.identifier}
|
const id = downloadObj?.properties?.jsonId;
|
||||||
sx={{
|
if (!id) return;
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
width: '100%',
|
|
||||||
justifyContent: 'center',
|
|
||||||
background: theme.palette.primary.main,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '2px',
|
|
||||||
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
const id = downloadObj?.properties?.jsonId
|
|
||||||
if (!id) return
|
|
||||||
|
|
||||||
navigate(
|
|
||||||
`/share/${downloadObj?.properties?.name}/${id}`
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
|
|
||||||
<AttachFileIcon sx={{ color: theme.palette.text.primary }} />
|
|
||||||
|
|
||||||
</ListItemIcon>
|
|
||||||
|
|
||||||
<Box
|
navigate(`/issue/${downloadObj?.properties?.name}/${id}`);
|
||||||
sx={{ width: '100px', marginLeft: 1, marginRight: 1 }}
|
}}
|
||||||
>
|
>
|
||||||
<LinearProgress
|
<Box
|
||||||
variant="determinate"
|
sx={{
|
||||||
value={progress}
|
width: "100%",
|
||||||
sx={{
|
display: "flex",
|
||||||
borderRadius: '5px',
|
alignItems: "center",
|
||||||
color: theme.palette.secondary.main
|
justifyContent: "space-between",
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</Box>
|
<ListItemIcon>
|
||||||
<Typography
|
<AttachFileIcon
|
||||||
sx={{
|
sx={{ color: theme.palette.text.primary }}
|
||||||
fontFamily: 'Arial',
|
/>
|
||||||
color: theme.palette.text.primary
|
</ListItemIcon>
|
||||||
}}
|
|
||||||
variant="caption"
|
<Box sx={{ width: "100px", marginLeft: 1, marginRight: 1 }}>
|
||||||
>
|
<LinearProgress
|
||||||
{`${progress?.toFixed(0)}%`}{' '}
|
variant="determinate"
|
||||||
{status && status === 'REFETCHING' && '- refetching'}
|
value={progress}
|
||||||
{status && status === 'DOWNLOADED' && '- building'}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '10px',
|
borderRadius: "5px",
|
||||||
width: '100%',
|
color: theme.palette.secondary.main,
|
||||||
textAlign: 'end',
|
|
||||||
fontFamily: 'Arial',
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{downloadObj?.identifier}
|
</Box>
|
||||||
</Typography>
|
<Typography
|
||||||
</ListItem>
|
sx={{
|
||||||
)
|
fontFamily: "Arial",
|
||||||
})}
|
color: theme.palette.text.primary,
|
||||||
</List>
|
}}
|
||||||
</Popover>
|
variant="caption"
|
||||||
|
>
|
||||||
|
{`${progress?.toFixed(0)}%`}{" "}
|
||||||
|
{status && status === "REFETCHING" && "- refetching"}
|
||||||
|
{status && status === "DOWNLOADED" && "- building"}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "10px",
|
||||||
|
width: "100%",
|
||||||
|
textAlign: "end",
|
||||||
|
fontFamily: "Arial",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{downloadObj?.identifier}
|
||||||
|
</Typography>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</Popover>
|
||||||
</Box>
|
</Box>
|
||||||
|
);
|
||||||
)
|
};
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const useTestIdentifiers = false;
|
const useTestIdentifiers = true;
|
||||||
|
|
||||||
export const QSUPPORT_FILE_BASE = useTestIdentifiers
|
export const QSUPPORT_FILE_BASE = useTestIdentifiers
|
||||||
? "MYTEST_support_issue_"
|
? "MYTEST_support_issue_"
|
||||||
|
@ -1,21 +1,10 @@
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import { Avatar, Box, Button, Typography, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
import { FileContainer } from "./IssueList-styles.tsx";
|
||||||
import LazyLoad from "../../components/common/LazyLoad";
|
|
||||||
import {
|
|
||||||
BottomParent,
|
|
||||||
NameContainer,
|
|
||||||
VideoCard,
|
|
||||||
VideoCardName,
|
|
||||||
VideoCardTitle,
|
|
||||||
FileContainer,
|
|
||||||
VideoUploadDate,
|
|
||||||
} from "./FileList-styles.tsx";
|
|
||||||
import ResponsiveImage from "../../components/ResponsiveImage";
|
import ResponsiveImage from "../../components/ResponsiveImage";
|
||||||
import { formatDate, formatTimestampSeconds } from "../../utils/time";
|
|
||||||
import { ChannelCard, ChannelTitle } from "./Home-styles";
|
import { ChannelCard, ChannelTitle } from "./Home-styles";
|
||||||
|
|
||||||
interface VideoListProps {
|
interface VideoListProps {
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
VideoCardName,
|
VideoCardName,
|
||||||
VideoCardTitle,
|
VideoCardTitle,
|
||||||
VideoUploadDate,
|
VideoUploadDate,
|
||||||
} from "./FileList-styles.tsx";
|
} from "./IssueList-styles.tsx";
|
||||||
import { formatDate } from "../../utils/time";
|
import { formatDate } from "../../utils/time";
|
||||||
import { Video } from "../../state/features/fileSlice.ts";
|
import { Video } from "../../state/features/fileSlice.ts";
|
||||||
import { queue } from "../../wrappers/GlobalWrapper";
|
import { queue } from "../../wrappers/GlobalWrapper";
|
||||||
@ -149,7 +149,7 @@ export const FileListComponentLevel = ({ mode }: VideoListProps) => {
|
|||||||
<>
|
<>
|
||||||
<VideoCard
|
<VideoCard
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/share/${fileObj?.user}/${fileObj?.id}`);
|
navigate(`/issue/${fileObj?.user}/${fileObj?.id}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
@ -2,11 +2,11 @@ import React, { useEffect, useRef, useState } from "react";
|
|||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import { FileList } from "./FileList.tsx";
|
import { IssueList } from "./IssueList.tsx";
|
||||||
import { Box, Button, Grid, Input, useTheme } from "@mui/material";
|
import { Box, Button, Grid, Input, useTheme } from "@mui/material";
|
||||||
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
import { useFetchFiles } from "../../hooks/useFetchFiles.tsx";
|
||||||
import LazyLoad from "../../components/common/LazyLoad";
|
import LazyLoad from "../../components/common/LazyLoad";
|
||||||
import { FiltersCol, FiltersContainer } from "./FileList-styles.tsx";
|
import { FiltersCol, FiltersContainer } from "./IssueList-styles.tsx";
|
||||||
import { SubtitleContainer } from "./Home-styles";
|
import { SubtitleContainer } from "./Home-styles";
|
||||||
import {
|
import {
|
||||||
changefilterName,
|
changefilterName,
|
||||||
@ -315,7 +315,7 @@ export const Home = ({ mode }: HomeProps) => {
|
|||||||
maxWidth: "1400px",
|
maxWidth: "1400px",
|
||||||
}}
|
}}
|
||||||
></SubtitleContainer>
|
></SubtitleContainer>
|
||||||
<FileList files={videos} />
|
<IssueList files={videos} />
|
||||||
<LazyLoad
|
<LazyLoad
|
||||||
onLoadMore={getFilesHandler}
|
onLoadMore={getFilesHandler}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { styled } from "@mui/system";
|
import { styled } from "@mui/system";
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Grid,
|
|
||||||
Typography,
|
|
||||||
Checkbox,
|
|
||||||
TextField,
|
|
||||||
InputLabel,
|
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
|
Box,
|
||||||
|
Checkbox,
|
||||||
|
Grid,
|
||||||
|
InputLabel,
|
||||||
|
TextField,
|
||||||
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
export const FileContainer = styled(Box)(({ theme }) => ({
|
export const FileContainer = styled(Box)(({ theme }) => ({
|
||||||
@ -283,6 +283,7 @@ export const BlockIconContainer = styled(Box)({
|
|||||||
padding: "2px",
|
padding: "2px",
|
||||||
borderRadius: "3px",
|
borderRadius: "3px",
|
||||||
transition: "all 0.3s ease-in-out",
|
transition: "all 0.3s ease-in-out",
|
||||||
|
fontSize: "18px",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
transform: "scale(1.1)",
|
transform: "scale(1.1)",
|
@ -1,4 +1,4 @@
|
|||||||
import { Avatar, Box, Skeleton, Tooltip } from "@mui/material";
|
import { Avatar, Box, Skeleton } from "@mui/material";
|
||||||
import {
|
import {
|
||||||
BlockIconContainer,
|
BlockIconContainer,
|
||||||
BottomParent,
|
BottomParent,
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
VideoCardName,
|
VideoCardName,
|
||||||
VideoCardTitle,
|
VideoCardTitle,
|
||||||
VideoUploadDate,
|
VideoUploadDate,
|
||||||
} from "./FileList-styles.tsx";
|
} from "./IssueList-styles.tsx";
|
||||||
import EditIcon from "@mui/icons-material/Edit";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import {
|
import {
|
||||||
blockUser,
|
blockUser,
|
||||||
@ -29,7 +29,7 @@ import { getIconsFromObject } from "../../constants/Categories/CategoryFunctions
|
|||||||
interface FileListProps {
|
interface FileListProps {
|
||||||
files: Video[];
|
files: Video[];
|
||||||
}
|
}
|
||||||
export const FileList = ({ files }: FileListProps) => {
|
export const IssueList = ({ files }: FileListProps) => {
|
||||||
const hashMapFiles = useSelector(
|
const hashMapFiles = useSelector(
|
||||||
(state: RootState) => state.file.hashMapFiles
|
(state: RootState) => state.file.hashMapFiles
|
||||||
);
|
);
|
||||||
@ -40,7 +40,7 @@ export const FileList = ({ files }: FileListProps) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const blockUserFunc = async (user: string) => {
|
const blockUserFunc = async (user: string) => {
|
||||||
if (user === "Q-Share") return;
|
if (user === "Q-Support") return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await qortalRequest({
|
const response = await qortalRequest({
|
||||||
@ -88,30 +88,30 @@ export const FileList = ({ files }: FileListProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{fileObj?.user === username && (
|
{fileObj?.user === username && (
|
||||||
<Tooltip title="Edit Issue Properties" placement="top">
|
<BlockIconContainer
|
||||||
<BlockIconContainer>
|
onClick={() => {
|
||||||
<EditIcon
|
dispatch(setEditFile(fileObj));
|
||||||
onClick={() => {
|
}}
|
||||||
dispatch(setEditFile(fileObj));
|
>
|
||||||
}}
|
<EditIcon />
|
||||||
/>
|
Edit Issue
|
||||||
</BlockIconContainer>
|
</BlockIconContainer>
|
||||||
</Tooltip>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Tooltip title="Block user content" placement="top">
|
{fileObj?.user !== username && (
|
||||||
<BlockIconContainer>
|
<BlockIconContainer
|
||||||
<BlockIcon
|
onClick={() => {
|
||||||
onClick={() => {
|
blockUserFunc(fileObj?.user);
|
||||||
blockUserFunc(fileObj?.user);
|
}}
|
||||||
}}
|
>
|
||||||
/>
|
<BlockIcon />
|
||||||
|
Block User
|
||||||
</BlockIconContainer>
|
</BlockIconContainer>
|
||||||
</Tooltip>
|
)}
|
||||||
</IconsBox>
|
</IconsBox>
|
||||||
<VideoCard
|
<VideoCard
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/share/${fileObj?.user}/${fileObj?.id}`);
|
navigate(`/issue/${fileObj?.user}/${fileObj?.id}`);
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
height: "100%",
|
height: "100%",
|
Loading…
x
Reference in New Issue
Block a user