Browse Source
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.master
Qortal Dev
2 days ago
18 changed files with 365 additions and 495 deletions
@ -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; |
||||
|
@ -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> |
||||
); |
||||
}; |
@ -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; |
||||
}; |
@ -0,0 +1,3 @@
|
||||
export const ForumThreads = () => { |
||||
return <></>; |
||||
}; |
@ -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");
|
||||
// }
|
||||
}; |
@ -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<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]); |
||||
useSignals(); |
||||
|
||||
return ( |
||||
<Box> |
||||
<NewForumModal /> |
||||
<ActionBar /> |
||||
{forums.value.map(forum => ( |
||||
<Forum key={forum.title} {...forum} /> |
||||
))} |
||||
</Box> |
||||
); |
||||
}; |
||||
|
Loading…
Reference in new issue