Forums can now be published using the ForumModal.
Forums added to Home.tsx as forumData[], which is fetched when Home component initializes Many functions moved from ForumModal to ForumModal-Data, Most code in Home page deleted because it was from Q-Mail and not needed. Minor changes to identifiers App owner temporarily changed to Qortal Seth for easier testing.
This commit is contained in:
parent
11d5a46f0c
commit
0659433f03
32
package-lock.json
generated
32
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "q-mail",
|
"name": "q-mintership",
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "q-mail",
|
"name": "q-mintership",
|
||||||
"version": "0.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
@ -19,6 +19,7 @@
|
|||||||
"@tiptap/extension-underline": "^2.0.4",
|
"@tiptap/extension-underline": "^2.0.4",
|
||||||
"@tiptap/starter-kit": "^2.0.4",
|
"@tiptap/starter-kit": "^2.0.4",
|
||||||
"@types/react-grid-layout": "^1.3.2",
|
"@types/react-grid-layout": "^1.3.2",
|
||||||
|
"@types/react-redux": "^7.1.33",
|
||||||
"axios": "1.6.0",
|
"axios": "1.6.0",
|
||||||
"compressorjs": "^1.2.1",
|
"compressorjs": "^1.2.1",
|
||||||
"dompurify": "^3.0.3",
|
"dompurify": "^3.0.3",
|
||||||
@ -2422,6 +2423,18 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-redux": {
|
||||||
|
"version": "7.1.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz",
|
||||||
|
"integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/hoist-non-react-statics": "^3.3.0",
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"redux": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react-transition-group": {
|
"node_modules/@types/react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
@ -7173,6 +7186,17 @@
|
|||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-redux": {
|
||||||
|
"version": "7.1.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz",
|
||||||
|
"integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==",
|
||||||
|
"requires": {
|
||||||
|
"@types/hoist-non-react-statics": "^3.3.0",
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0",
|
||||||
|
"redux": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-transition-group": {
|
"@types/react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"@tiptap/extension-underline": "^2.0.4",
|
"@tiptap/extension-underline": "^2.0.4",
|
||||||
"@tiptap/starter-kit": "^2.0.4",
|
"@tiptap/starter-kit": "^2.0.4",
|
||||||
"@types/react-grid-layout": "^1.3.2",
|
"@types/react-grid-layout": "^1.3.2",
|
||||||
|
"@types/react-redux": "^7.1.33",
|
||||||
"axios": "1.6.0",
|
"axios": "1.6.0",
|
||||||
"compressorjs": "^1.2.1",
|
"compressorjs": "^1.2.1",
|
||||||
"dompurify": "^3.0.3",
|
"dompurify": "^3.0.3",
|
||||||
|
13
src/App.tsx
13
src/App.tsx
@ -1,20 +1,31 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { signal } from "@preact/signals-react";
|
||||||
|
import { useEffect } from "react";
|
||||||
import { Routes, Route } from "react-router-dom";
|
import { Routes, Route } from "react-router-dom";
|
||||||
|
|
||||||
import { ThemeProvider } from "@mui/material/styles";
|
import { ThemeProvider } from "@mui/material/styles";
|
||||||
import { CssBaseline } from "@mui/material";
|
import { CssBaseline } from "@mui/material";
|
||||||
|
import { ForumData } from "./pages/Forum/ForumModal";
|
||||||
import { Home } from "./pages/Home/Home";
|
import { Home } from "./pages/Home/Home";
|
||||||
import { lightTheme, darkTheme } from "./styles/theme";
|
import { lightTheme, darkTheme } from "./styles/theme";
|
||||||
import { store } from "./state/store";
|
import { store } from "./state/store";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
|
import { fetchForumData } from "./utils/QortalRequests";
|
||||||
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
||||||
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
||||||
import Notification from "./components/common/Notification/Notification";
|
import Notification from "./components/common/Notification/Notification";
|
||||||
|
|
||||||
|
export const forums = signal<ForumData[]>([]);
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const themeColor = window._qdnTheme;
|
const themeColor = window._qdnTheme;
|
||||||
|
useEffect(() => {
|
||||||
|
fetchForumData().then(data => {
|
||||||
|
if (data) forums.value = data;
|
||||||
|
console.log("forums is : ", forums.value);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ThemeProvider theme={darkTheme}>
|
<ThemeProvider theme={darkTheme}>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { appOwner } from "./Misc";
|
||||||
|
|
||||||
export const useTestIdentifiers = true;
|
export const useTestIdentifiers = true;
|
||||||
|
|
||||||
const appName = useTestIdentifiers ? "mintTEST" : "mintership";
|
const appName = useTestIdentifiers ? "mintTEST" : "mintership";
|
||||||
@ -6,8 +8,6 @@ export const QMAIL_BASE = `${appName}_mail`;
|
|||||||
export const ATTATCHMENT_BASE = `${appName}_attachments`;
|
export const ATTATCHMENT_BASE = `${appName}_attachments`;
|
||||||
export const AUDIO_BASE = `${appName}_qaudio`;
|
export const AUDIO_BASE = `${appName}_qaudio`;
|
||||||
|
|
||||||
export const FORUM_BASE = `${appName}_forum`;
|
export const FORUMS_ID = `${appName}_forums_${appOwner}`;
|
||||||
export const THREAD_BASE = `${appName}_thread`;
|
export const THREAD_BASE = `${appName}_thread_${appOwner}`;
|
||||||
export const THREAD_MESSAGE = `${appName}_thread_message`;
|
export const THREAD_MESSAGE_BASE = `${appName}_thread_message`;
|
||||||
|
|
||||||
export const FORUM_GROUPID = 0; // will be determined later
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const appOwner = "Q-Mintership";
|
//export const appOwner = "Q-Mintership";
|
||||||
|
export const appOwner = "Qortal Seth";
|
||||||
const maxSizeMB = 25;
|
const maxSizeMB = 25;
|
||||||
export const maxPrivateFileSize = maxSizeMB * 1024 * 1024;
|
export const maxPrivateFileSize = maxSizeMB * 1024 * 1024;
|
||||||
|
28
src/pages/Forum/ActionBar.tsx
Normal file
28
src/pages/Forum/ActionBar.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
||||||
|
import React from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { forums } from "../../App";
|
||||||
|
import {
|
||||||
|
ComposeContainer,
|
||||||
|
ComposeP,
|
||||||
|
InstanceContainer,
|
||||||
|
} from "../Home/Home-styles";
|
||||||
|
import { ForumModal } from "./ForumModal";
|
||||||
|
|
||||||
|
export const ActionBar = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InstanceContainer>
|
||||||
|
<ForumModal />
|
||||||
|
<ForumModal forumData={forums.value} />
|
||||||
|
<ComposeContainer
|
||||||
|
sx={{ width: "200px", marginLeft: "auto" }}
|
||||||
|
onClick={() => navigate("/sponsorshipData")}
|
||||||
|
>
|
||||||
|
<MenuBookIcon />
|
||||||
|
<ComposeP sx={{ fontSize: "70%" }}>{"Sponsorship Data"}</ComposeP>
|
||||||
|
</ComposeContainer>
|
||||||
|
</InstanceContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -9,23 +9,16 @@ import {
|
|||||||
InstanceContainer,
|
InstanceContainer,
|
||||||
} from "../Home/Home-styles";
|
} from "../Home/Home-styles";
|
||||||
import { GroupMail } from "../Mail/GroupMail";
|
import { GroupMail } from "../Mail/GroupMail";
|
||||||
import { EncryptionType } from "./NewForumModal";
|
import { Group } from "./GroupPermissionsForm";
|
||||||
|
import { EncryptionType, ForumData } from "./ForumModal";
|
||||||
|
|
||||||
export type GroupPermissionType = "Read" | "Write";
|
export const Forum = ({
|
||||||
export interface GroupPermissions {
|
title,
|
||||||
id: string;
|
encryption,
|
||||||
permissions: GroupPermissionType;
|
groups,
|
||||||
}
|
descriptionHTML,
|
||||||
|
descriptionText,
|
||||||
interface ForumProps {
|
}: ForumData) => {
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
encryption?: EncryptionType;
|
|
||||||
groupID: string;
|
|
||||||
groupPermissions: GroupPermissions[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Forum = ({ encryption = "None" }: ForumProps) => {
|
|
||||||
const [currentThread, setCurrentThread] = useState<any>(null);
|
const [currentThread, setCurrentThread] = useState<any>(null);
|
||||||
|
|
||||||
const openNewThread = () => {
|
const openNewThread = () => {
|
||||||
@ -36,20 +29,44 @@ export const Forum = ({ encryption = "None" }: ForumProps) => {
|
|||||||
executeEvent("openNewThreadModal", {});
|
executeEvent("openNewThreadModal", {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const forumWidth = 95;
|
||||||
|
const forumMarginLeft = (100 - forumWidth) / 2;
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box
|
||||||
<InstanceContainer>
|
sx={{
|
||||||
<ComposeContainer onClick={openNewThread}>
|
display: "flex",
|
||||||
<ComposeIcon src={ComposeIconSVG} />
|
flexDirection: "column",
|
||||||
<ComposeP>{currentThread ? "New Post" : "New Thread"}</ComposeP>
|
width: `${forumWidth}vw`,
|
||||||
</ComposeContainer>
|
marginLeft: `${forumMarginLeft}vw`,
|
||||||
</InstanceContainer>
|
justifyItems: "center",
|
||||||
|
marginTop: "10px",
|
||||||
{/*<GroupMail*/}
|
border: "1px solid white",
|
||||||
{/* groupInfo={selectedGroup}*/}
|
}}
|
||||||
{/* currentThread={currentThread}*/}
|
>
|
||||||
{/* setCurrentThread={setCurrentThread}*/}
|
<Box
|
||||||
{/*/>*/}
|
sx={{
|
||||||
|
backgroundImage: "linear-gradient(#355D80, #85A1BE)", // ends at #85A1BE bottom 1px line is #375576
|
||||||
|
width: "100%",
|
||||||
|
border: "1px solid gray",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{title}</span>
|
||||||
|
<Box>{descriptionText.trim()}</Box>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
// <Box>
|
||||||
|
// <InstanceContainer>
|
||||||
|
// <ComposeContainer onClick={openNewThread}>
|
||||||
|
// <ComposeIcon src={ComposeIconSVG} />
|
||||||
|
// <ComposeP>{currentThread ? "New Post" : "New Thread"}</ComposeP>
|
||||||
|
// </ComposeContainer>
|
||||||
|
// </InstanceContainer>
|
||||||
|
|
||||||
|
/*<GroupMail*/
|
||||||
|
/* groupInfo={selectedGroup}*/
|
||||||
|
/* currentThread={currentThread}*/
|
||||||
|
/* setCurrentThread={setCurrentThread}*/
|
||||||
|
/*/>*/
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
129
src/pages/Forum/ForumModal-Data.ts
Normal file
129
src/pages/Forum/ForumModal-Data.ts
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import { ReadonlySignal } from "@preact/signals-react";
|
||||||
|
import ShortUniqueId from "short-unique-id";
|
||||||
|
import { forums } from "../../App";
|
||||||
|
import {
|
||||||
|
ATTATCHMENT_BASE,
|
||||||
|
FORUMS_ID,
|
||||||
|
THREAD_BASE,
|
||||||
|
} from "../../constants/Identifiers";
|
||||||
|
import {
|
||||||
|
MAIL_ATTACHMENT_SERVICE_TYPE,
|
||||||
|
MAIL_SERVICE_TYPE,
|
||||||
|
THREAD_SERVICE_TYPE,
|
||||||
|
} from "../../constants/mail";
|
||||||
|
import { appOwner } from "../../constants/Misc";
|
||||||
|
import { setNotification } from "../../state/features/notificationsSlice";
|
||||||
|
import { store } from "../../state/store";
|
||||||
|
import { getGroup } from "../../utils/QortalRequests";
|
||||||
|
import { objectToBase64, toBase64 } from "../../utils/toBase64";
|
||||||
|
import { Group } from "./GroupPermissionsForm";
|
||||||
|
import { descriptionMaxLength, ForumData } from "./ForumModal";
|
||||||
|
|
||||||
|
const getGroupNames = async (groups: Group[]) => {
|
||||||
|
const groupPromises = groups.map(group => getGroup(group.id.value));
|
||||||
|
return await Promise.all(groupPromises);
|
||||||
|
};
|
||||||
|
|
||||||
|
const verifyData = async (formData: ForumData) => {
|
||||||
|
const userName = store.getState()?.auth?.user?.name;
|
||||||
|
const { title, encryption, groups, descriptionHTML, descriptionText } =
|
||||||
|
formData;
|
||||||
|
let errorMsg = "";
|
||||||
|
if (!userName) errorMsg = "Cannot send a message without access to your name";
|
||||||
|
if (!title) errorMsg = "Forum title is empty";
|
||||||
|
if (!encryption) errorMsg = "Encryption Type is empty";
|
||||||
|
if (!descriptionText) errorMsg = "Description is empty";
|
||||||
|
if (groups.filter(group => !!group.id).length < groups.length)
|
||||||
|
errorMsg = "A group has an empty ID";
|
||||||
|
if (groups.filter(group => !!group.permissions).length < groups.length)
|
||||||
|
errorMsg = "A group has empty permissions";
|
||||||
|
|
||||||
|
const groupsWithNames = await getGroupNames(groups);
|
||||||
|
|
||||||
|
if (
|
||||||
|
groupsWithNames.filter(group => !!group?.groupName).length < groups.length
|
||||||
|
)
|
||||||
|
errorMsg = "A group ID provided doesn't exist";
|
||||||
|
|
||||||
|
if (errorMsg) {
|
||||||
|
store.dispatch(
|
||||||
|
setNotification({
|
||||||
|
msg: errorMsg,
|
||||||
|
alertType: "error",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return errorMsg;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addForum = async (formData: ForumData) => {
|
||||||
|
const errorMsg = await verifyData(formData);
|
||||||
|
if (errorMsg) return;
|
||||||
|
forums.value = [...forums.value, formData];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editForums = async (newForums: ForumData[]) => {
|
||||||
|
const errorMsgPromises = newForums.map(forumData => verifyData(forumData));
|
||||||
|
const errorMsgs = await Promise.all(errorMsgPromises);
|
||||||
|
const errorMsgNum = errorMsgs.filter(msg => !!msg).length;
|
||||||
|
|
||||||
|
if (errorMsgNum > 0) return;
|
||||||
|
|
||||||
|
forums.value = newForums;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const publishForum = async (formData: ForumData) => {
|
||||||
|
let success = false;
|
||||||
|
const errorMsg = await verifyData(formData);
|
||||||
|
if (errorMsg) return success;
|
||||||
|
try {
|
||||||
|
await addForum(formData);
|
||||||
|
|
||||||
|
const publishDescription =
|
||||||
|
formData.title +
|
||||||
|
"_" +
|
||||||
|
formData.descriptionText.substring(0, descriptionMaxLength);
|
||||||
|
|
||||||
|
const userName = store.getState()?.auth?.user?.name;
|
||||||
|
await qortalRequest({
|
||||||
|
action: "PUBLISH_QDN_RESOURCE",
|
||||||
|
name: userName,
|
||||||
|
service: "METADATA",
|
||||||
|
identifier: FORUMS_ID,
|
||||||
|
description: publishDescription,
|
||||||
|
data64: await objectToBase64(forums),
|
||||||
|
});
|
||||||
|
success = true;
|
||||||
|
store.dispatch(
|
||||||
|
setNotification({
|
||||||
|
msg: "Forum published",
|
||||||
|
alertType: "success",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (error: any) {
|
||||||
|
let notificationObj = null;
|
||||||
|
const defaultErrorMessage = "Failed to submit forum data";
|
||||||
|
|
||||||
|
if (typeof error === "string") {
|
||||||
|
notificationObj = {
|
||||||
|
msg: error || defaultErrorMessage,
|
||||||
|
alertType: "error",
|
||||||
|
};
|
||||||
|
} else if (typeof error?.error === "string") {
|
||||||
|
notificationObj = {
|
||||||
|
msg: error?.error || defaultErrorMessage,
|
||||||
|
alertType: "error",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
notificationObj = {
|
||||||
|
msg: error?.message || defaultErrorMessage,
|
||||||
|
alertType: "error",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!notificationObj) return;
|
||||||
|
store.dispatch(setNotification(notificationObj));
|
||||||
|
|
||||||
|
throw new Error(defaultErrorMessage);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
};
|
@ -1,36 +1,28 @@
|
|||||||
import CloseIcon from "@mui/icons-material/Close";
|
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
||||||
import { Box, Input, Typography } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import {
|
import {
|
||||||
ReadonlySignal,
|
ReadonlySignal,
|
||||||
Signal,
|
Signal,
|
||||||
signal,
|
signal,
|
||||||
useComputed,
|
useComputed,
|
||||||
useSignal,
|
useSignal,
|
||||||
useSignalEffect,
|
|
||||||
} from "@preact/signals-react";
|
} from "@preact/signals-react";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import mime from "mime";
|
import React from "react";
|
||||||
import React, { useState } from "react";
|
import { useSelector } from "react-redux";
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg";
|
import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg";
|
||||||
|
|
||||||
import { CreateThreadIcon } from "../../assets/svgs/CreateThreadIcon";
|
import { CreateThreadIcon } from "../../assets/svgs/CreateThreadIcon";
|
||||||
import ModalCloseSVG from "../../assets/svgs/ModalClose.svg";
|
import ModalCloseSVG from "../../assets/svgs/ModalClose.svg";
|
||||||
import AttachmentSVG from "../../assets/svgs/NewMessageAttachment.svg";
|
|
||||||
import { SendNewMessage } from "../../assets/svgs/SendNewMessage";
|
|
||||||
import { SelectField } from "../../components/common/SelectField";
|
import { SelectField } from "../../components/common/SelectField";
|
||||||
import { Spacer } from "../../components/common/Spacer";
|
import { Spacer } from "../../components/common/Spacer";
|
||||||
import { TextEditor } from "../../components/common/TextEditor/TextEditor";
|
import { TextEditor } from "../../components/common/TextEditor/TextEditor";
|
||||||
import { ReusableModal } from "../../components/modals/ReusableModal";
|
import { ReusableModal } from "../../components/modals/ReusableModal";
|
||||||
import { useTestIdentifiers } from "../../constants/Identifiers";
|
import { useTestIdentifiers } from "../../constants/Identifiers";
|
||||||
import { appOwner, maxPrivateFileSize } from "../../constants/Misc";
|
import { appOwner } from "../../constants/Misc";
|
||||||
import { setNotification } from "../../state/features/notificationsSlice";
|
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import { formatBytes } from "../../utils/displaySize";
|
|
||||||
import { getFileExtension } from "../../utils/helpers";
|
|
||||||
import {
|
import {
|
||||||
AttachmentContainer,
|
|
||||||
CloseContainer,
|
CloseContainer,
|
||||||
ComposeContainer,
|
ComposeContainer,
|
||||||
ComposeIcon,
|
ComposeIcon,
|
||||||
@ -39,81 +31,84 @@ import {
|
|||||||
InstanceFooter,
|
InstanceFooter,
|
||||||
InstanceListContainer,
|
InstanceListContainer,
|
||||||
InstanceListHeader,
|
InstanceListHeader,
|
||||||
NewMessageAttachmentImg,
|
|
||||||
NewMessageCloseImg,
|
NewMessageCloseImg,
|
||||||
NewMessageHeaderP,
|
NewMessageHeaderP,
|
||||||
NewMessageInputRow,
|
NewMessageInputRow,
|
||||||
NewMessageSendButton,
|
NewMessageSendButton,
|
||||||
NewMessageSendP,
|
NewMessageSendP,
|
||||||
} from "../Home/Home-styles";
|
} from "../Home/Home-styles";
|
||||||
import { GroupPermissionType } from "./Forum";
|
|
||||||
import { Group, GroupPermissionsForm } from "./GroupPermissionsForm";
|
import { Group, GroupPermissionsForm } from "./GroupPermissionsForm";
|
||||||
import { publishForum } from "./NewForumModal-data";
|
import { publishForum } from "./ForumModal-Data";
|
||||||
import { QmailTextField } from "./QmailTextField";
|
import { QmailTextField } from "./QmailTextField";
|
||||||
|
|
||||||
export type EncryptionType = "None" | "Group" | "GroupAdmin";
|
export type EncryptionType = "None" | "Group" | "GroupAdmin" | "";
|
||||||
export type NewForumModalData = {
|
export interface ForumData {
|
||||||
name: string | undefined;
|
|
||||||
title: string;
|
title: string;
|
||||||
encryption: "None" | "Group" | "GroupAdmin" | "";
|
encryption: EncryptionType;
|
||||||
groups: Group[];
|
groups: Group[];
|
||||||
description: string;
|
descriptionHTML: string;
|
||||||
};
|
descriptionText: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const NewForumModal = () => {
|
export type GroupPermissionType = "Read" | "Write";
|
||||||
|
export interface GroupPermissions {
|
||||||
|
id: string;
|
||||||
|
permissions: GroupPermissionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const titleMaxLength = 60;
|
||||||
|
export const descriptionMaxLength = 160;
|
||||||
|
|
||||||
|
interface NewForumModalProps {
|
||||||
|
forumData?: ForumData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ForumModal = ({ forumData }: NewForumModalProps) => {
|
||||||
useSignals();
|
useSignals();
|
||||||
const navigate = useNavigate();
|
|
||||||
const isOpen = useSignal<boolean>(false);
|
const isOpen = useSignal<boolean>(false);
|
||||||
|
|
||||||
const forumTitle = useSignal<string>("");
|
const forumTitle = useSignal<string>("");
|
||||||
const description = useSignal<string>("");
|
const descriptionHTML = useSignal<string>("");
|
||||||
|
const descriptionText = useSignal<string>("");
|
||||||
const selectedEncryptionType = useSignal<EncryptionType | "">("None");
|
const groups = useSignal<Group[]>([
|
||||||
const formData = useComputed(() => {
|
|
||||||
return {
|
|
||||||
name: user?.name,
|
|
||||||
title: forumTitle.value,
|
|
||||||
encryption: selectedEncryptionType.value,
|
|
||||||
groups: groups.value,
|
|
||||||
description: description.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const { user } = useSelector((state: RootState) => state.auth);
|
|
||||||
|
|
||||||
// const dispatch = useDispatch();
|
|
||||||
|
|
||||||
const closeModal = () => {
|
|
||||||
forumTitle.value = "";
|
|
||||||
description.value = "";
|
|
||||||
isOpen.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const groups = signal<Group[]>([
|
|
||||||
{
|
{
|
||||||
id: signal<string>(""),
|
id: signal<string>(""),
|
||||||
name: "",
|
|
||||||
permissions: signal<GroupPermissionType>("Read"),
|
permissions: signal<GroupPermissionType>("Read"),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const selectedEncryptionType = useSignal<EncryptionType | "">("None");
|
||||||
|
|
||||||
|
const { user } = useSelector((state: RootState) => state.auth);
|
||||||
|
|
||||||
|
const formData: ReadonlySignal<ForumData> = useComputed(() => {
|
||||||
|
return {
|
||||||
|
title: forumTitle.value,
|
||||||
|
encryption: selectedEncryptionType.value,
|
||||||
|
groups: groups.value,
|
||||||
|
descriptionHTML: descriptionHTML.value,
|
||||||
|
descriptionText: descriptionText.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
isOpen.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const forumText = !!forumData ? "Edit Forums" : "Add Forum";
|
||||||
|
const publishText = !!forumData ? "Edit Forums" : "Create Forum";
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InstanceContainer>
|
{(user?.name === appOwner || useTestIdentifiers) && (
|
||||||
{(user?.name === appOwner || useTestIdentifiers) && (
|
<ComposeContainer onClick={e => (isOpen.value = true)}>
|
||||||
<ComposeContainer onClick={e => (isOpen.value = true)}>
|
|
||||||
<ComposeIcon src={ComposeIconSVG} />
|
|
||||||
<ComposeP>{"New Forum"}</ComposeP>
|
|
||||||
</ComposeContainer>
|
|
||||||
)}
|
|
||||||
<ComposeContainer onClick={e => navigate("/sponsorshipData")}>
|
|
||||||
<ComposeIcon src={ComposeIconSVG} />
|
<ComposeIcon src={ComposeIconSVG} />
|
||||||
<ComposeP>{"Sponsorship Data"}</ComposeP>
|
<ComposeP sx={{ fontSize: "70%" }}>{forumText}</ComposeP>
|
||||||
</ComposeContainer>
|
</ComposeContainer>
|
||||||
</InstanceContainer>
|
)}
|
||||||
<ReusableModal
|
<ReusableModal
|
||||||
open={isOpen.value}
|
open={isOpen.value}
|
||||||
customStyles={{
|
customStyles={{
|
||||||
@ -136,7 +131,7 @@ export const NewForumModal = () => {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewMessageHeaderP>{"New Forum"}</NewMessageHeaderP>
|
<NewMessageHeaderP>{forumText}</NewMessageHeaderP>
|
||||||
<CloseContainer onClick={closeModal}>
|
<CloseContainer onClick={closeModal}>
|
||||||
<NewMessageCloseImg src={ModalCloseSVG} />
|
<NewMessageCloseImg src={ModalCloseSVG} />
|
||||||
</CloseContainer>
|
</CloseContainer>
|
||||||
@ -157,7 +152,7 @@ export const NewForumModal = () => {
|
|||||||
height: "60px",
|
height: "60px",
|
||||||
borderBottom: "1px solid gray",
|
borderBottom: "1px solid gray",
|
||||||
}}
|
}}
|
||||||
maxLength={30}
|
maxLength={titleMaxLength}
|
||||||
/>
|
/>
|
||||||
<SelectField
|
<SelectField
|
||||||
options={["None", "Group", "GroupAdmin"]}
|
options={["None", "Group", "GroupAdmin"]}
|
||||||
@ -179,9 +174,15 @@ export const NewForumModal = () => {
|
|||||||
>
|
>
|
||||||
<p style={{ color: "black" }}> Description </p>
|
<p style={{ color: "black" }}> Description </p>
|
||||||
<TextEditor
|
<TextEditor
|
||||||
inlineContent={description.value}
|
inlineContent={descriptionHTML.value}
|
||||||
setInlineContent={(val: any) => {
|
setInlineContent={(
|
||||||
description.value = val;
|
value: any,
|
||||||
|
delta: any,
|
||||||
|
source: any,
|
||||||
|
editor: any
|
||||||
|
) => {
|
||||||
|
descriptionHTML.value = value;
|
||||||
|
descriptionText.value = editor.getText(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@ -194,8 +195,14 @@ export const NewForumModal = () => {
|
|||||||
height: "90px",
|
height: "90px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewMessageSendButton onClick={() => publishForum(formData.value)}>
|
<NewMessageSendButton
|
||||||
<NewMessageSendP>{"Create Forum"}</NewMessageSendP>
|
onClick={() => {
|
||||||
|
publishForum(formData.value).then(success => {
|
||||||
|
if (success) closeModal();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewMessageSendP>{publishText}</NewMessageSendP>
|
||||||
|
|
||||||
<CreateThreadIcon
|
<CreateThreadIcon
|
||||||
color="red"
|
color="red"
|
3
src/pages/Forum/ForumThreads.tsx
Normal file
3
src/pages/Forum/ForumThreads.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const ForumThreads = () => {
|
||||||
|
return <></>;
|
||||||
|
};
|
@ -16,12 +16,11 @@ import React, { useEffect } from "react";
|
|||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { SelectField } from "../../components/common/SelectField";
|
import { SelectField } from "../../components/common/SelectField";
|
||||||
import { NewMessageInputRow } from "../Home/Home-styles";
|
import { NewMessageInputRow } from "../Home/Home-styles";
|
||||||
import { GroupPermissions, GroupPermissionType } from "./Forum";
|
import { GroupPermissionType } from "./ForumModal";
|
||||||
import { QmailTextField } from "./QmailTextField";
|
import { QmailTextField } from "./QmailTextField";
|
||||||
|
|
||||||
export interface Group {
|
export interface Group {
|
||||||
id: Signal<string>;
|
id: Signal<string>;
|
||||||
name: string;
|
|
||||||
permissions: Signal<GroupPermissionType>;
|
permissions: Signal<GroupPermissionType>;
|
||||||
}
|
}
|
||||||
export interface GroupPermissionsFormProps {
|
export interface GroupPermissionsFormProps {
|
||||||
@ -33,7 +32,6 @@ export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => {
|
|||||||
const uid = new ShortUniqueId();
|
const uid = new ShortUniqueId();
|
||||||
const newGroup = {
|
const newGroup = {
|
||||||
id: signal<string>(""),
|
id: signal<string>(""),
|
||||||
name: "",
|
|
||||||
permissions: signal<GroupPermissionType>("Read"),
|
permissions: signal<GroupPermissionType>("Read"),
|
||||||
} as Group;
|
} as Group;
|
||||||
const addGroup = () => {
|
const addGroup = () => {
|
||||||
|
@ -1,289 +0,0 @@
|
|||||||
import { ReadonlySignal } from "@preact/signals-react";
|
|
||||||
import { ATTATCHMENT_BASE, THREAD_BASE } from "../../constants/Identifiers";
|
|
||||||
import {
|
|
||||||
MAIL_ATTACHMENT_SERVICE_TYPE,
|
|
||||||
MAIL_SERVICE_TYPE,
|
|
||||||
THREAD_SERVICE_TYPE,
|
|
||||||
} from "../../constants/mail";
|
|
||||||
import { setNotification } from "../../state/features/notificationsSlice";
|
|
||||||
import { store } from "../../state/store";
|
|
||||||
import { getGroup } from "../../utils/QortalRequests";
|
|
||||||
import { objectToBase64, toBase64 } from "../../utils/toBase64";
|
|
||||||
import { Group } from "./GroupPermissionsForm";
|
|
||||||
import { NewForumModalData } from "./NewForumModal";
|
|
||||||
|
|
||||||
const getGroupNames = async (groups: Group[]) => {
|
|
||||||
const groupPromises = groups.map(group => getGroup(group.id.value));
|
|
||||||
return await Promise.all(groupPromises);
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyData = async (formData: NewForumModalData) => {
|
|
||||||
const { name, title, encryption, groups, description } = formData;
|
|
||||||
|
|
||||||
let errorMsg = "";
|
|
||||||
if (!name) errorMsg = "Cannot send a message without a access to your name";
|
|
||||||
if (!title) errorMsg = "Forum title is empty";
|
|
||||||
if (!encryption) errorMsg = "Encryption Type is empty";
|
|
||||||
if (!description) errorMsg = "Description is empty";
|
|
||||||
if (groups.filter(group => !!group.id).length < groups.length)
|
|
||||||
errorMsg = "A group has an empty ID";
|
|
||||||
if (groups.filter(group => !!group.permissions).length < groups.length)
|
|
||||||
errorMsg = "A group has an empty permissions";
|
|
||||||
|
|
||||||
const groupsWithNames = await getGroupNames(groups);
|
|
||||||
|
|
||||||
if (groupsWithNames.filter(group => !!group.groupName).length < groups.length)
|
|
||||||
errorMsg = "A group doesn't exist";
|
|
||||||
|
|
||||||
// if (!groupInfo) {
|
|
||||||
// errorMsg = "Cannot access group information";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // if (!description) missingFields.push('subject')
|
|
||||||
// if (missingFields.length > 0) {
|
|
||||||
// const missingFieldsString = missingFields.join(", ");
|
|
||||||
// const errMsg = `Missing: ${missingFieldsString}`;
|
|
||||||
// errorMsg = errMsg;
|
|
||||||
// }
|
|
||||||
// const noExtension = attachments.filter(item => !item.extension);
|
|
||||||
// if (noExtension.length > 0) {
|
|
||||||
// errorMsg =
|
|
||||||
// "One of your attachments does not have an extension (example: .png, .pdf, ect...)";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
return errorMsg;
|
|
||||||
};
|
|
||||||
export const publishForum = async (formData: NewForumModalData) => {
|
|
||||||
const { name, title, encryption, groups, description } = formData;
|
|
||||||
const errorMsg = await verifyData(formData);
|
|
||||||
try {
|
|
||||||
if (errorMsg) {
|
|
||||||
store.dispatch(
|
|
||||||
setNotification({
|
|
||||||
msg: errorMsg,
|
|
||||||
alertType: "error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
throw new Error(errorMsg);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
debugger;
|
|
||||||
|
|
||||||
//
|
|
||||||
// const mailObject: any = {
|
|
||||||
// subject,
|
|
||||||
// createdAt: Date.now(),
|
|
||||||
// version: 1,
|
|
||||||
// attachments,
|
|
||||||
// textContentV2: value,
|
|
||||||
// name,
|
|
||||||
// threadOwner: currentThread?.threadData?.name || name,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// const groupPublicKeys = Object.keys(members)?.map(
|
|
||||||
// (key: any) => members[key]?.publicKey
|
|
||||||
// );
|
|
||||||
// if (!groupPublicKeys || groupPublicKeys?.length === 0) {
|
|
||||||
// throw new Error("No members in this group could be found");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // START OF ATTACHMENT LOGIC
|
|
||||||
//
|
|
||||||
// const attachmentArray: any[] = [];
|
|
||||||
// for (const singleAttachment of attachments) {
|
|
||||||
// const attachment = singleAttachment.file;
|
|
||||||
//
|
|
||||||
// const fileBase64 = await toBase64(attachment);
|
|
||||||
// if (typeof fileBase64 !== "string" || !fileBase64)
|
|
||||||
// throw new Error("Could not convert file to base64");
|
|
||||||
// const base64String = fileBase64.split(",")[1];
|
|
||||||
//
|
|
||||||
// const id = uid();
|
|
||||||
// const id2 = uid();
|
|
||||||
// const identifier = `${ATTATCHMENT_BASE}${id}_${id2}`;
|
|
||||||
// let fileExtension = attachment?.name?.split(".")?.pop();
|
|
||||||
// if (!fileExtension) {
|
|
||||||
// fileExtension = singleAttachment.extension;
|
|
||||||
// }
|
|
||||||
// const obj = {
|
|
||||||
// name: name,
|
|
||||||
// service: MAIL_ATTACHMENT_SERVICE_TYPE,
|
|
||||||
// filename: `${id}.${fileExtension}`,
|
|
||||||
// originalFilename: attachment?.name || "",
|
|
||||||
// identifier,
|
|
||||||
// data64: base64String,
|
|
||||||
// type: attachment?.type,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// attachmentArray.push(obj);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (attachmentArray?.length > 0) {
|
|
||||||
// mailObject.attachments = attachmentArray.map(item => {
|
|
||||||
// return {
|
|
||||||
// identifier: item.identifier,
|
|
||||||
// name,
|
|
||||||
// service: MAIL_ATTACHMENT_SERVICE_TYPE,
|
|
||||||
// filename: item.filename,
|
|
||||||
// originalFilename: item.originalFilename,
|
|
||||||
// type: item?.type,
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// // const multiplePublish = {
|
|
||||||
// // action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
|
||||||
// // resources: [...attachmentArray],
|
|
||||||
// // encrypt: true,
|
|
||||||
// // publicKeys: groupPublicKeys,
|
|
||||||
// // };
|
|
||||||
// // await qortalRequest(multiplePublish);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// //END OF ATTACHMENT LOGIC
|
|
||||||
// if (!isMessage) {
|
|
||||||
// const idThread = uid();
|
|
||||||
// const messageToBase64 = await objectToBase64(mailObject);
|
|
||||||
// const threadObject = {
|
|
||||||
// title: threadTitle,
|
|
||||||
// groupId: groupInfo.id,
|
|
||||||
// createdAt: Date.now(),
|
|
||||||
// name,
|
|
||||||
// };
|
|
||||||
// const threadToBase64 = await objectToBase64(threadObject);
|
|
||||||
// let identifierThread = `${THREAD_BASE}${groupInfo.id}_${idThread}`;
|
|
||||||
// let requestBodyThread: any = {
|
|
||||||
// name: name,
|
|
||||||
// service: THREAD_SERVICE_TYPE,
|
|
||||||
// data64: threadToBase64,
|
|
||||||
// identifier: identifierThread,
|
|
||||||
// description: threadTitle?.slice(0, 200),
|
|
||||||
// action: "PUBLISH_QDN_RESOURCE",
|
|
||||||
// };
|
|
||||||
// const idMsg = uid();
|
|
||||||
// let groupIndex = identifierThread.indexOf("group");
|
|
||||||
// let result = identifierThread.substring(groupIndex);
|
|
||||||
// let identifier = `qortal_qmail_thmsg_${result}_${idMsg}`;
|
|
||||||
// let requestBody: any = {
|
|
||||||
// name: name,
|
|
||||||
// service: MAIL_SERVICE_TYPE,
|
|
||||||
// data64: messageToBase64,
|
|
||||||
// identifier,
|
|
||||||
// };
|
|
||||||
// const multiplePublishMsg = {
|
|
||||||
// action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
|
||||||
// resources: [requestBody, ...attachmentArray],
|
|
||||||
// encrypt: true,
|
|
||||||
// publicKeys: groupPublicKeys,
|
|
||||||
// };
|
|
||||||
// await qortalRequest(requestBodyThread);
|
|
||||||
// setPublishes(multiplePublishMsg);
|
|
||||||
// setIsOpenMultiplePublish(true);
|
|
||||||
// // await qortalRequest(multiplePublishMsg);
|
|
||||||
// // dispatch(
|
|
||||||
// // setNotification({
|
|
||||||
// // msg: "Message sent",
|
|
||||||
// // alertType: "success",
|
|
||||||
// // })
|
|
||||||
// // );
|
|
||||||
// if (threadCallback) {
|
|
||||||
// // threadCallback({
|
|
||||||
// // threadData: threadObject,
|
|
||||||
// // threadOwner: name,
|
|
||||||
// // name,
|
|
||||||
// // threadId: identifierThread,
|
|
||||||
// // created: Date.now(),
|
|
||||||
// // service: 'MAIL_PRIVATE',
|
|
||||||
// // identifier: identifier
|
|
||||||
// // })
|
|
||||||
// setCallbackContent({
|
|
||||||
// thread: {
|
|
||||||
// threadData: threadObject,
|
|
||||||
// threadOwner: name,
|
|
||||||
// name,
|
|
||||||
// threadId: identifierThread,
|
|
||||||
// created: Date.now(),
|
|
||||||
// service: "MAIL_PRIVATE",
|
|
||||||
// identifier: identifier,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// closeModal();
|
|
||||||
// } else {
|
|
||||||
// if (!currentThread) throw new Error("unable to locate thread Id");
|
|
||||||
// const idThread = currentThread.threadId;
|
|
||||||
// const messageToBase64 = await objectToBase64(mailObject);
|
|
||||||
// const idMsg = uid();
|
|
||||||
// let groupIndex = idThread.indexOf("group");
|
|
||||||
// let result = idThread.substring(groupIndex);
|
|
||||||
// let identifier = `qortal_qmail_thmsg_${result}_${idMsg}`;
|
|
||||||
// let requestBody: any = {
|
|
||||||
// name: name,
|
|
||||||
// service: MAIL_SERVICE_TYPE,
|
|
||||||
// data64: messageToBase64,
|
|
||||||
// identifier,
|
|
||||||
// };
|
|
||||||
// const multiplePublishMsg = {
|
|
||||||
// action: "PUBLISH_MULTIPLE_QDN_RESOURCES",
|
|
||||||
// resources: [requestBody, ...attachmentArray],
|
|
||||||
// encrypt: true,
|
|
||||||
// publicKeys: groupPublicKeys,
|
|
||||||
// };
|
|
||||||
// setPublishes(multiplePublishMsg);
|
|
||||||
// setIsOpenMultiplePublish(true);
|
|
||||||
// // await qortalRequest(multiplePublishMsg);
|
|
||||||
// // dispatch(
|
|
||||||
// // setNotification({
|
|
||||||
// // msg: "Message sent",
|
|
||||||
// // alertType: "success",
|
|
||||||
// // })
|
|
||||||
// // );
|
|
||||||
// if (messageCallback) {
|
|
||||||
// setCallbackContent({
|
|
||||||
// message: {
|
|
||||||
// identifier,
|
|
||||||
// id: identifier,
|
|
||||||
// name,
|
|
||||||
// service: MAIL_SERVICE_TYPE,
|
|
||||||
// created: Date.now(),
|
|
||||||
// ...mailObject,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// // messageCallback({
|
|
||||||
// // identifier,
|
|
||||||
// // id: identifier,
|
|
||||||
// // name,
|
|
||||||
// // service: MAIL_SERVICE_TYPE,
|
|
||||||
// // created: Date.now(),
|
|
||||||
// // ...mailObject,
|
|
||||||
// // });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// closeModal();
|
|
||||||
// }
|
|
||||||
// } catch (error: any) {
|
|
||||||
// let notificationObj = null;
|
|
||||||
// if (typeof error === "string") {
|
|
||||||
// notificationObj = {
|
|
||||||
// msg: error || "Failed to send message",
|
|
||||||
// alertType: "error",
|
|
||||||
// };
|
|
||||||
// } else if (typeof error?.error === "string") {
|
|
||||||
// notificationObj = {
|
|
||||||
// msg: error?.error || "Failed to send message",
|
|
||||||
// alertType: "error",
|
|
||||||
// };
|
|
||||||
// } else {
|
|
||||||
// notificationObj = {
|
|
||||||
// msg: error?.message || "Failed to send message",
|
|
||||||
// alertType: "error",
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// if (!notificationObj) return;
|
|
||||||
// dispatch(setNotification(notificationObj));
|
|
||||||
//
|
|
||||||
// throw new Error("Failed to send message");
|
|
||||||
// }
|
|
||||||
};
|
|
@ -15,7 +15,6 @@ export const InstanceContainer = styled(Box)(({ theme }) => ({
|
|||||||
backgroundColor: "var(--color-instance)",
|
backgroundColor: "var(--color-instance)",
|
||||||
height: "59px",
|
height: "59px",
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
justifyContent: "space-between",
|
|
||||||
}));
|
}));
|
||||||
export const MailContainer = styled(Box)(({ theme }) => ({
|
export const MailContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -1,98 +1,19 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import { signal } from "@preact/signals-react";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import React, {
|
import React from "react";
|
||||||
useCallback,
|
import { forums } from "../../App";
|
||||||
useEffect,
|
import { ActionBar } from "../Forum/ActionBar";
|
||||||
useMemo,
|
import { Forum } from "../Forum/Forum";
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg";
|
|
||||||
import { useTestIdentifiers } from "../../constants/Identifiers";
|
|
||||||
import { appOwner } from "../../constants/Misc";
|
|
||||||
import { useFetchMail } from "../../hooks/useFetchMail";
|
|
||||||
import { RootState } from "../../state/store";
|
|
||||||
import { executeEvent } from "../../utils/events";
|
|
||||||
import { NewForumModal } from "../Forum/NewForumModal";
|
|
||||||
|
|
||||||
import {
|
|
||||||
ComposeContainer,
|
|
||||||
ComposeIcon,
|
|
||||||
ComposeP,
|
|
||||||
InstanceContainer,
|
|
||||||
MailContainer,
|
|
||||||
} from "./Home-styles";
|
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
const { user } = useSelector((state: RootState) => state.auth);
|
useSignals();
|
||||||
|
|
||||||
const privateGroups = useSelector(
|
|
||||||
(state: RootState) => state.global.privateGroups
|
|
||||||
);
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return Object.keys(privateGroups).map(key => {
|
|
||||||
return {
|
|
||||||
...privateGroups[key],
|
|
||||||
name: privateGroups[key].groupName,
|
|
||||||
id: key,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [privateGroups]);
|
|
||||||
const hashMapMailMessages = useSelector(
|
|
||||||
(state: RootState) => state.mail.hashMapMailMessages
|
|
||||||
);
|
|
||||||
|
|
||||||
const userName = useMemo(() => {
|
|
||||||
if (!user?.name) return "";
|
|
||||||
return user.name;
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
const { getMailMessages, checkNewMessages } = useFetchMail();
|
|
||||||
const getMessages = React.useCallback(
|
|
||||||
async (isOnMount?: boolean) => {
|
|
||||||
if (!user?.name || !user?.address) return;
|
|
||||||
try {
|
|
||||||
await getMailMessages(user.name, user.address);
|
|
||||||
} catch (error) {}
|
|
||||||
},
|
|
||||||
[getMailMessages, user]
|
|
||||||
);
|
|
||||||
|
|
||||||
const interval = useRef<any>(null);
|
|
||||||
|
|
||||||
const checkNewMessagesFunc = useCallback(() => {
|
|
||||||
if (!user?.name || !user?.address) return;
|
|
||||||
let isCalling = false;
|
|
||||||
interval.current = setInterval(async () => {
|
|
||||||
if (isCalling || !user?.name || !user?.address) return;
|
|
||||||
isCalling = true;
|
|
||||||
const res = await checkNewMessages(user?.name, user.address);
|
|
||||||
isCalling = false;
|
|
||||||
}, 30000);
|
|
||||||
}, [checkNewMessages, user]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
checkNewMessagesFunc();
|
|
||||||
return () => {
|
|
||||||
if (interval?.current) {
|
|
||||||
clearInterval(interval.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [checkNewMessagesFunc]);
|
|
||||||
|
|
||||||
const firstMount = useRef(false);
|
|
||||||
useEffect(() => {
|
|
||||||
if (user?.name && !firstMount.current) {
|
|
||||||
getMessages(true);
|
|
||||||
firstMount.current = true;
|
|
||||||
}
|
|
||||||
}, [user]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<NewForumModal />
|
<ActionBar />
|
||||||
|
{forums.value.map(forum => (
|
||||||
|
<Forum key={forum.title} {...forum} />
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ import React, {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { THREAD_BASE, THREAD_MESSAGE } from "../../constants/Identifiers";
|
import { THREAD_BASE, THREAD_MESSAGE_BASE } from "../../constants/Identifiers";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import EditIcon from "@mui/icons-material/Edit";
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import {
|
import {
|
||||||
@ -214,7 +214,7 @@ export const GroupMail = ({
|
|||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
dispatch(setIsLoadingCustom("Loading recent threads"));
|
dispatch(setIsLoadingCustom("Loading recent threads"));
|
||||||
const query = `${THREAD_MESSAGE}${groupId}`;
|
const query = `${THREAD_MESSAGE_BASE}${groupId}`;
|
||||||
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=100&includemetadata=false&offset=${0}&reverse=true&excludeblocked=true${queryString}`;
|
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=100&includemetadata=false&offset=${0}&reverse=true&excludeblocked=true${queryString}`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -8,7 +8,7 @@ import React, {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { THREAD_MESSAGE } from "../../constants/Identifiers";
|
import { THREAD_MESSAGE_BASE } from "../../constants/Identifiers";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -99,7 +99,7 @@ export const Thread = ({
|
|||||||
let result = parts[0];
|
let result = parts[0];
|
||||||
const threadId = result;
|
const threadId = result;
|
||||||
const offset = messages.length;
|
const offset = messages.length;
|
||||||
const query = `${THREAD_MESSAGE}${groupInfo?.threadData?.groupId}_${threadId}`;
|
const query = `${THREAD_MESSAGE_BASE}${groupInfo?.threadData?.groupId}_${threadId}`;
|
||||||
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=20&includemetadata=false&offset=${offset}&reverse=true&excludeblocked=true`;
|
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=20&includemetadata=false&offset=${offset}&reverse=true&excludeblocked=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -195,7 +195,7 @@ export const Thread = ({
|
|||||||
let parts = str.split("_").reverse();
|
let parts = str.split("_").reverse();
|
||||||
let result = parts[0];
|
let result = parts[0];
|
||||||
const threadId = result;
|
const threadId = result;
|
||||||
const query = `${THREAD_MESSAGE}${groupInfo?.threadData?.groupId}_${threadId}`;
|
const query = `${THREAD_MESSAGE_BASE}${groupInfo?.threadData?.groupId}_${threadId}`;
|
||||||
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=20&includemetadata=false&offset=${0}&reverse=true&excludeblocked=true`;
|
const url = `/arbitrary/resources/search?mode=ALL&service=${MAIL_SERVICE_TYPE}&query=${query}&limit=20&includemetadata=false&offset=${0}&reverse=true&excludeblocked=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { FORUMS_ID } from "../constants/Identifiers";
|
||||||
|
import { appOwner } from "../constants/Misc";
|
||||||
|
|
||||||
export interface GroupData {
|
export interface GroupData {
|
||||||
groupId: number;
|
groupId: number;
|
||||||
owner: string;
|
owner: string;
|
||||||
@ -17,11 +20,30 @@ export const listGroups = async () => {
|
|||||||
|
|
||||||
export const getGroup = async (groupID: number | string) => {
|
export const getGroup = async (groupID: number | string) => {
|
||||||
const url = `/groups/${groupID.toString()}`;
|
const url = `/groups/${groupID.toString()}`;
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "GET",
|
try {
|
||||||
headers: {
|
const response = await fetch(url, {
|
||||||
"Content-Type": "application/json",
|
method: "GET",
|
||||||
},
|
headers: {
|
||||||
});
|
"Content-Type": "application/json",
|
||||||
return (await response.json()) as GroupData;
|
},
|
||||||
|
});
|
||||||
|
return (await response.json()) as GroupData;
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchForumData = async () => {
|
||||||
|
try {
|
||||||
|
return await qortalRequest({
|
||||||
|
action: "FETCH_QDN_RESOURCE",
|
||||||
|
name: appOwner,
|
||||||
|
service: "METADATA",
|
||||||
|
identifier: FORUMS_ID,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,6 @@ export const objectToBase64 = async (obj: any): Promise<string> => {
|
|||||||
"data:application/json;base64,",
|
"data:application/json;base64,",
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
console.log(`base64 resolution: ${base64}`);
|
|
||||||
resolve(base64);
|
resolve(base64);
|
||||||
} else {
|
} else {
|
||||||
reject(new Error("Failed to read the Blob as a base64-encoded string"));
|
reject(new Error("Failed to read the Blob as a base64-encoded string"));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user