3
0
mirror of https://github.com/Qortal/q-support.git synced 2025-02-11 17:55:50 +00:00

First Commit

Q-Support is forked from Q-Share and has the following changes from it:

Changed Q-Share Categories and Identifiers to Q-Support Categories and Identifiers.

Theme updated with main colors being the same as Qortal logo.

Added Support Icon

StatsData component only displays if published posts

Full Publish form shown before choosing file

User doesn't input Issue State Category in Publish Form because it is always open by default

Publishing File is Optional in publish form

User can add multiple images in PublishFile.tsx
This commit is contained in:
Qortal Dev 2024-04-15 16:15:07 -06:00
parent a3bce74ad8
commit da9bc5ab59
44 changed files with 1431 additions and 1057 deletions

854
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "qtube",
"name": "qsupport",
"private": true,
"version": "0.0.0",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
@ -43,6 +43,6 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4",
"typescript": "^5.0.2",
"vite": "^4.3.2"
"vite": "6.0.0-alpha.1"
}
}

View File

@ -1,14 +1,14 @@
import { useState } from "react";
import { Routes, Route } from "react-router-dom";
import { Route, Routes } from "react-router-dom";
import { ThemeProvider } from "@mui/material/styles";
import { CssBaseline } from "@mui/material";
import { lightTheme, darkTheme } from "./styles/theme";
import { darkTheme, lightTheme } from "./styles/theme";
import { store } from "./state/store";
import { Provider } from "react-redux";
import GlobalWrapper from "./wrappers/GlobalWrapper";
import Notification from "./components/common/Notification/Notification";
import { Home } from "./pages/Home/Home";
import { FileContent } from "./pages/FileContent/FileContent.tsx";
import { IssueContent } from "./pages/IssueContent/IssueContent.tsx";
import DownloadWrapper from "./wrappers/DownloadWrapper";
import { IndividualProfile } from "./pages/IndividualProfile/IndividualProfile";
@ -26,7 +26,7 @@ function App() {
<CssBaseline />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/share/:name/:id" element={<FileContent />} />
<Route path="/share/:name/:id" element={<IssueContent />} />
<Route path="/channel/:name" element={<IndividualProfile />} />
</Routes>
</GlobalWrapper>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -21,7 +21,7 @@ import {
updateFile,
updateInHashMap,
} from "../../state/features/fileSlice.ts";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { QSUPPORT_FILE_BASE } from "../../constants/Identifiers.ts";
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
import { TextEditor } from "../common/TextEditor/TextEditor";
import { extractTextFromHTML } from "../common/TextEditor/utils";
@ -32,6 +32,10 @@ import {
CategoryListRef,
getCategoriesFromObject,
} from "../common/CategoryList/CategoryList.tsx";
import {
ImagePublisher,
ImagePublisherRef,
} from "../common/ImagePublisher/ImagePublisher.tsx";
const uid = new ShortUniqueId();
const shortuid = new ShortUniqueId({ length: 5 });
@ -53,7 +57,7 @@ interface VideoFile {
identifier?: string;
filename?: string;
}
export const EditFile = () => {
export const EditIssue = () => {
const theme = useTheme();
const dispatch = useDispatch();
const username = useSelector((state: RootState) => state.auth?.user?.name);
@ -75,6 +79,7 @@ export const EditFile = () => {
const [files, setFiles] = useState<VideoFile[]>([]);
const [editCategories, setEditCategories] = useState<string[]>([]);
const categoryListRef = useRef<CategoryListRef>(null);
const imagePublisherRef = useRef<ImagePublisherRef>(null);
const { getRootProps, getInputProps } = useDropzone({
maxFiles: 10,
@ -121,7 +126,10 @@ export const EditFile = () => {
const paragraph = `<p>${editFileProperties?.fullDescription}</p>`;
setDescription(paragraph);
}
setEditCategories(getCategoriesFromObject(editFileProperties));
const categoriesFromEditFile =
getCategoriesFromObject(editFileProperties);
setEditCategories(categoriesFromEditFile);
}
}, [editFileProperties]);
const onClose = () => {
@ -141,7 +149,6 @@ export const EditFile = () => {
if (!categoryList[0]) throw new Error("Please select a category");
if (!editFileProperties) return;
if (!userAddress) throw new Error("Unable to locate user address");
if (files.length === 0) throw new Error("Add at least one file");
let errorMsg = "";
let name = "";
@ -186,7 +193,7 @@ export const EditFile = () => {
const file = publish.file;
const id = uid();
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
const identifier = `${QSUPPORT_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
let fileExtension = "";
const fileExtensionSplit = file?.name?.split(".");
@ -227,7 +234,7 @@ export const EditFile = () => {
description: metadescription,
identifier,
filename,
tag1: QSHARE_FILE_BASE,
tag1: QSUPPORT_FILE_BASE,
};
listOfPublishes.push(requestBodyVideo);
fileReferences.push({
@ -248,24 +255,25 @@ export const EditFile = () => {
commentsId: editFileProperties.commentsId,
...categoryListRef.current?.categoriesToObject(),
files: fileReferences,
images: imagePublisherRef?.current?.getImageArray(),
};
let metadescription =
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
fullDescription.slice(0, 150);
const crowdfundObjectToBase64 = await objectToBase64(fileObject);
const fileObjectToBase64 = await objectToBase64(fileObject);
// Description is obtained from raw data
const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE",
name: name,
service: "DOCUMENT",
data64: crowdfundObjectToBase64,
data64: fileObjectToBase64,
title: title.slice(0, 50),
description: metadescription,
identifier: editFileProperties.id,
tag1: QSHARE_FILE_BASE,
tag1: QSUPPORT_FILE_BASE,
filename: `video_metadata.json`,
};
listOfPublishes.push(requestBodyJson);
@ -336,7 +344,7 @@ export const EditFile = () => {
justifyContent: "space-between",
}}
>
<NewCrowdfundTitle>Update share</NewCrowdfundTitle>
<NewCrowdfundTitle>Update Issue</NewCrowdfundTitle>
</Box>
<>
<Box
@ -390,8 +398,6 @@ export const EditFile = () => {
alignItems: "flex-start",
}}
>
{files?.length > 0 && (
<>
<Box
sx={{
display: "flex",
@ -407,14 +413,14 @@ export const EditFile = () => {
ref={categoryListRef}
/>
</Box>
</>
)}
</Box>
{files?.length > 0 && (
<>
<ImagePublisher
ref={imagePublisherRef}
initialImages={editFileProperties?.images}
/>
<CustomInputField
name="title"
label="Title of share"
label="Title of Issue"
variant="filled"
value={title}
onChange={e => {
@ -430,7 +436,7 @@ export const EditFile = () => {
fontSize: "18px",
}}
>
Description of share
Description of Issue
</Typography>
<TextEditor
inlineContent={description}
@ -439,8 +445,6 @@ export const EditFile = () => {
}}
/>
</>
)}
</>
<CrowdfundActionButtonRow>
<CrowdfundActionButton

View File

@ -6,11 +6,9 @@ import {
CrowdfundActionButton,
CrowdfundActionButtonRow,
CustomInputField,
CustomSelect,
LogoPreviewRow,
ModalBody,
NewCrowdfundTitle,
StyledButton,
TimesIcon,
} from "./Upload-styles.tsx";
import {
@ -27,27 +25,20 @@ import {
} from "@mui/material";
import ShortUniqueId from "short-unique-id";
import { useDispatch, useSelector } from "react-redux";
import AddBoxIcon from "@mui/icons-material/AddBox";
import { useDropzone } from "react-dropzone";
import { setNotification } from "../../state/features/notificationsSlice";
import { objectToBase64, uint8ArrayToBase64 } from "../../utils/toBase64";
import { objectToBase64 } from "../../utils/toBase64";
import { RootState } from "../../state/store";
import {
upsertFilesBeginning,
addToHashMap,
upsertFiles,
setEditFile,
setEditPlaylist,
updateFile,
updateInHashMap,
setEditPlaylist,
} from "../../state/features/fileSlice.ts";
import ImageUploader from "../common/ImageUploader";
import ImageUploader from "../common/ImagePublisher/ImageUploader.tsx";
import {
QSHARE_PLAYLIST_BASE,
QSHARE_FILE_BASE,
QSUPPORT_FILE_BASE,
QSUPPORT_PLAYLIST_BASE,
} from "../../constants/Identifiers.ts";
import { Playlists } from "../Playlists/Playlists";
import { PlaylistListEdit } from "../PlaylistListEdit/PlaylistListEdit";
import { TextEditor } from "../common/TextEditor/TextEditor";
import { extractTextFromHTML } from "../common/TextEditor/utils";
@ -87,7 +78,7 @@ export const EditPlaylist = () => {
const [playlistData, setPlaylistData] = useState<any>(null);
const [title, setTitle] = useState<string>("");
const [description, setDescription] = useState<string>("");
const [coverImage, setCoverImage] = useState<string>("");
const [coverImage, setCoverImage] = useState<string[]>([]);
const [videos, setVideos] = useState([]);
const [selectedCategoryVideos, setSelectedCategoryVideos] =
useState<any>(null);
@ -222,7 +213,7 @@ export const EditPlaylist = () => {
setPlaylistData(null);
setSelectedCategoryVideos(null);
setSelectedSubCategoryVideos(null);
setCoverImage("");
setCoverImage([]);
dispatch(setEditPlaylist(null));
};
@ -292,7 +283,7 @@ export const EditPlaylist = () => {
let commentsId = editVideoProperties?.id;
if (isNew) {
commentsId = `${QSHARE_PLAYLIST_BASE}_cm_${id}`;
commentsId = `${QSUPPORT_PLAYLIST_BASE}_cm_${id}`;
}
const stringDescription = extractTextFromHTML(description);
@ -324,7 +315,7 @@ export const EditPlaylist = () => {
.trim()
.toLowerCase();
if (isNew) {
identifier = `${QSHARE_PLAYLIST_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
identifier = `${QSUPPORT_PLAYLIST_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
}
const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE",
@ -334,7 +325,7 @@ export const EditPlaylist = () => {
title: title.slice(0, 50),
description: metadescription,
identifier: identifier,
tag1: QSHARE_FILE_BASE,
tag1: QSUPPORT_FILE_BASE,
};
await qortalRequest(requestBodyJson);
@ -519,7 +510,7 @@ export const EditPlaylist = () => {
</Box>
<React.Fragment>
{!coverImage ? (
<ImageUploader onPick={(img: string) => setCoverImage(img)}>
<ImageUploader onPick={(img: string[]) => setCoverImage(img)}>
<AddCoverImageButton variant="contained">
Add Cover Image
<AddLogoIcon
@ -532,10 +523,15 @@ export const EditPlaylist = () => {
</ImageUploader>
) : (
<LogoPreviewRow>
<CoverImagePreview src={coverImage} alt="logo" />
{coverImage.map(
image =>
image && (
<CoverImagePreview src={image} alt="logo" key={image} />
)
)}
<TimesIcon
color={theme.palette.text.primary}
onClickFunc={() => setCoverImage("")}
onClickFunc={() => setCoverImage([])}
height={"32"}
width={"32"}
></TimesIcon>

View File

@ -3,15 +3,15 @@ import { CardContentContainerComment } from "../common/Comments/Comments-styles"
import {
CrowdfundSubTitle,
CrowdfundSubTitleRow,
} from "../PublishFile/Upload-styles.tsx";
} from "../PublishIssue/Upload-styles.tsx";
import { Box, Button, Input, Typography, useTheme } from "@mui/material";
import { useNavigate } from "react-router-dom";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { removeFile } from "../../state/features/fileSlice.ts";
import AddIcon from "@mui/icons-material/Add";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { QSUPPORT_FILE_BASE } from "../../constants/Identifiers.ts";
import { useSelector } from "react-redux";
import { RootState } from "../../state/store";
export const PlaylistListEdit = ({ playlistData, removeVideo, addVideo }) => {
const theme = useTheme();
const navigate = useNavigate();
@ -20,7 +20,7 @@ export const PlaylistListEdit = ({ playlistData, removeVideo, addVideo }) => {
const [searchResults, setSearchResults] = useState([]);
const [filterSearch, setFilterSearch] = useState("");
const search = async () => {
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&mode=ALL&identifier=${QSHARE_FILE_BASE}&title=${filterSearch}&limit=20&includemetadata=true&reverse=true&name=${username}&exactmatchnames=true&offset=0`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&mode=ALL&identifier=${QSUPPORT_FILE_BASE}&title=${filterSearch}&limit=20&includemetadata=true&reverse=true&name=${username}&exactmatchnames=true&offset=0`;
const response = await fetch(url, {
method: "GET",
headers: {

View File

@ -1,66 +1,79 @@
import React from 'react'
import { CardContentContainerComment } from '../common/Comments/Comments-styles'
import { CrowdfundSubTitle, CrowdfundSubTitleRow } from '../PublishFile/Upload-styles.tsx'
import { Box, Typography, useTheme } from '@mui/material'
import { useNavigate } from 'react-router-dom'
import React from "react";
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
import {
CrowdfundSubTitle,
CrowdfundSubTitleRow,
} from "../PublishIssue/Upload-styles.tsx";
import { Box, Typography, useTheme } from "@mui/material";
import { useNavigate } from "react-router-dom";
export const Playlists = ({playlistData, currentVideoIdentifier}) => {
export const Playlists = ({ playlistData, currentVideoIdentifier }) => {
const theme = useTheme();
const navigate = useNavigate()
const navigate = useNavigate();
return (
<Box sx={{
display: 'flex',
flexDirection: 'column',
<Box
sx={{
display: "flex",
flexDirection: "column",
maxWidth: '400px',
width: '100%'
}}>
<CrowdfundSubTitleRow >
<CrowdfundSubTitle>Playlist</CrowdfundSubTitle>
</CrowdfundSubTitleRow>
<CardContentContainerComment sx={{
marginTop: '25px',
height: '450px',
overflow: 'auto'
}}>
{playlistData?.videos?.map((vid, index)=> {
const isCurrentVidPlayling = vid?.identifier === currentVideoIdentifier;
return (
<Box key={vid?.identifier} sx={{
display: 'flex',
gap: '10px',
width: '100%',
background: isCurrentVidPlayling && theme.palette.primary.main,
alignItems: 'center',
padding: '10px',
borderRadius: '5px',
cursor: isCurrentVidPlayling ? 'default' : 'pointer',
userSelect: 'none'
}}
onClick={()=> {
if(isCurrentVidPlayling) return
navigate(`/video/${vid.name}/${vid.identifier}`)
maxWidth: "400px",
width: "100%",
}}
>
<Typography sx={{
fontSize: '14px'
}}>{index + 1}</Typography>
<Typography sx={{
fontSize: '18px',
wordBreak: 'break-word'
}}>{vid?.metadata?.title}</Typography>
<CrowdfundSubTitleRow>
<CrowdfundSubTitle>Playlist</CrowdfundSubTitle>
</CrowdfundSubTitleRow>
<CardContentContainerComment
sx={{
marginTop: "25px",
height: "450px",
overflow: "auto",
}}
>
{playlistData?.videos?.map((vid, index) => {
const isCurrentVidPlayling =
vid?.identifier === currentVideoIdentifier;
return (
<Box
key={vid?.identifier}
sx={{
display: "flex",
gap: "10px",
width: "100%",
background: isCurrentVidPlayling && theme.palette.primary.main,
alignItems: "center",
padding: "10px",
borderRadius: "5px",
cursor: isCurrentVidPlayling ? "default" : "pointer",
userSelect: "none",
}}
onClick={() => {
if (isCurrentVidPlayling) return;
navigate(`/video/${vid.name}/${vid.identifier}`);
}}
>
<Typography
sx={{
fontSize: "14px",
}}
>
{index + 1}
</Typography>
<Typography
sx={{
fontSize: "18px",
wordBreak: "break-word",
}}
>
{vid?.metadata?.title}
</Typography>
</Box>
)
);
})}
</CardContentContainerComment>
</Box>
)
}
);
};

View File

@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from "react";
import {
CrowdfundActionButton,
CrowdfundActionButtonRow,
ActionButton,
ActionButtonRow,
CustomInputField,
ModalBody,
NewCrowdfundTitle,
@ -17,16 +17,22 @@ import { useDropzone } from "react-dropzone";
import { setNotification } from "../../state/features/notificationsSlice";
import { objectToBase64 } from "../../utils/toBase64";
import { RootState } from "../../state/store";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { QSUPPORT_FILE_BASE } from "../../constants/Identifiers.ts";
import { MultiplePublish } from "../common/MultiplePublish/MultiplePublishAll";
import { TextEditor } from "../common/TextEditor/TextEditor";
import { extractTextFromHTML } from "../common/TextEditor/utils";
import { allCategoryData } from "../../constants/Categories/1stCategories.ts";
import { titleFormatter } from "../../constants/Misc.ts";
import {
appendCategoryToList,
CategoryList,
CategoryListRef,
} from "../common/CategoryList/CategoryList.tsx";
import { SupportState } from "../../constants/Categories/2ndCategories.ts";
import {
ImagePublisher,
ImagePublisherRef,
} from "../common/ImagePublisher/ImagePublisher.tsx";
const uid = new ShortUniqueId();
const shortuid = new ShortUniqueId({ length: 5 });
@ -46,7 +52,7 @@ interface VideoFile {
description: string;
coverImage?: string;
}
export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
export const PublishIssue = ({ editId, editContent }: NewCrowdfundProps) => {
const theme = useTheme();
const dispatch = useDispatch();
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
@ -73,7 +79,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
const [playlistSetting, setPlaylistSetting] = useState<null | string>(null);
const [publishes, setPublishes] = useState<any>(null);
const categoryListRef = useRef<CategoryListRef>(null);
const imagePublisherRef = useRef<ImagePublisherRef>(null);
const { getRootProps, getInputProps } = useDropzone({
maxFiles: 10,
maxSize: 419430400, // 400 MB in bytes
@ -127,7 +133,6 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
if (!description) throw new Error("Please enter a description");
if (!categoryListRef.current?.getSelectedCategories()[0])
throw new Error("Please select a category");
if (files.length === 0) throw new Error("Add at least one file");
let errorMsg = "";
let name = "";
if (username) {
@ -169,7 +174,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
const file = publish.file;
const id = uid();
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
const identifier = `${QSUPPORT_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${id}`;
let fileExtension = "";
const fileExtensionSplit = file?.name?.split(".");
@ -197,9 +202,12 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
filename = alphanumericString;
}
let metadescription =
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
fullDescription.slice(0, 150);
const categoryList = appendCategoryToList(
categoryListRef.current?.getSelectedCategories(),
"101"
);
const categoryString = `**${categoryListRef.current?.getCategoriesFetchString(categoryList)}**`;
let metadescription = categoryString + fullDescription.slice(0, 150);
const requestBodyVideo: any = {
action: "PUBLISH_QDN_RESOURCE",
@ -210,7 +218,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
description: metadescription,
identifier,
filename,
tag1: QSHARE_FILE_BASE,
tag1: QSUPPORT_FILE_BASE,
};
listOfPublishes.push(requestBodyVideo);
fileReferences.push({
@ -224,32 +232,38 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
}
const idMeta = uid();
const identifier = `${QSHARE_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${idMeta}`;
const identifier = `${QSUPPORT_FILE_BASE}${sanitizeTitle.slice(0, 30)}_${idMeta}`;
const categoryList = appendCategoryToList(
categoryListRef.current?.getSelectedCategories(),
"101"
);
const fileObject: any = {
title,
version: 1,
fullDescription,
htmlDescription: description,
commentsId: `${QSHARE_FILE_BASE}_cm_${idMeta}`,
...categoryListRef.current?.categoriesToObject(),
commentsId: `${QSUPPORT_FILE_BASE}_cm_${idMeta}`,
...categoryListRef.current?.categoriesToObject(categoryList),
files: fileReferences,
images: imagePublisherRef?.current?.getImageArray(),
};
let metadescription =
`**${categoryListRef.current?.getCategoriesFetchString()}**` +
fullDescription.slice(0, 150);
const categoryString = `**${categoryListRef.current?.getCategoriesFetchString(categoryList)}**`;
let metadescription = categoryString + fullDescription.slice(0, 150);
const crowdfundObjectToBase64 = await objectToBase64(fileObject);
const fileObjectToBase64 = await objectToBase64(fileObject);
// Description is obtained from raw data
const requestBodyJson: any = {
action: "PUBLISH_QDN_RESOURCE",
name: name,
service: "DOCUMENT",
data64: crowdfundObjectToBase64,
data64: fileObjectToBase64,
title: title.slice(0, 50),
description: metadescription,
identifier: identifier + "_metadata",
tag1: QSHARE_FILE_BASE,
tag1: QSUPPORT_FILE_BASE,
filename: `video_metadata.json`,
};
listOfPublishes.push(requestBodyJson);
@ -264,17 +278,17 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
let notificationObj: any = null;
if (typeof error === "string") {
notificationObj = {
msg: error || "Failed to publish share",
msg: error || "Failed to publish issue",
alertType: "error",
};
} else if (typeof error?.error === "string") {
notificationObj = {
msg: error?.error || "Failed to publish share",
msg: error?.error || "Failed to publish issue",
alertType: "error",
};
} else {
notificationObj = {
msg: error?.message || "Failed to publish share",
msg: error?.message || "Failed to publish issue",
alertType: "error",
};
}
@ -295,7 +309,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
setIsOpen(true);
}}
>
share
Open an Issue
</StyledButton>
)}
</>
@ -314,7 +328,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
justifyContent: "space-between",
}}
>
<NewCrowdfundTitle>Share</NewCrowdfundTitle>
<NewCrowdfundTitle>Issue</NewCrowdfundTitle>
</Box>
{step === "videos" && (
@ -331,7 +345,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
>
<input {...getInputProps()} />
<Typography>
Drag and drop files here or click to select files
Publish files related to issue (Optional)
</Typography>
</Box>
{files.map((file, index) => {
@ -362,7 +376,6 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
);
})}
{files?.length > 0 && (
<>
<Box
sx={{
@ -375,11 +388,13 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
categoryData={allCategoryData}
ref={categoryListRef}
columns={3}
excludeCategories={SupportState}
/>
</Box>
<ImagePublisher ref={imagePublisherRef} />
<CustomInputField
name="title"
label="Title of share"
label="Title of Issue"
variant="filled"
value={title}
onChange={e => {
@ -395,7 +410,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
fontSize: "18px",
}}
>
Description of share
Description of Issue
</Typography>
<TextEditor
inlineContent={description}
@ -404,11 +419,10 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
}}
/>
</>
)}
</>
)}
<CrowdfundActionButtonRow>
<CrowdfundActionButton
<ActionButtonRow>
<ActionButton
onClick={() => {
onClose();
}}
@ -416,7 +430,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
color="error"
>
Cancel
</CrowdfundActionButton>
</ActionButton>
<Box
sx={{
display: "flex",
@ -424,16 +438,16 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
alignItems: "center",
}}
>
<CrowdfundActionButton
<ActionButton
variant="contained"
onClick={() => {
publishQDNResource();
}}
>
Publish
</CrowdfundActionButton>
</ActionButton>
</Box>
</CrowdfundActionButtonRow>
</ActionButtonRow>
</ModalBody>
</Modal>
@ -466,7 +480,7 @@ export const PublishFile = ({ editId, editContent }: NewCrowdfundProps) => {
categoryListRef.current?.clearCategories();
dispatch(
setNotification({
msg: "Files published",
msg: "Issue published",
alertType: "success",
})
);

View File

@ -7,9 +7,9 @@ import {
Button,
Grid,
Rating,
Select,
TextField,
Typography,
Select
} from "@mui/material";
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternate";
import { TimesSVG } from "../../assets/svgs/TimesSVG";
@ -159,8 +159,6 @@ export const CustomInputField = styled(TextField)(({ theme }) => ({
},
}));
export const CrowdfundTitle = styled(Typography)(({ theme }) => ({
fontFamily: "Copse",
letterSpacing: "1px",
@ -466,14 +464,14 @@ export const CoverImage = styled("img")({
objectPosition: "center",
});
export const CrowdfundActionButtonRow = styled(Box)({
export const ActionButtonRow = styled(Box)({
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
});
export const CrowdfundActionButton = styled(Button)(({ theme }) => ({
export const ActionButton = styled(Button)(({ theme }) => ({
display: "flex",
alignItems: "center",
fontFamily: "Montserrat",
@ -540,8 +538,8 @@ export const NoReviewsFont = styled(Typography)(({ theme }) => ({
export const StyledButton = styled(Button)(({ theme }) => ({
fontWeight: 600,
color: theme.palette.text.primary,
fontFamily: "Cairo"
}))
fontFamily: "Cairo",
}));
export const CustomSelect = styled(Select)(({ theme }) => ({
fontFamily: "Mulish",
@ -550,34 +548,34 @@ export const CustomSelect = styled(Select)(({ theme }) => ({
fontWeight: 400,
color: theme.palette.text.primary,
backgroundColor: theme.palette.background.default,
'& .MuiSelect-select': {
padding: '12px',
"& .MuiSelect-select": {
padding: "12px",
fontFamily: "Mulish",
fontSize: "19px",
letterSpacing: "0px",
fontWeight: 400,
borderRadius: theme.shape.borderRadius, // Match border radius
},
'&:before': {
"&:before": {
// Underline style
borderBottomColor: theme.palette.mode === "light" ? "#B2BAC2" : "#c9cccf",
},
'&:after': {
"&:after": {
// Underline style when focused
borderBottomColor: theme.palette.secondary.main,
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
"& .MuiOutlinedInput-root": {
"& fieldset": {
borderColor: "#E0E3E7",
},
'&:hover fieldset': {
"&:hover fieldset": {
borderColor: "#B2BAC2",
},
'&.Mui-focused fieldset': {
"&.Mui-focused fieldset": {
borderColor: "#6F7E8C",
},
},
'& .MuiInputBase-root': {
"& .MuiInputBase-root": {
fontFamily: "Mulish",
fontSize: "19px",
letterSpacing: "0px",

View File

@ -25,13 +25,13 @@ export const StatsData = () => {
getFilesCount,
} = useFetchFiles();
const totalVideosPublished = useSelector(
const totalIssuesPublished = useSelector(
(state: RootState) => state.global.totalFilesPublished
);
const totalNamesPublished = useSelector(
(state: RootState) => state.global.totalNamesPublished
);
const videosPerNamePublished = useSelector(
const issuesPerNamePublished = useSelector(
(state: RootState) => state.global.filesPerNamePublished
);
@ -40,22 +40,28 @@ export const StatsData = () => {
}, [getFilesCount]);
return (
totalIssuesPublished > 0 && (
<StatsCol>
<div>
Shares:{" "}
<span style={{ fontWeight: "bold" }}>{totalVideosPublished}</span>
Issues Published:{" "}
<span style={{ fontWeight: "bold" }}>
{totalIssuesPublished || ""}
</span>
</div>
<div>
Publishers:{" "}
<span style={{ fontWeight: "bold" }}>{totalNamesPublished}</span>
<span style={{ fontWeight: "bold" }}>
{totalNamesPublished || ""}
</span>
</div>
<div>
Average:{" "}
<span style={{ fontWeight: "bold" }}>
{videosPerNamePublished > 0 &&
Number(videosPerNamePublished).toFixed(0)}
{issuesPerNamePublished > 0 &&
Number(issuesPerNamePublished).toFixed(0)}
</span>
</div>
</StatsCol>
)
);
};

View File

@ -10,9 +10,10 @@ import {
Theme,
} from "@mui/material";
import React, { forwardRef, useImperativeHandle, useState } from "react";
import React, { useEffect, useImperativeHandle, useState } from "react";
import { CategoryContainer } from "./CategoryList-styles.tsx";
import { allCategoryData } from "../../../constants/Categories/1stCategories.ts";
import { log } from "../../../constants/Misc.ts";
export interface Category {
id: number;
@ -29,19 +30,22 @@ export interface CategoryData {
}
type ListDirection = "column" | "row";
interface CategoryListProps {
sx?: SxProps<Theme>;
categoryData: CategoryData;
initialCategories?: string[];
columns?: number;
afterChange?: (categories: string[]) => void;
excludeCategories?: Category[];
}
export type CategoryListRef = {
getSelectedCategories: () => string[];
setSelectedCategories: (arr: string[]) => void;
clearCategories: () => void;
getCategoriesFetchString: () => string;
categoriesToObject: () => object;
getCategoriesFetchString: (categories?: string[]) => string;
categoriesToObject: (categories?: string[]) => object;
};
export const CategoryList = React.forwardRef<
@ -49,7 +53,14 @@ export const CategoryList = React.forwardRef<
CategoryListProps
>(
(
{ sx, categoryData, initialCategories, columns = 1 }: CategoryListProps,
{
sx,
categoryData,
initialCategories,
columns = 1,
afterChange,
excludeCategories,
}: CategoryListProps,
ref
) => {
const categoriesLength = categoryData.subCategories.length + 1;
@ -60,20 +71,27 @@ export const CategoryList = React.forwardRef<
const [selectedCategories, setSelectedCategories] = useState<string[]>(
initialCategories || emptyCategories
);
useEffect(() => {
if (initialCategories) setSelectedCategories(initialCategories);
}, [initialCategories]);
const categoriesToObject = () => {
const updateCategories = (categories: string[]) => {
setSelectedCategories(categories);
if (afterChange) afterChange(categories);
};
const categoriesToObject = (categories: string[]) => {
let categoriesObject = {};
selectedCategories.map((category, index) => {
categories.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);
if (log) console.log("categoriesObject is: ", categoriesObject);
return categoriesObject;
};
const clearCategories = () => {
setSelectedCategories(emptyCategories);
updateCategories(emptyCategories);
};
useImperativeHandle(ref, () => ({
@ -81,26 +99,31 @@ export const CategoryList = React.forwardRef<
return selectedCategories;
},
setSelectedCategories: categories => {
console.log("setSelectedCategories: ", categories);
//categories.map((category, index) => selectCategory(category, index));
setSelectedCategories(categories);
if (log) console.log("setSelectedCategories: ", categories);
updateCategories(categories);
},
clearCategories,
getCategoriesFetchString: () =>
getCategoriesFetchString(selectedCategories),
categoriesToObject,
getCategoriesFetchString: (categories?: string[]) =>
getCategoriesFetchString(categories || selectedCategories),
categoriesToObject: (categories?: string[]) =>
categoriesToObject(categories || selectedCategories),
}));
const selectCategory = (optionId: string, index: number) => {
const isMainCategory = index === 0;
const subCategoryIndex = index - 1;
let selectedOption: Category | undefined;
if (isMainCategory)
selectedOption = categoryData.category.find(
option => option.id === +optionId
);
else {
const subCategoryLevel = categoryData.subCategories[subCategoryIndex];
const parentCategory = selectedCategories[subCategoryIndex];
const subCategory = subCategoryLevel[parentCategory];
const selectedOption = isMainCategory
? categoryData.category.find(option => option.id === +optionId)
: categoryData.subCategories[subCategoryIndex][
selectedCategories[subCategoryIndex]
].find(option => option.id === +optionId);
selectedOption = subCategory.find(option => option.id === +optionId);
}
const newSelectedCategories: string[] = selectedCategories.map(
(category, categoryIndex) => {
if (index > categoryIndex) return category;
@ -108,7 +131,7 @@ export const CategoryList = React.forwardRef<
else return "";
}
);
setSelectedCategories(newSelectedCategories);
updateCategories(newSelectedCategories);
};
const selectCategoryEvent = (event: SelectChangeEvent, index: number) => {
@ -136,15 +159,16 @@ export const CategoryList = React.forwardRef<
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);
if (log) console.log("selected categories: ", selectedCategories);
if (log) console.log("index is: ", index);
if (log) console.log("subCategoryIndex is: ", subCategoryIndex);
if (log) console.log("category is: ", category);
if (log)
console.log(
"subCategoryIndex within category: ",
selectedCategories[subCategoryIndex]
);
console.log("categoryData: ", categoryData);
if (log) console.log("categoryData: ", categoryData);
const menuToFill = category[subCategoryIndex];
if (menuToFill)
@ -158,6 +182,7 @@ export const CategoryList = React.forwardRef<
const hasSubCategory = (category: Categories, index: number) => {
const subCategoryIndex = selectedCategories[index];
const subCategory = category[subCategoryIndex];
if (excludeCategories && subCategory === excludeCategories) return false;
return subCategory && subCategoryIndex;
};
@ -265,10 +290,21 @@ export const getCategoriesFetchString = (categories: string[]) => {
else fetchString += `;sub${index}:${category}`;
}
});
console.log("categoriesAsDescription: ", fetchString);
if (log) console.log("categoriesAsDescription: ", fetchString);
return fetchString;
};
export const appendCategoryToList = (
categories: string[],
appendedCategoryID: string
) => {
const filteredCategories = categories.filter(
categoryString => categoryString.length > 0
);
filteredCategories.push(appendedCategoryID);
return filteredCategories;
};
export const getCategoriesFromObject = (editFileProperties: any) => {
const categoryList: string[] = [];
const categoryCount = allCategoryData.subCategories.length + 1;

View File

@ -11,7 +11,7 @@ import {
CommentInputContainer,
SubmitCommentButton,
} from "./Comments-styles";
import { QSHARE_COMMENT_BASE } from "../../../constants/Identifiers.ts";
import { QSUPPORT_COMMENT_BASE } from "../../../constants/Identifiers.ts";
const uid = new ShortUniqueId();
const notification = localforage.createInstance({
@ -201,13 +201,13 @@ export const CommentEditor = ({
try {
const id = uid();
let identifier = `${QSHARE_COMMENT_BASE}${postId.slice(-12)}_base_${id}`;
let identifier = `${QSUPPORT_COMMENT_BASE}${postId.slice(-12)}_base_${id}`;
let idForNotification = identifier;
if (isReply && commentId) {
const removeBaseCommentId = commentId;
removeBaseCommentId.replace("_base_", "");
identifier = `${QSHARE_COMMENT_BASE}${postId.slice(
identifier = `${QSUPPORT_COMMENT_BASE}${postId.slice(
-12
)}_reply_${removeBaseCommentId.slice(-6)}_${id}`;
idForNotification = commentId;

View File

@ -1,11 +1,11 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { CommentEditor } from "./CommentEditor";
import { Comment } from "./Comment";
import { Box, Button, CircularProgress, useTheme } from "@mui/material";
import { CircularProgress } from "@mui/material";
import { styled } from "@mui/system";
import { useSelector } from "react-redux";
import { RootState } from "../../../state/store";
import { useNavigate, useLocation } from "react-router-dom";
import { useLocation, useNavigate } from "react-router-dom";
import {
CommentContainer,
CommentEditorContainer,
@ -14,8 +14,11 @@ import {
LoadMoreCommentsButtonRow,
NoCommentsRow,
} from "./Comments-styles";
import { QSHARE_COMMENT_BASE } from "../../../constants/Identifiers.ts";
import { CrowdfundSubTitle, CrowdfundSubTitleRow } from "../../PublishFile/Upload-styles.tsx";
import { QSUPPORT_COMMENT_BASE } from "../../../constants/Identifiers.ts";
import {
CrowdfundSubTitle,
CrowdfundSubTitleRow,
} from "../../PublishIssue/Upload-styles.tsx";
interface CommentSectionProps {
postId: string;
@ -105,7 +108,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => {
const offset = 0;
const removeBaseCommentId = commentId.replace("_base_", "");
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${QSHARE_COMMENT_BASE}${postId.slice(
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${QSUPPORT_COMMENT_BASE}${postId.slice(
-12
)}_reply_${removeBaseCommentId.slice(
-6
@ -150,7 +153,7 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => {
if (isNewMessages && numberOfComments) {
offset = numberOfComments;
}
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${QSHARE_COMMENT_BASE}${postId.slice(
const url = `/arbitrary/resources/search?mode=ALL&service=BLOG_COMMENT&query=${QSUPPORT_COMMENT_BASE}${postId.slice(
-12
)}_base_&limit=20&includemetadata=false&offset=${offset}&reverse=false&excludeblocked=true`;
const response = await fetch(url, {
@ -218,9 +221,8 @@ export const CommentSection = ({ postId, postName }: CommentSectionProps) => {
return (
<>
<Panel>
<CrowdfundSubTitleRow >
<CrowdfundSubTitleRow>
<CrowdfundSubTitle>Comments</CrowdfundSubTitle>
</CrowdfundSubTitleRow>
<CommentsContainer>

View File

@ -0,0 +1,47 @@
import { Box, Button } from "@mui/material";
import { styled } from "@mui/system";
import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternate";
import { TimesSVG } from "./TimesSVG.tsx";
export const AddCoverImageButton = styled(Button)(({ theme }) => ({
display: "flex",
alignItems: "center",
fontFamily: "Montserrat",
fontSize: "16px",
fontWeight: 400,
letterSpacing: "0.2px",
color: "white",
gap: "5px",
}));
export const AddLogoIcon = styled(AddPhotoAlternateIcon)(({ theme }) => ({
color: "#fff",
height: "25px",
width: "auto",
}));
export const LogoPreviewRow = styled(Box)(({ theme }) => ({
display: "flex",
alignItems: "center",
gap: "10px",
}));
export const CoverImagePreview = styled("img")(({ theme }) => ({
width: "100px",
height: "100px",
objectFit: "contain",
userSelect: "none",
borderRadius: "3px",
marginBottom: "10px",
}));
export const TimesIcon = styled(TimesSVG)(({ theme }) => ({
backgroundColor: theme.palette.background.paper,
borderRadius: "50%",
padding: "5px",
transition: "all 0.2s ease-in-out",
"&:hover": {
cursor: "pointer",
scale: "1.1",
},
}));

View File

@ -0,0 +1,62 @@
import ImageUploader from "./ImageUploader.tsx";
import React, { useImperativeHandle, useState } from "react";
import {
AddCoverImageButton,
AddLogoIcon,
CoverImagePreview,
LogoPreviewRow,
TimesIcon,
} from "./ImagePublisher-styles.tsx";
import { useTheme } from "@mui/material";
export type ImagePublisherRef = {
getImageArray: () => string[];
};
interface ImagePublisherProps {
initialImages?: string[];
}
export const ImagePublisher = React.forwardRef<
ImagePublisherRef,
ImagePublisherProps
>(({ initialImages }: ImagePublisherProps, ref) => {
const theme = useTheme();
const [imageArray, setImageArray] = useState<string[]>(initialImages || []);
useImperativeHandle(ref, () => ({
getImageArray: () => {
return imageArray;
},
}));
return (
<>
{imageArray.length === 0 ? (
<ImageUploader onPick={(img: string[]) => setImageArray(img)}>
<AddCoverImageButton variant="contained">
Add Images
<AddLogoIcon
sx={{
height: "25px",
width: "auto",
}}
></AddLogoIcon>
</AddCoverImageButton>
</ImageUploader>
) : (
<LogoPreviewRow>
{imageArray.map(
image =>
image && <CoverImagePreview src={image} alt="logo" key={image} />
)}
<TimesIcon
color={theme.palette.text.primary}
onClickFunc={() => setImageArray([])}
height={"32"}
width={"32"}
></TimesIcon>
</LogoPreviewRow>
)}
</>
);
});

View File

@ -0,0 +1,109 @@
import React, { useCallback } from "react";
import { Box } from "@mui/material";
import {
DropzoneInputProps,
DropzoneRootProps,
useDropzone,
} from "react-dropzone";
import Compressor from "compressorjs";
import { setNotification } from "../../../state/features/notificationsSlice.ts";
import { useDispatch } from "react-redux";
const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => {
reject(error);
};
});
interface ImageUploaderProps {
children: React.ReactNode;
onPick: (base64Img: string[]) => void;
}
export const ImageUploader: React.FC<ImageUploaderProps> = ({
children,
onPick,
}) => {
const dispatch = useDispatch();
const imageLimit = 3;
const compressImages = async (images: File[]) => {
const promises = images.map(image => {
return new Promise<File | Blob>(resolve => {
new Compressor(image, {
quality: 0.6,
maxWidth: 1200,
mimeType: "image/webp",
success(result) {
const file = new File([result], "name", {
type: "image/webp",
});
resolve(result);
},
error(err) {},
});
});
});
return await Promise.all(promises);
};
const onDrop = useCallback(
async (acceptedFiles: File[]) => {
if (acceptedFiles.length > imageLimit) {
const notificationObj = {
msg: `Only ${imageLimit} images can be published`,
alertType: "error",
};
dispatch(setNotification(notificationObj));
return;
}
try {
const compressedImages = await compressImages(acceptedFiles);
if (!compressedImages) return;
const base64Iamges = await Promise.all(
compressedImages.map(image => toBase64(image as File))
);
onPick(base64Iamges as string[]);
} catch (error) {
console.error(error);
}
},
[onPick]
);
const {
getRootProps,
getInputProps,
isDragActive,
}: {
getRootProps: () => DropzoneRootProps;
getInputProps: () => DropzoneInputProps;
isDragActive: boolean;
} = useDropzone({
onDrop,
accept: {
"image/*": [],
},
});
return (
<Box
{...getRootProps()}
sx={{
display: "flex",
}}
>
<input {...getInputProps()} />
{children}
</Box>
);
};
export default ImageUploader;

View File

@ -0,0 +1,28 @@
export interface IconTypes {
color?: string;
height: string;
width: string;
className?: string;
onClickFunc?: (e?: any) => void;
}
export const TimesSVG: React.FC<IconTypes> = ({
color,
height,
width,
className,
onClickFunc,
}) => {
return (
<svg
onClick={onClickFunc}
className={className}
fill={color}
xmlns="http://www.w3.org/2000/svg"
height={height}
viewBox="0 -960 960 960"
width={width}
>
<path d="m249-207-42-42 231-231-231-231 42-42 231 231 231-231 42 42-231 231 231 231-42 42-231-231-231 231Z" />
</svg>
);
};

View File

@ -1,89 +0,0 @@
import React, { useCallback } from 'react'
import { Box, Button, TextField, Typography, Modal } from '@mui/material'
import {
useDropzone,
DropzoneRootProps,
DropzoneInputProps
} from 'react-dropzone'
import Compressor from 'compressorjs'
const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = (error) => {
reject(error)
}
})
interface ImageUploaderProps {
children: React.ReactNode
onPick: (base64Img: string) => void
}
const ImageUploader: React.FC<ImageUploaderProps> = ({ children, onPick }) => {
const onDrop = useCallback(
async (acceptedFiles: File[]) => {
if (acceptedFiles.length > 1) {
return
}
let compressedFile: File | undefined
try {
const image = acceptedFiles[0]
await new Promise<void>((resolve) => {
new Compressor(image, {
quality: 0.6,
maxWidth: 1200,
mimeType: 'image/webp',
success(result) {
const file = new File([result], 'name', {
type: 'image/webp'
})
compressedFile = file
resolve()
},
error(err) {}
})
})
if (!compressedFile) return
const base64Img = await toBase64(compressedFile)
onPick(base64Img as string)
} catch (error) {
console.error(error)
}
},
[onPick]
)
const {
getRootProps,
getInputProps,
isDragActive
}: {
getRootProps: () => DropzoneRootProps
getInputProps: () => DropzoneInputProps
isDragActive: boolean
} = useDropzone({
onDrop,
accept: {
'image/*': []
}
})
return (
<Box
{...getRootProps()}
sx={{
display: 'flex'
}}
>
<input {...getInputProps()} />
{children}
</Box>
)
}
export default ImageUploader

View File

@ -14,26 +14,22 @@ export const CustomAppBar = styled(AppBar)(({ theme }) => ({
borderBottom: `1px solid ${theme.palette.primary.light}`,
backgroundColor: theme.palette.background.default,
[theme.breakpoints.only("xs")]: {
gap: "15px"
gap: "15px",
},
height: '55px'
height: "100px",
}));
export const LogoContainer = styled("div")({
cursor: 'pointer',
height: '100%',
display: 'flex',
alignItems: 'center'
cursor: "pointer",
height: "100%",
display: "flex",
alignItems: "center",
});
export const CustomTitle = styled(Typography)({
fontWeight: 600,
color: "#000000"
color: "#000000",
});
export const AuthenticateButton = styled(Button)(({ theme }) => ({
display: "flex",
flexDirection: "row",
@ -50,8 +46,8 @@ export const AuthenticateButton = styled(Button)(({ theme }) => ({
cursor: "pointer",
boxShadow: "rgba(0, 0, 0, 0.15) 1.95px 1.95px 2.6px;",
backgroundColor: theme.palette.secondary.dark,
filter: "brightness(1.1)"
}
filter: "brightness(1.1)",
},
}));
export const AvatarContainer = styled(Box)({
@ -61,9 +57,9 @@ export const AvatarContainer = styled(Box)({
cursor: "pointer",
"& #expand-icon": {
transition: "all 0.3s ease-in-out",
filter: "brightness(0.7)"
}
}
filter: "brightness(0.7)",
},
},
});
export const DropdownContainer = styled(Box)(({ theme }) => ({
@ -76,22 +72,22 @@ export const DropdownContainer = styled(Box)(({ theme }) => ({
"&:hover": {
cursor: "pointer",
filter:
theme.palette.mode === "light" ? "brightness(0.95)" : "brightness(1.1)"
}
theme.palette.mode === "light" ? "brightness(0.95)" : "brightness(1.1)",
},
}));
export const DropdownText = styled(Typography)(({ theme }) => ({
fontFamily: "Raleway",
fontSize: "16px",
color: theme.palette.text.primary,
userSelect: "none"
userSelect: "none",
}));
export const NavbarName = styled(Typography)(({ theme }) => ({
fontFamily: "Raleway",
fontSize: "18px",
color: theme.palette.text.primary,
margin: "0 10px"
margin: "0 10px",
}));
export const ThemeSelectRow = styled(Box)({
@ -99,7 +95,7 @@ export const ThemeSelectRow = styled(Box)({
alignItems: "center",
gap: "5px",
flexBasis: 0,
height: '100%'
height: "100%",
});
export const LightModeIcon = styled(LightModeSVG)(({ theme }) => ({
@ -109,8 +105,8 @@ export const LightModeIcon = styled(LightModeSVG)(({ theme }) => ({
filter:
theme.palette.mode === "dark"
? "drop-shadow(0px 4px 6px rgba(255, 255, 255, 0.6))"
: "drop-shadow(0px 4px 6px rgba(99, 88, 88, 0.1))"
}
: "drop-shadow(0px 4px 6px rgba(99, 88, 88, 0.1))",
},
}));
export const DarkModeIcon = styled(DarkModeSVG)(({ theme }) => ({
@ -120,6 +116,6 @@ export const DarkModeIcon = styled(DarkModeSVG)(({ theme }) => ({
filter:
theme.palette.mode === "dark"
? "drop-shadow(0px 4px 6px rgba(255, 255, 255, 0.6))"
: "drop-shadow(0px 4px 6px rgba(99, 88, 88, 0.1))"
}
: "drop-shadow(0px 4px 6px rgba(99, 88, 88, 0.1))",
},
}));

View File

@ -1,27 +1,15 @@
import React, { useState, useRef } from "react";
import {
Box,
Button,
Input,
Popover,
Typography,
useTheme,
} from "@mui/material";
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
import React, { useRef, useState } from "react";
import { Box, Input, Popover, Typography, useTheme } from "@mui/material";
import { BlockedNamesModal } from "../../common/BlockedNamesModal/BlockedNamesModal";
import AddBoxIcon from "@mui/icons-material/AddBox";
import {
AvatarContainer,
CustomAppBar,
DropdownContainer,
DropdownText,
AuthenticateButton,
NavbarName,
LightModeIcon,
DarkModeIcon,
ThemeSelectRow,
LogoContainer,
NavbarName,
ThemeSelectRow,
} from "./Navbar-styles";
import { AccountCircleSVG } from "../../../assets/svgs/AccountCircleSVG";
import BackspaceIcon from "@mui/icons-material/Backspace";
@ -32,18 +20,17 @@ import { useNavigate } from "react-router-dom";
import SearchIcon from "@mui/icons-material/Search";
import { DownloadTaskManager } from "../../common/DownloadTaskManager";
import QShareLogo from "../../../assets/img/q-share-icon.webp";
import QSupportLogo from "../../../assets/img/Q-SupportIcon.webp";
import { useDispatch, useSelector } from "react-redux";
import {
addFilteredFiles,
setEditPlaylist,
setFilterValue,
setIsFiltering,
} from "../../../state/features/fileSlice.ts";
import { RootState } from "../../../state/store";
import { useWindowSize } from "../../../hooks/useWindowSize";
import { PublishFile } from "../../PublishFile/PublishFile.tsx";
import { StyledButton } from "../../PublishFile/Upload-styles.tsx";
import { PublishIssue } from "../../PublishIssue/PublishIssue.tsx";
interface Props {
isAuthenticated: boolean;
userName: string | null;
@ -125,21 +112,21 @@ const NavBar: React.FC<Props> = ({
}}
>
<img
src={QShareLogo}
src={QSupportLogo}
style={{
width: "auto",
height: "55px",
height: "100px",
padding: "2px",
}}
/>
</LogoContainer>
<Typography
sx={{
fontSize: "16px",
fontSize: "30px",
whiteSpace: "nowrap",
}}
>
Sharing is caring
Welcome to Q-Support
</Typography>
</Box>
</ThemeSelectRow>
@ -150,135 +137,6 @@ const NavBar: React.FC<Props> = ({
gap: "10px",
}}
>
{/* {windowSize.width <= 600 ? (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1
}}
className="myClassOver600"
>
<Box onClick={openNotificationPopover}>
<SearchIcon
sx={{
cursor: 'pointer',
display: 'flex'
}}
/>
</Box>
{filterValue && (
<BackspaceIcon
sx={{
cursor: 'pointer'
}}
onClick={() => {
dispatch(setIsFiltering(false))
dispatch(setFilterValue(''))
dispatch(addFilteredVideos([]))
searchValRef.current = ''
if (!inputRef.current) return
inputRef.current.value = ''
}}
/>
)}
</Box>
): (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1
}}
className="myClassUnder600"
>
<Input
id="standard-adornment-name"
inputRef={inputRef}
onChange={(e) => {
searchValRef.current = e.target.value
}}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.keyCode === 13) {
if (!searchValRef.current) {
dispatch(setIsFiltering(false))
dispatch(setFilterValue(''))
dispatch(addFilteredVideos([]))
searchValRef.current = ''
if (!inputRef.current) return
inputRef.current.value = ''
return
}
navigate('/')
dispatch(setIsFiltering(true))
dispatch(addFilteredVideos([]))
dispatch(setFilterValue(searchValRef.current))
}
}}
placeholder="Search"
sx={{
'&&:before': {
borderBottom: 'none'
},
'&&:after': {
borderBottom: 'none'
},
'&&:hover:before': {
borderBottom: 'none'
},
'&&.Mui-focused:before': {
borderBottom: 'none'
},
'&&.Mui-focused': {
outline: 'none'
},
fontSize: '18px'
}}
/>
<SearchIcon
sx={{
cursor: 'pointer'
}}
onClick={() => {
if (!searchValRef.current) {
dispatch(setIsFiltering(false))
dispatch(setFilterValue(''))
dispatch(addFilteredVideos([]))
searchValRef.current = ''
if (!inputRef.current) return
inputRef.current.value = ''
return
}
navigate('/')
dispatch(setIsFiltering(true))
dispatch(addFilteredVideos([]))
dispatch(setFilterValue(searchValRef.current))
}}
/>
{filterValue && (
<BackspaceIcon
sx={{
cursor: 'pointer'
}}
onClick={() => {
dispatch(setIsFiltering(false))
dispatch(setFilterValue(''))
dispatch(addFilteredVideos([]))
searchValRef.current = ''
if (!inputRef.current) return
inputRef.current.value = ''
}}
/>
)}
</Box>
)} */}
<Popover
id={idNotification}
open={openPopover}
@ -411,7 +269,7 @@ const NavBar: React.FC<Props> = ({
<AvatarContainer>
{isAuthenticated && userName && (
<>
<PublishFile />
<PublishIssue />
</>
)}
</AvatarContainer>

View File

@ -7,15 +7,6 @@ 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,
@ -25,30 +16,30 @@ import {
getAllCategoriesWithIcons,
sortCategory,
} from "./CategoryFunctions.ts";
import { QappCategories, SupportState } from "./2ndCategories.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);
{ id: 1, name: "Core" },
{ id: 2, name: "UI" },
{ id: 3, name: "Q-Apps" },
{ id: 4, name: "Website" },
{ id: 5, name: "Marketing" },
{ id: 99, name: "Other" },
];
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,
1: SupportState,
2: SupportState,
3: QappCategories,
4: SupportState,
5: SupportState,
99: SupportState,
};
export let thirdCategories: Categories = {};
QappCategories.map(
supportStateCategory =>
(thirdCategories[supportStateCategory.id] = SupportState)
);
export const allCategoryData: CategoryData = {
category: firstCategories,
subCategories: [secondCategories, thirdCategories],

View File

@ -1,88 +1,23 @@
export const softwareSubCategories = [
{ id: 101, name: "OS" },
{ id: 102, name: "Application" },
{ id: 103, name: "Source Code" },
{ id: 104, name: "Plugin" },
{ id: 199, name: "Other" },
import OpenIcon from "../../assets/icons/OpenIcon.png";
import ClosedIcon from "../../assets/icons/ClosedIcon.png";
import InProgressIcon from "../../assets/icons/InProgressIcon.png";
import CompleteIcon from "../../assets/icons/CompleteIcon.png";
export const SupportState = [
{ id: 101, name: "Open", icon: OpenIcon },
{ id: 102, name: "Closed", icon: ClosedIcon },
{ id: 103, name: "In Progress", icon: InProgressIcon },
{ id: 104, name: "Complete", icon: CompleteIcon },
];
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" },
export const QappCategories = [
{ id: 301, name: "Q-Blog" },
{ id: 302, name: "Q-Mail" },
{ id: 303, name: "Q-Shop" },
{ id: 304, name: "Q-Fund" },
{ id: 305, name: "Ear-Bump" },
{ id: 306, name: "Q-Tube" },
{ id: 307, name: "Q-Share" },
{ id: 308, name: "Q-Support" },
{ 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" },
];

View File

@ -1,23 +0,0 @@
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" },
];

View File

@ -1,13 +1,13 @@
const useTestIdentifiers = false;
export const QSHARE_FILE_BASE = useTestIdentifiers
? "MYTEST_share_vid_"
: "qshare_file_";
export const QSUPPORT_FILE_BASE = useTestIdentifiers
? "MYTEST_support_issue_"
: "q_support_issue_";
export const QSHARE_PLAYLIST_BASE = useTestIdentifiers
? "MYTEST_share_playlist_"
: "qshare_playlist_";
export const QSUPPORT_PLAYLIST_BASE = useTestIdentifiers
? "MYTEST_support_playlist_"
: "q_support_playlist_";
export const QSHARE_COMMENT_BASE = useTestIdentifiers
? "qcomment_v1_MYTEST_"
: "qcomment_v1_qshare_";
export const QSUPPORT_COMMENT_BASE = useTestIdentifiers
? "qcomment_v1_MYTEST_support_"
: "qcomment_v1_q_support_";

View File

@ -1,3 +1,5 @@
export const minPriceSuperlike = 10;
export const titleFormatter = /[^a-zA-Z0-9\s-_!?()&'",.;:|—~@#$%^*+=<>]/g;
export const titleFormatterOnSave = /[^a-zA-Z0-9\s-_!()&',.;—~@#$%^+=]/g;
export const log = false;

View File

@ -19,8 +19,8 @@ import {
import { RootState } from "../state/store";
import { fetchAndEvaluateVideos } from "../utils/fetchVideos";
import {
QSHARE_PLAYLIST_BASE,
QSHARE_FILE_BASE,
QSUPPORT_PLAYLIST_BASE,
QSUPPORT_FILE_BASE,
} from "../constants/Identifiers.ts";
import { RequestQueue } from "../utils/queue";
import { queue } from "../wrappers/GlobalWrapper";
@ -114,7 +114,7 @@ export const useFetchFiles = () => {
try {
dispatch(setIsLoadingGlobal(true));
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSUPPORT_FILE_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`;
const response = await fetch(url, {
method: "GET",
headers: {
@ -218,10 +218,10 @@ export const useFetchFiles = () => {
}
if (type === "playlists") {
defaultUrl = defaultUrl + `&service=PLAYLIST`;
defaultUrl = defaultUrl + `&identifier=${QSHARE_PLAYLIST_BASE}`;
defaultUrl = defaultUrl + `&identifier=${QSUPPORT_PLAYLIST_BASE}`;
} else {
defaultUrl = defaultUrl + `&service=DOCUMENT`;
defaultUrl = defaultUrl + `&identifier=${QSHARE_FILE_BASE}`;
defaultUrl = defaultUrl + `&identifier=${QSUPPORT_FILE_BASE}`;
}
// const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QTUBE_VIDEO_BASE}&limit=${videoLimit}&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}`
@ -289,7 +289,7 @@ export const useFetchFiles = () => {
const offset = filteredVideos.length;
const replaceSpacesWithUnderscore = filterValue.replace(/ /g, "_");
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${replaceSpacesWithUnderscore}&identifier=${QSHARE_FILE_BASE}&limit=10&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${replaceSpacesWithUnderscore}&identifier=${QSUPPORT_FILE_BASE}&limit=10&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true&offset=${offset}`;
const response = await fetch(url, {
method: "GET",
headers: {
@ -345,7 +345,7 @@ export const useFetchFiles = () => {
const checkNewFiles = React.useCallback(async () => {
try {
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSUPPORT_FILE_BASE}&limit=20&includemetadata=false&reverse=true&excludeblocked=true&exactmatchnames=true`;
const response = await fetch(url, {
method: "GET",
headers: {
@ -382,7 +382,7 @@ export const useFetchFiles = () => {
const getFilesCount = React.useCallback(async () => {
try {
let url = `/arbitrary/resources/search?mode=ALL&includemetadata=false&limit=0&service=DOCUMENT&identifier=${QSHARE_FILE_BASE}`;
let url = `/arbitrary/resources/search?mode=ALL&includemetadata=false&limit=0&service=DOCUMENT&identifier=${QSUPPORT_FILE_BASE}`;
const response = await fetch(url, {
method: "GET",

View File

@ -2,12 +2,12 @@ import { Avatar, Box, Skeleton, Tooltip } from "@mui/material";
import {
BlockIconContainer,
BottomParent,
FileContainer,
IconsBox,
NameContainer,
VideoCard,
VideoCardName,
VideoCardTitle,
FileContainer,
VideoUploadDate,
} from "./FileList-styles.tsx";
import EditIcon from "@mui/icons-material/Edit";
@ -18,7 +18,7 @@ import {
} from "../../state/features/fileSlice.ts";
import BlockIcon from "@mui/icons-material/Block";
import AttachFileIcon from "@mui/icons-material/AttachFile";
import { formatBytes } from "../FileContent/FileContent.tsx";
import { formatBytes } from "../IssueContent/IssueContent.tsx";
import { formatDate } from "../../utils/time.ts";
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
@ -88,7 +88,7 @@ export const FileList = ({ files }: FileListProps) => {
}}
>
{fileObj?.user === username && (
<Tooltip title="Edit video properties" placement="top">
<Tooltip title="Edit Issue Properties" placement="top">
<BlockIconContainer>
<EditIcon
onClick={() => {

View File

@ -19,8 +19,8 @@ import {
import { formatDate } from "../../utils/time";
import { Video } from "../../state/features/fileSlice.ts";
import { queue } from "../../wrappers/GlobalWrapper";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { formatBytes } from "../FileContent/FileContent.tsx";
import { QSUPPORT_FILE_BASE } from "../../constants/Identifiers.ts";
import { formatBytes } from "../IssueContent/IssueContent.tsx";
import { getIconsFromObject } from "../../constants/Categories/CategoryFunctions.ts";
interface VideoListProps {
@ -46,7 +46,7 @@ export const FileListComponentLevel = ({ mode }: VideoListProps) => {
const getVideos = React.useCallback(async () => {
try {
const offset = videos.length;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}_&limit=50&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSUPPORT_FILE_BASE}_&limit=50&includemetadata=false&reverse=true&excludeblocked=true&name=${paramName}&exactmatchnames=true&offset=${offset}`;
const response = await fetch(url, {
method: "GET",
headers: {

View File

@ -275,6 +275,8 @@ export const Home = ({ mode }: HomeProps) => {
}}
sx={{
marginTop: "20px",
fontWeight: 1000,
color: "white",
}}
variant="contained"
>
@ -286,6 +288,8 @@ export const Home = ({ mode }: HomeProps) => {
}}
sx={{
marginTop: "20px",
fontWeight: 1000,
color: "white",
}}
variant="contained"
>

View File

@ -5,11 +5,10 @@ import {
AuthorTextComment,
StyledCardColComment,
StyledCardHeaderComment,
} from "../FileContent/FileContent-styles.tsx";
} from "../IssueContent/IssueContent-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 = () => {

View File

@ -1,5 +1,5 @@
import { styled } from "@mui/system";
import { Box, Grid, Typography, Checkbox } from "@mui/material";
import { Box, Typography } from "@mui/material";
export const FilePlayerContainer = styled(Box)(({ theme }) => ({
maxWidth: "95%",
@ -25,6 +25,10 @@ export const FileDescription = styled(Typography)(({ theme }) => ({
wordBreak: "break-word",
}));
export const ImageContainer = styled(Box)(({ theme }) => ({
display: "flex",
}));
export const Spacer = ({ height }: any) => {
return (
<Box

View File

@ -14,29 +14,22 @@ import {
FileDescription,
FilePlayerContainer,
FileTitle,
ImageContainer,
Spacer,
StyledCardColComment,
StyledCardHeaderComment,
} from "./FileContent-styles.tsx";
} from "./IssueContent-styles.tsx";
import { formatDate } from "../../utils/time";
import { CommentSection } from "../../components/common/Comments/CommentSection";
import { QSHARE_FILE_BASE } from "../../constants/Identifiers.ts";
import { QSUPPORT_FILE_BASE } from "../../constants/Identifiers.ts";
import { DisplayHtml } from "../../components/common/TextEditor/DisplayHtml";
import FileElement from "../../components/common/FileElement";
import {
allCategoryData,
iconCategories,
} from "../../constants/Categories/1stCategories.ts";
import { allCategoryData } from "../../constants/Categories/1stCategories.ts";
import {
Category,
getCategoriesFromObject,
} from "../../components/common/CategoryList/CategoryList.tsx";
import {
findAllCategoryData,
findCategoryData,
getCategoriesWithIcons,
getIconsFromObject,
} from "../../constants/Categories/CategoryFunctions.ts";
import { getIconsFromObject } from "../../constants/Categories/CategoryFunctions.ts";
export function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return "0 Bytes";
@ -50,7 +43,7 @@ export function formatBytes(bytes, decimals = 2) {
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
export const FileContent = () => {
export const IssueContent = () => {
const { name, id } = useParams();
const [isExpandedDescription, setIsExpandedDescription] =
useState<boolean>(false);
@ -106,7 +99,7 @@ export const FileContent = () => {
if (!name || !id) return;
dispatch(setIsLoadingGlobal(true));
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSHARE_FILE_BASE}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0&identifier=${id}`;
const url = `/arbitrary/resources/search?mode=ALL&service=DOCUMENT&query=${QSUPPORT_FILE_BASE}&limit=1&includemetadata=true&reverse=true&excludeblocked=true&name=${name}&exactmatchnames=true&offset=0&identifier=${id}`;
const response = await fetch(url, {
method: "GET",
headers: {
@ -272,8 +265,6 @@ export const FileContent = () => {
}
}
if (fileData) {
//const icon = getIconsFromObject(fileData)[0]?.icon || null;
const icon = getIconsFromObject(fileData);
setIcon(icon);
}
@ -415,6 +406,19 @@ export const FileContent = () => {
{categoriesDisplay}
</Typography>
</Box>
<ImageContainer>
{fileData?.images &&
fileData.images.map(image => {
return (
<img
src={image}
width={`${1280 / fileData.images.length}px`}
height={"480px"}
style={{ marginRight: "10px", marginBottom: "10px" }}
/>
);
})}
</ImageContainer>
<Spacer height="15px" />
<Box
sx={{

View File

@ -11,38 +11,38 @@ const commonThemeOptions = {
"Oxygen",
"Catamaran",
"Cairo",
"Arial"
"Arial",
].join(","),
h1: {
fontSize: "2rem",
fontWeight: 600
fontWeight: 600,
},
h2: {
fontSize: "1.75rem",
fontWeight: 500
fontWeight: 500,
},
h3: {
fontSize: "1.5rem",
fontWeight: 500
fontWeight: 500,
},
h4: {
fontSize: "1.25rem",
fontWeight: 500
fontWeight: 500,
},
h5: {
fontSize: "1rem",
fontWeight: 500
fontWeight: 500,
},
h6: {
fontSize: "0.875rem",
fontWeight: 500
fontWeight: 500,
},
body1: {
fontSize: "23px",
fontFamily: "Raleway",
fontWeight: 400,
lineHeight: 1.5,
letterSpacing: "0.5px"
letterSpacing: "0.5px",
},
body2: {
@ -50,12 +50,12 @@ const commonThemeOptions = {
fontFamily: "Raleway, Arial",
fontWeight: 400,
lineHeight: 1.4,
letterSpacing: "0.2px"
}
letterSpacing: "0.2px",
},
},
spacing: 8,
shape: {
borderRadius: 4
borderRadius: 4,
},
breakpoints: {
values: {
@ -63,8 +63,8 @@ const commonThemeOptions = {
sm: 600,
md: 900,
lg: 1200,
xl: 1536
}
xl: 1536,
},
},
components: {
MuiButton: {
@ -73,16 +73,16 @@ const commonThemeOptions = {
backgroundColor: "inherit",
transition: "filter 0.3s ease-in-out",
"&:hover": {
filter: "brightness(1.1)"
}
}
filter: "brightness(1.1)",
},
},
},
defaultProps: {
disableElevation: true,
disableRipple: true
}
}
}
disableRipple: true,
},
},
},
};
const lightTheme = createTheme({
@ -92,20 +92,20 @@ const lightTheme = createTheme({
primary: {
main: "#ffffff",
dark: "#F5F5F5",
light: "#FCFCFC"
light: "#FCFCFC",
},
secondary: {
main: "#417Ed4",
dark: "#3e74c1"
dark: "#3e74c1",
},
background: {
default: "#fcfcfc",
paper: "#F5F5F5"
paper: "#F5F5F5",
},
text: {
primary: "#000000",
secondary: "#525252"
}
secondary: "#525252",
},
},
components: {
MuiCard: {
@ -118,19 +118,19 @@ const lightTheme = createTheme({
"&:hover": {
cursor: "pointer",
boxShadow:
"rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;"
}
}
}
"rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;",
},
},
},
},
MuiIcon: {
defaultProps: {
style: {
color: "#000000"
}
}
}
}
color: "#000000",
},
},
},
},
});
const darkTheme = createTheme({
@ -138,23 +138,23 @@ const darkTheme = createTheme({
palette: {
mode: "dark",
primary: {
main: "#FF1493", // Neon pink
dark: "#C6127A", // Darker shade of neon pink
light: "#FF5EC4" // Lighter shade of neon pink
main: "#01a9e9", //
dark: "#008fcd", //
light: "#44c4ff", //
},
secondary: {
main: "#007FFF", // Electric blue
dark: "#0059B2", // Darker shade of electric blue
light: "#3399FF" // Lighter shade of electric blue
light: "#3399FF", // Lighter shade of electric blue
},
background: {
default: "#1C1C1C", // Deep space black
paper: "#342F41" // Dark cyberpunk-style purple
paper: "#342F41", // Dark cyberpunk-style purple
},
text: {
primary: "#ffffff",
secondary: "#b3b3b3"
}
secondary: "#b3b3b3",
},
},
components: {
MuiCard: {
@ -165,20 +165,20 @@ const darkTheme = createTheme({
transition: "all 0.3s ease-in-out",
"&:hover": {
cursor: "pointer",
boxShadow: "0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);"
}
}
}
boxShadow:
"0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);",
},
},
},
},
MuiIcon: {
defaultProps: {
style: {
color: "#ffffff"
}
}
}
}
color: "#ffffff",
},
},
},
},
});
export { lightTheme, darkTheme };

View File

@ -1,9 +1,9 @@
import React, {
useEffect,
useState,
useCallback,
useRef,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useDispatch, useSelector } from "react-redux";
@ -15,7 +15,7 @@ import { setUserAvatarHash } from "../state/features/globalSlice";
import { VideoPlayerGlobal } from "../components/common/VideoPlayerGlobal";
import { Rnd } from "react-rnd";
import { RequestQueue } from "../utils/queue";
import { EditFile } from "../components/EditFile/EditFile.tsx";
import { EditIssue } from "../components/EditIssue/EditIssue.tsx";
import { EditPlaylist } from "../components/EditPlaylist/EditPlaylist";
import ConsentModal from "../components/common/ConsentModal";
@ -138,7 +138,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
userAvatar={userAvatar}
authenticate={askForAccountInformation}
/>
<EditFile />
<EditIssue />
<EditPlaylist />
<Rnd
onDragStart={onDragStart}