diff --git a/package-lock.json b/package-lock.json index 8dd3488..a70c7d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { - "name": "q-mail", - "version": "0.0.0", + "name": "q-mintership", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "q-mail", - "version": "0.0.0", + "name": "q-mintership", + "version": "1.0.0", "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", @@ -19,6 +19,7 @@ "@tiptap/extension-underline": "^2.0.4", "@tiptap/starter-kit": "^2.0.4", "@types/react-grid-layout": "^1.3.2", + "@types/react-redux": "^7.1.33", "axios": "1.6.0", "compressorjs": "^1.2.1", "dompurify": "^3.0.3", @@ -2422,6 +2423,18 @@ "@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": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -7173,6 +7186,17 @@ "@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": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/package.json b/package.json index 07c3b6d..edcf50d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@tiptap/extension-underline": "^2.0.4", "@tiptap/starter-kit": "^2.0.4", "@types/react-grid-layout": "^1.3.2", + "@types/react-redux": "^7.1.33", "axios": "1.6.0", "compressorjs": "^1.2.1", "dompurify": "^3.0.3", diff --git a/src/App.tsx b/src/App.tsx index f559e36..03d37cd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,20 +1,31 @@ // @ts-nocheck +import { signal } from "@preact/signals-react"; +import { useEffect } from "react"; import { Routes, Route } from "react-router-dom"; import { ThemeProvider } from "@mui/material/styles"; import { CssBaseline } from "@mui/material"; +import { ForumData } from "./pages/Forum/ForumModal"; import { Home } from "./pages/Home/Home"; import { lightTheme, darkTheme } from "./styles/theme"; import { store } from "./state/store"; import { Provider } from "react-redux"; +import { fetchForumData } from "./utils/QortalRequests"; import GlobalWrapper from "./wrappers/GlobalWrapper"; import DownloadWrapper from "./wrappers/DownloadWrapper"; import Notification from "./components/common/Notification/Notification"; +export const forums = signal([]); + function App() { const themeColor = window._qdnTheme; - + useEffect(() => { + fetchForumData().then(data => { + if (data) forums.value = data; + console.log("forums is : ", forums.value); + }); + }, []); return ( diff --git a/src/constants/Identifiers.ts b/src/constants/Identifiers.ts index f4d84e2..4183020 100644 --- a/src/constants/Identifiers.ts +++ b/src/constants/Identifiers.ts @@ -1,3 +1,5 @@ +import { appOwner } from "./Misc"; + export const useTestIdentifiers = true; const appName = useTestIdentifiers ? "mintTEST" : "mintership"; @@ -6,8 +8,6 @@ export const QMAIL_BASE = `${appName}_mail`; export const ATTATCHMENT_BASE = `${appName}_attachments`; export const AUDIO_BASE = `${appName}_qaudio`; -export const FORUM_BASE = `${appName}_forum`; -export const THREAD_BASE = `${appName}_thread`; -export const THREAD_MESSAGE = `${appName}_thread_message`; - -export const FORUM_GROUPID = 0; // will be determined later +export const FORUMS_ID = `${appName}_forums_${appOwner}`; +export const THREAD_BASE = `${appName}_thread_${appOwner}`; +export const THREAD_MESSAGE_BASE = `${appName}_thread_message`; diff --git a/src/constants/Misc.ts b/src/constants/Misc.ts index 4ea1b8b..ee1db43 100644 --- a/src/constants/Misc.ts +++ b/src/constants/Misc.ts @@ -1,4 +1,4 @@ -export const appOwner = "Q-Mintership"; - +//export const appOwner = "Q-Mintership"; +export const appOwner = "Qortal Seth"; const maxSizeMB = 25; export const maxPrivateFileSize = maxSizeMB * 1024 * 1024; diff --git a/src/pages/Forum/ActionBar.tsx b/src/pages/Forum/ActionBar.tsx new file mode 100644 index 0000000..f024f3d --- /dev/null +++ b/src/pages/Forum/ActionBar.tsx @@ -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 ( + + + + navigate("/sponsorshipData")} + > + + {"Sponsorship Data"} + + + ); +}; diff --git a/src/pages/Forum/Forum.tsx b/src/pages/Forum/Forum.tsx index 6b69436..1995b42 100644 --- a/src/pages/Forum/Forum.tsx +++ b/src/pages/Forum/Forum.tsx @@ -9,23 +9,16 @@ import { InstanceContainer, } from "../Home/Home-styles"; import { GroupMail } from "../Mail/GroupMail"; -import { EncryptionType } from "./NewForumModal"; +import { Group } from "./GroupPermissionsForm"; +import { EncryptionType, ForumData } from "./ForumModal"; -export type GroupPermissionType = "Read" | "Write"; -export interface GroupPermissions { - id: string; - permissions: GroupPermissionType; -} - -interface ForumProps { - title: string; - description: string; - encryption?: EncryptionType; - groupID: string; - groupPermissions: GroupPermissions[]; -} - -export const Forum = ({ encryption = "None" }: ForumProps) => { +export const Forum = ({ + title, + encryption, + groups, + descriptionHTML, + descriptionText, +}: ForumData) => { const [currentThread, setCurrentThread] = useState(null); const openNewThread = () => { @@ -36,20 +29,44 @@ export const Forum = ({ encryption = "None" }: ForumProps) => { executeEvent("openNewThreadModal", {}); }; + const forumWidth = 95; + const forumMarginLeft = (100 - forumWidth) / 2; return ( - - - - - {currentThread ? "New Post" : "New Thread"} - - - - {/**/} + + + {title} + {descriptionText.trim()} + + + // + // + // + // + // {currentThread ? "New Post" : "New Thread"} + // + // + + /**/ ); }; diff --git a/src/pages/Forum/ForumModal-Data.ts b/src/pages/Forum/ForumModal-Data.ts new file mode 100644 index 0000000..518fdcb --- /dev/null +++ b/src/pages/Forum/ForumModal-Data.ts @@ -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; +}; diff --git a/src/pages/Forum/NewForumModal.tsx b/src/pages/Forum/ForumModal.tsx similarity index 66% rename from src/pages/Forum/NewForumModal.tsx rename to src/pages/Forum/ForumModal.tsx index 745ed77..d330551 100644 --- a/src/pages/Forum/NewForumModal.tsx +++ b/src/pages/Forum/ForumModal.tsx @@ -1,36 +1,28 @@ -import CloseIcon from "@mui/icons-material/Close"; -import { Box, Input, Typography } from "@mui/material"; +import MenuBookIcon from "@mui/icons-material/MenuBook"; +import { Box } from "@mui/material"; import { ReadonlySignal, Signal, signal, useComputed, useSignal, - useSignalEffect, } from "@preact/signals-react"; import { useSignals } from "@preact/signals-react/runtime"; -import mime from "mime"; -import React, { useState } from "react"; -import { useDropzone } from "react-dropzone"; -import { useDispatch, useSelector } from "react-redux"; +import React from "react"; +import { useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import ComposeIconSVG from "../../assets/svgs/ComposeIcon.svg"; + import { CreateThreadIcon } from "../../assets/svgs/CreateThreadIcon"; 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 { Spacer } from "../../components/common/Spacer"; import { TextEditor } from "../../components/common/TextEditor/TextEditor"; import { ReusableModal } from "../../components/modals/ReusableModal"; import { useTestIdentifiers } from "../../constants/Identifiers"; -import { appOwner, maxPrivateFileSize } from "../../constants/Misc"; -import { setNotification } from "../../state/features/notificationsSlice"; +import { appOwner } from "../../constants/Misc"; import { RootState } from "../../state/store"; -import { formatBytes } from "../../utils/displaySize"; -import { getFileExtension } from "../../utils/helpers"; import { - AttachmentContainer, CloseContainer, ComposeContainer, ComposeIcon, @@ -39,81 +31,84 @@ import { InstanceFooter, InstanceListContainer, InstanceListHeader, - NewMessageAttachmentImg, NewMessageCloseImg, NewMessageHeaderP, NewMessageInputRow, NewMessageSendButton, NewMessageSendP, } from "../Home/Home-styles"; -import { GroupPermissionType } from "./Forum"; import { Group, GroupPermissionsForm } from "./GroupPermissionsForm"; -import { publishForum } from "./NewForumModal-data"; +import { publishForum } from "./ForumModal-Data"; import { QmailTextField } from "./QmailTextField"; -export type EncryptionType = "None" | "Group" | "GroupAdmin"; -export type NewForumModalData = { - name: string | undefined; +export type EncryptionType = "None" | "Group" | "GroupAdmin" | ""; +export interface ForumData { title: string; - encryption: "None" | "Group" | "GroupAdmin" | ""; + encryption: EncryptionType; groups: Group[]; - description: string; -}; + descriptionHTML: string; + descriptionText: string; +} + +export type GroupPermissionType = "Read" | "Write"; +export interface GroupPermissions { + id: string; + permissions: GroupPermissionType; +} -export const NewForumModal = () => { +export const titleMaxLength = 60; +export const descriptionMaxLength = 160; + +interface NewForumModalProps { + forumData?: ForumData[]; +} + +export const ForumModal = ({ forumData }: NewForumModalProps) => { useSignals(); - const navigate = useNavigate(); const isOpen = useSignal(false); const forumTitle = useSignal(""); - const description = useSignal(""); + const descriptionHTML = useSignal(""); + const descriptionText = useSignal(""); + const groups = useSignal([ + { + id: signal(""), + permissions: signal("Read"), + }, + ]); const selectedEncryptionType = useSignal("None"); - const formData = useComputed(() => { + + const { user } = useSelector((state: RootState) => state.auth); + + const formData: ReadonlySignal = useComputed(() => { return { - name: user?.name, title: forumTitle.value, encryption: selectedEncryptionType.value, groups: groups.value, - description: description.value, + descriptionHTML: descriptionHTML.value, + descriptionText: descriptionText.value, }; }); - const { user } = useSelector((state: RootState) => state.auth); - - // const dispatch = useDispatch(); - const closeModal = () => { - forumTitle.value = ""; - description.value = ""; isOpen.value = false; }; - const groups = signal([ - { - id: signal(""), - name: "", - permissions: signal("Read"), - }, - ]); + const forumText = !!forumData ? "Edit Forums" : "Add Forum"; + const publishText = !!forumData ? "Edit Forums" : "Create Forum"; return ( - - {(user?.name === appOwner || useTestIdentifiers) && ( - (isOpen.value = true)}> - - {"New Forum"} - - )} - navigate("/sponsorshipData")}> + {(user?.name === appOwner || useTestIdentifiers) && ( + (isOpen.value = true)}> - {"Sponsorship Data"} + {forumText} - + )} { alignItems: "center", }} > - {"New Forum"} + {forumText} @@ -157,7 +152,7 @@ export const NewForumModal = () => { height: "60px", borderBottom: "1px solid gray", }} - maxLength={30} + maxLength={titleMaxLength} /> { >

Description

{ - description.value = val; + inlineContent={descriptionHTML.value} + setInlineContent={( + value: any, + delta: any, + source: any, + editor: any + ) => { + descriptionHTML.value = value; + descriptionText.value = editor.getText(value); }} />
@@ -194,8 +195,14 @@ export const NewForumModal = () => { height: "90px", }} > - publishForum(formData.value)}> - {"Create Forum"} + { + publishForum(formData.value).then(success => { + if (success) closeModal(); + }); + }} + > + {publishText} { + return <>; +}; diff --git a/src/pages/Forum/GroupPermissionsForm.tsx b/src/pages/Forum/GroupPermissionsForm.tsx index 10c7db4..fd948cd 100644 --- a/src/pages/Forum/GroupPermissionsForm.tsx +++ b/src/pages/Forum/GroupPermissionsForm.tsx @@ -16,12 +16,11 @@ import React, { useEffect } from "react"; import ShortUniqueId from "short-unique-id"; import { SelectField } from "../../components/common/SelectField"; import { NewMessageInputRow } from "../Home/Home-styles"; -import { GroupPermissions, GroupPermissionType } from "./Forum"; +import { GroupPermissionType } from "./ForumModal"; import { QmailTextField } from "./QmailTextField"; export interface Group { id: Signal; - name: string; permissions: Signal; } export interface GroupPermissionsFormProps { @@ -33,7 +32,6 @@ export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => { const uid = new ShortUniqueId(); const newGroup = { id: signal(""), - name: "", permissions: signal("Read"), } as Group; const addGroup = () => { diff --git a/src/pages/Forum/NewForumModal-data.ts b/src/pages/Forum/NewForumModal-data.ts deleted file mode 100644 index 0e9fa2d..0000000 --- a/src/pages/Forum/NewForumModal-data.ts +++ /dev/null @@ -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"); - // } -}; diff --git a/src/pages/Home/Home-styles.ts b/src/pages/Home/Home-styles.ts index 5509dbe..12cc9c1 100644 --- a/src/pages/Home/Home-styles.ts +++ b/src/pages/Home/Home-styles.ts @@ -15,7 +15,6 @@ export const InstanceContainer = styled(Box)(({ theme }) => ({ backgroundColor: "var(--color-instance)", height: "59px", flexShrink: 0, - justifyContent: "space-between", })); export const MailContainer = styled(Box)(({ theme }) => ({ display: "flex", diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 3b763e0..c2787c6 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,98 +1,19 @@ import { Box } from "@mui/material"; -import { signal } from "@preact/signals-react"; -import React, { - useCallback, - useEffect, - useMemo, - 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"; +import { useSignals } from "@preact/signals-react/runtime"; +import React from "react"; +import { forums } from "../../App"; +import { ActionBar } from "../Forum/ActionBar"; +import { Forum } from "../Forum/Forum"; export const Home = () => { - const { user } = useSelector((state: RootState) => state.auth); - - 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(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]); + useSignals(); return ( - + + {forums.value.map(forum => ( + + ))} ); }; diff --git a/src/pages/Mail/GroupMail.tsx b/src/pages/Mail/GroupMail.tsx index 3353735..2ad246e 100644 --- a/src/pages/Mail/GroupMail.tsx +++ b/src/pages/Mail/GroupMail.tsx @@ -8,7 +8,7 @@ import React, { } from "react"; import { useNavigate } from "react-router-dom"; 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 EditIcon from "@mui/icons-material/Edit"; import { @@ -214,7 +214,7 @@ export const GroupMail = ({ .join(""); 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 response = await fetch(url, { method: "GET", diff --git a/src/pages/Mail/Thread.tsx b/src/pages/Mail/Thread.tsx index 0c810e4..45006fc 100644 --- a/src/pages/Mail/Thread.tsx +++ b/src/pages/Mail/Thread.tsx @@ -8,7 +8,7 @@ import React, { } from "react"; import { useNavigate } from "react-router-dom"; 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 { @@ -99,7 +99,7 @@ export const Thread = ({ let result = parts[0]; const threadId = result; 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 response = await fetch(url, { method: "GET", @@ -195,7 +195,7 @@ export const Thread = ({ let parts = str.split("_").reverse(); let result = parts[0]; 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 response = await fetch(url, { method: "GET", diff --git a/src/utils/QortalRequests.ts b/src/utils/QortalRequests.ts index f179a03..b56d995 100644 --- a/src/utils/QortalRequests.ts +++ b/src/utils/QortalRequests.ts @@ -1,3 +1,6 @@ +import { FORUMS_ID } from "../constants/Identifiers"; +import { appOwner } from "../constants/Misc"; + export interface GroupData { groupId: number; owner: string; @@ -17,11 +20,30 @@ export const listGroups = async () => { export const getGroup = async (groupID: number | string) => { const url = `/groups/${groupID.toString()}`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - return (await response.json()) as GroupData; + + try { + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + 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; + } }; diff --git a/src/utils/toBase64.ts b/src/utils/toBase64.ts index edb7fb6..627cbf5 100644 --- a/src/utils/toBase64.ts +++ b/src/utils/toBase64.ts @@ -25,7 +25,6 @@ export const objectToBase64 = async (obj: any): Promise => { "data:application/json;base64,", "" ); - console.log(`base64 resolution: ${base64}`); resolve(base64); } else { reject(new Error("Failed to read the Blob as a base64-encoded string"));