New Forum Modal design implmented. Forum Submission in progress.
This commit is contained in:
parent
8c76029d0c
commit
11d5a46f0c
@ -22,9 +22,9 @@ function App() {
|
|||||||
<DownloadWrapper>
|
<DownloadWrapper>
|
||||||
<GlobalWrapper>
|
<GlobalWrapper>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="/sponsorshipData" element={<Home />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</GlobalWrapper>
|
</GlobalWrapper>
|
||||||
</DownloadWrapper>
|
</DownloadWrapper>
|
||||||
|
56
src/components/common/SelectField.tsx
Normal file
56
src/components/common/SelectField.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
MenuItem,
|
||||||
|
OutlinedInput,
|
||||||
|
Select,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { SxProps } from "@mui/system";
|
||||||
|
import { signal, Signal } from "@preact/signals-react";
|
||||||
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
interface SelectFieldProps {
|
||||||
|
options: string[];
|
||||||
|
value: Signal<string>;
|
||||||
|
label: string;
|
||||||
|
sx?: SxProps;
|
||||||
|
}
|
||||||
|
export const SelectField = ({
|
||||||
|
options,
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
sx = {},
|
||||||
|
}: SelectFieldProps) => {
|
||||||
|
useSignals();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel
|
||||||
|
sx={{
|
||||||
|
fontSize: "100%",
|
||||||
|
color: "rgba(84, 84, 84, 0.70)",
|
||||||
|
}}
|
||||||
|
id={label}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
fullWidth
|
||||||
|
labelId={label}
|
||||||
|
value={value.value || ""}
|
||||||
|
variant={"standard"}
|
||||||
|
onChange={e => {
|
||||||
|
value.value = e.target.value;
|
||||||
|
}}
|
||||||
|
sx={{ color: "black", ...sx }}
|
||||||
|
>
|
||||||
|
{options.map(option => (
|
||||||
|
<MenuItem key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
@ -9,3 +9,5 @@ export const AUDIO_BASE = `${appName}_qaudio`;
|
|||||||
export const FORUM_BASE = `${appName}_forum`;
|
export const FORUM_BASE = `${appName}_forum`;
|
||||||
export const THREAD_BASE = `${appName}_thread`;
|
export const THREAD_BASE = `${appName}_thread`;
|
||||||
export const THREAD_MESSAGE = `${appName}_thread_message`;
|
export const THREAD_MESSAGE = `${appName}_thread_message`;
|
||||||
|
|
||||||
|
export const FORUM_GROUPID = 0; // will be determined later
|
||||||
|
@ -1 +1,4 @@
|
|||||||
export const appOwner = "Q-Mintership";
|
export const appOwner = "Q-Mintership";
|
||||||
|
|
||||||
|
const maxSizeMB = 25;
|
||||||
|
export const maxPrivateFileSize = maxSizeMB * 1024 * 1024;
|
||||||
|
@ -9,12 +9,20 @@ 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";
|
||||||
|
|
||||||
|
export type GroupPermissionType = "Read" | "Write";
|
||||||
|
export interface GroupPermissions {
|
||||||
|
id: string;
|
||||||
|
permissions: GroupPermissionType;
|
||||||
|
}
|
||||||
|
|
||||||
export type EncryptionType = "None" | "Group" | "GroupAdmin";
|
|
||||||
interface ForumProps {
|
interface ForumProps {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
encryption?: EncryptionType;
|
encryption?: EncryptionType;
|
||||||
|
groupID: string;
|
||||||
|
groupPermissions: GroupPermissions[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Forum = ({ encryption = "None" }: ForumProps) => {
|
export const Forum = ({ encryption = "None" }: ForumProps) => {
|
||||||
|
130
src/pages/Forum/GroupPermissionsForm.tsx
Normal file
130
src/pages/Forum/GroupPermissionsForm.tsx
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import RemoveIcon from "@mui/icons-material/Remove";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
MenuItem,
|
||||||
|
OutlinedInput,
|
||||||
|
Select,
|
||||||
|
SelectChangeEvent,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { styled } from "@mui/material/styles";
|
||||||
|
import { signal, Signal } from "@preact/signals-react";
|
||||||
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
|
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 { QmailTextField } from "./QmailTextField";
|
||||||
|
|
||||||
|
export interface Group {
|
||||||
|
id: Signal<string>;
|
||||||
|
name: string;
|
||||||
|
permissions: Signal<GroupPermissionType>;
|
||||||
|
}
|
||||||
|
export interface GroupPermissionsFormProps {
|
||||||
|
groups: Signal<Group[]>;
|
||||||
|
}
|
||||||
|
export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => {
|
||||||
|
useSignals();
|
||||||
|
|
||||||
|
const uid = new ShortUniqueId();
|
||||||
|
const newGroup = {
|
||||||
|
id: signal<string>(""),
|
||||||
|
name: "",
|
||||||
|
permissions: signal<GroupPermissionType>("Read"),
|
||||||
|
} as Group;
|
||||||
|
const addGroup = () => {
|
||||||
|
groups.value = [...groups.value, newGroup];
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeGroup = (groupIndex: number) => {
|
||||||
|
if (groups.value.length > 1)
|
||||||
|
groups.value = groups.value.filter(
|
||||||
|
(group, index) => index !== groupIndex
|
||||||
|
);
|
||||||
|
else groups.value = [newGroup];
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonStyle = {
|
||||||
|
width: "70px",
|
||||||
|
height: "70px",
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledIconButton = styled(IconButton)(`
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 4px;
|
||||||
|
&:hover {
|
||||||
|
background-color: #cccdd0;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const iconStyle = { fontSize: 50, color: "#2e3d60" };
|
||||||
|
const options = ["Read", "Write"];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
|
||||||
|
width: "100%",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{groups.value.map((group, index) => (
|
||||||
|
<NewMessageInputRow
|
||||||
|
sx={{
|
||||||
|
height: "80px",
|
||||||
|
width: "100%",
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "1fr 1fr",
|
||||||
|
alignItems: "end",
|
||||||
|
}}
|
||||||
|
key={uid()}
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<StyledIconButton
|
||||||
|
onClick={addGroup}
|
||||||
|
sx={{
|
||||||
|
visibility:
|
||||||
|
index === groups.value.length - 1 ? "visible" : "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AddIcon sx={iconStyle} />
|
||||||
|
</StyledIconButton>
|
||||||
|
|
||||||
|
<StyledIconButton onClick={() => removeGroup(index)}>
|
||||||
|
<RemoveIcon sx={{ ...iconStyle, fontSize: 50 }} />
|
||||||
|
</StyledIconButton>
|
||||||
|
<QmailTextField
|
||||||
|
value={group.id}
|
||||||
|
label={"Group ID"}
|
||||||
|
filter={/[^0-9]/}
|
||||||
|
sx={{
|
||||||
|
width: "50%",
|
||||||
|
height: "70px",
|
||||||
|
borderBottom: "1px solid gray",
|
||||||
|
}}
|
||||||
|
maxLength={10}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<SelectField
|
||||||
|
options={options}
|
||||||
|
value={group.permissions}
|
||||||
|
label={"Group Permissions"}
|
||||||
|
sx={{
|
||||||
|
"& .MuiSvgIcon-root": {
|
||||||
|
color: "gray",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</NewMessageInputRow>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
289
src/pages/Forum/NewForumModal-data.ts
Normal file
289
src/pages/Forum/NewForumModal-data.ts
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
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");
|
||||||
|
// }
|
||||||
|
};
|
211
src/pages/Forum/NewForumModal.tsx
Normal file
211
src/pages/Forum/NewForumModal.tsx
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
import { Box, Input, Typography } 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 { 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 { RootState } from "../../state/store";
|
||||||
|
import { formatBytes } from "../../utils/displaySize";
|
||||||
|
import { getFileExtension } from "../../utils/helpers";
|
||||||
|
import {
|
||||||
|
AttachmentContainer,
|
||||||
|
CloseContainer,
|
||||||
|
ComposeContainer,
|
||||||
|
ComposeIcon,
|
||||||
|
ComposeP,
|
||||||
|
InstanceContainer,
|
||||||
|
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 { QmailTextField } from "./QmailTextField";
|
||||||
|
|
||||||
|
export type EncryptionType = "None" | "Group" | "GroupAdmin";
|
||||||
|
export type NewForumModalData = {
|
||||||
|
name: string | undefined;
|
||||||
|
title: string;
|
||||||
|
encryption: "None" | "Group" | "GroupAdmin" | "";
|
||||||
|
groups: Group[];
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NewForumModal = () => {
|
||||||
|
useSignals();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const isOpen = useSignal<boolean>(false);
|
||||||
|
|
||||||
|
const forumTitle = useSignal<string>("");
|
||||||
|
const description = useSignal<string>("");
|
||||||
|
|
||||||
|
const selectedEncryptionType = useSignal<EncryptionType | "">("None");
|
||||||
|
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>(""),
|
||||||
|
name: "",
|
||||||
|
permissions: signal<GroupPermissionType>("Read"),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InstanceContainer>
|
||||||
|
{(user?.name === appOwner || useTestIdentifiers) && (
|
||||||
|
<ComposeContainer onClick={e => (isOpen.value = true)}>
|
||||||
|
<ComposeIcon src={ComposeIconSVG} />
|
||||||
|
<ComposeP>{"New Forum"}</ComposeP>
|
||||||
|
</ComposeContainer>
|
||||||
|
)}
|
||||||
|
<ComposeContainer onClick={e => navigate("/sponsorshipData")}>
|
||||||
|
<ComposeIcon src={ComposeIconSVG} />
|
||||||
|
<ComposeP>{"Sponsorship Data"}</ComposeP>
|
||||||
|
</ComposeContainer>
|
||||||
|
</InstanceContainer>
|
||||||
|
<ReusableModal
|
||||||
|
open={isOpen.value}
|
||||||
|
customStyles={{
|
||||||
|
maxHeight: "95vh",
|
||||||
|
maxWidth: "950px",
|
||||||
|
height: "700px",
|
||||||
|
borderRadius: "12px 12px 0px 0px",
|
||||||
|
background: "var(--Mail-Background, #313338)",
|
||||||
|
padding: "0px",
|
||||||
|
gap: "0px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InstanceListHeader
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "unset",
|
||||||
|
height: "50px",
|
||||||
|
padding: "20px 42px",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewMessageHeaderP>{"New Forum"}</NewMessageHeaderP>
|
||||||
|
<CloseContainer onClick={closeModal}>
|
||||||
|
<NewMessageCloseImg src={ModalCloseSVG} />
|
||||||
|
</CloseContainer>
|
||||||
|
</InstanceListHeader>
|
||||||
|
<InstanceListContainer
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "rgba(217, 217, 217, 1)",
|
||||||
|
padding: "20px 42px",
|
||||||
|
height: "calc(100% - 150px)",
|
||||||
|
flexShrink: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewMessageInputRow sx={{ height: "80px", alignItems: "end" }}>
|
||||||
|
<QmailTextField
|
||||||
|
value={forumTitle}
|
||||||
|
label={"Forum Title"}
|
||||||
|
sx={{
|
||||||
|
height: "60px",
|
||||||
|
borderBottom: "1px solid gray",
|
||||||
|
}}
|
||||||
|
maxLength={30}
|
||||||
|
/>
|
||||||
|
<SelectField
|
||||||
|
options={["None", "Group", "GroupAdmin"]}
|
||||||
|
label={"Encryption Type"}
|
||||||
|
value={selectedEncryptionType}
|
||||||
|
sx={{
|
||||||
|
"& .MuiSvgIcon-root": {
|
||||||
|
color: "gray",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</NewMessageInputRow>
|
||||||
|
<GroupPermissionsForm groups={groups} />
|
||||||
|
<Spacer height="40px" />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
maxHeight: "40vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p style={{ color: "black" }}> Description </p>
|
||||||
|
<TextEditor
|
||||||
|
inlineContent={description.value}
|
||||||
|
setInlineContent={(val: any) => {
|
||||||
|
description.value = val;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</InstanceListContainer>
|
||||||
|
<InstanceFooter
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "rgba(217, 217, 217, 1)",
|
||||||
|
padding: "20px 42px",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "90px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewMessageSendButton onClick={() => publishForum(formData.value)}>
|
||||||
|
<NewMessageSendP>{"Create Forum"}</NewMessageSendP>
|
||||||
|
|
||||||
|
<CreateThreadIcon
|
||||||
|
color="red"
|
||||||
|
opacity={1}
|
||||||
|
height="25px"
|
||||||
|
width="25px"
|
||||||
|
/>
|
||||||
|
</NewMessageSendButton>
|
||||||
|
</InstanceFooter>
|
||||||
|
</ReusableModal>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
58
src/pages/Forum/QmailTextField.tsx
Normal file
58
src/pages/Forum/QmailTextField.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Input } from "@mui/material";
|
||||||
|
import { SxProps } from "@mui/system";
|
||||||
|
import { Signal, useSignalEffect } from "@preact/signals-react";
|
||||||
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface QmailTextFieldProps {
|
||||||
|
value: Signal<string>;
|
||||||
|
label: string;
|
||||||
|
sx?: SxProps;
|
||||||
|
filter?: RegExp;
|
||||||
|
maxLength?: number;
|
||||||
|
}
|
||||||
|
export const QmailTextField = ({
|
||||||
|
value,
|
||||||
|
label,
|
||||||
|
sx = {},
|
||||||
|
filter,
|
||||||
|
maxLength = 60,
|
||||||
|
}: QmailTextFieldProps) => {
|
||||||
|
useSignals();
|
||||||
|
useSignalEffect(() => {
|
||||||
|
if (filter) value.value = value.value.replace(filter, "");
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
id="standard-adornment-name"
|
||||||
|
value={value.value}
|
||||||
|
onChange={e => {
|
||||||
|
if (e.target.value.length <= maxLength) value.value = e.target.value;
|
||||||
|
}}
|
||||||
|
placeholder={label}
|
||||||
|
disableUnderline
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
color: "black",
|
||||||
|
"& .MuiInput-input::placeholder": {
|
||||||
|
color: "rgba(84, 84, 84, 0.70) !important",
|
||||||
|
fontSize: "100%",
|
||||||
|
fontStyle: "normal",
|
||||||
|
fontWeight: 400,
|
||||||
|
lineHeight: "120%", // 24px
|
||||||
|
letterSpacing: "0.15px",
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
"&:focus": {
|
||||||
|
outline: "none",
|
||||||
|
},
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -419,7 +419,6 @@ export const NewMessageInputRow = styled(Box)(({ theme }) => ({
|
|||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
borderBottom: "3px solid rgba(237, 239, 241, 1)",
|
borderBottom: "3px solid rgba(237, 239, 241, 1)",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
paddingBottom: "6px",
|
|
||||||
}));
|
}));
|
||||||
export const NewMessageInputLabelP = styled(Typography)`
|
export const NewMessageInputLabelP = styled(Typography)`
|
||||||
color: rgba(84, 84, 84, 0.7);
|
color: rgba(84, 84, 84, 0.7);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
|
import { signal } from "@preact/signals-react";
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
@ -13,6 +14,7 @@ import { appOwner } from "../../constants/Misc";
|
|||||||
import { useFetchMail } from "../../hooks/useFetchMail";
|
import { useFetchMail } from "../../hooks/useFetchMail";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import { executeEvent } from "../../utils/events";
|
import { executeEvent } from "../../utils/events";
|
||||||
|
import { NewForumModal } from "../Forum/NewForumModal";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ComposeContainer,
|
ComposeContainer,
|
||||||
@ -88,20 +90,9 @@ export const Home = () => {
|
|||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const publishNewForum = () => {
|
|
||||||
executeEvent("openNewThreadModal", {});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<InstanceContainer>
|
<NewForumModal />
|
||||||
{(user?.name === appOwner || useTestIdentifiers) && (
|
|
||||||
<ComposeContainer onClick={publishNewForum}>
|
|
||||||
<ComposeIcon src={ComposeIconSVG} />
|
|
||||||
<ComposeP>{"New Forum"}</ComposeP>
|
|
||||||
</ComposeContainer>
|
|
||||||
)}
|
|
||||||
</InstanceContainer>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -191,7 +191,7 @@ export const NewThread = ({
|
|||||||
};
|
};
|
||||||
}, [openModalPostFromEvent]);
|
}, [openModalPostFromEvent]);
|
||||||
|
|
||||||
async function publishQDNResource() {
|
const publishQDNResource = async () => {
|
||||||
let name: string = "";
|
let name: string = "";
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ export const NewThread = ({
|
|||||||
|
|
||||||
throw new Error("Failed to send message");
|
throw new Error("Failed to send message");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const sendMail = () => {
|
const sendMail = () => {
|
||||||
publishQDNResource();
|
publishQDNResource();
|
||||||
|
27
src/utils/QortalRequests.ts
Normal file
27
src/utils/QortalRequests.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export interface GroupData {
|
||||||
|
groupId: number;
|
||||||
|
owner: string;
|
||||||
|
groupName: string;
|
||||||
|
description: string;
|
||||||
|
created: number;
|
||||||
|
isOpen: boolean;
|
||||||
|
approvalThreshold: string;
|
||||||
|
minimumBlockDelay: number;
|
||||||
|
maximumBlockDelay: number;
|
||||||
|
memberCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listGroups = async () => {
|
||||||
|
return (await qortalRequest({ action: "LIST_GROUPS" })) as GroupData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
@ -1,125 +1,121 @@
|
|||||||
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader()
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file)
|
reader.readAsDataURL(file);
|
||||||
reader.onload = () => resolve(reader.result)
|
reader.onload = () => resolve(reader.result);
|
||||||
reader.onerror = (error) => {
|
reader.onerror = error => {
|
||||||
reject(error)
|
reject(error);
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
export function objectToBase64(obj: any) {
|
export const objectToBase64 = async (obj: any): Promise<string> => {
|
||||||
// Step 1: Convert the object to a JSON string
|
// Step 1: Convert the object to a JSON string
|
||||||
const jsonString = JSON.stringify(obj)
|
const jsonString = JSON.stringify(obj);
|
||||||
|
|
||||||
// Step 2: Create a Blob from the JSON string
|
// Step 2: Create a Blob from the JSON string
|
||||||
const blob = new Blob([jsonString], { type: 'application/json' })
|
const blob = new Blob([jsonString], { type: "application/json" });
|
||||||
|
|
||||||
// Step 3: Create a FileReader to read the Blob as a base64-encoded string
|
// Step 3: Create a FileReader to read the Blob as a base64-encoded string
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader()
|
const reader = new FileReader();
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
if (typeof reader.result === 'string') {
|
if (typeof reader.result === "string") {
|
||||||
// Remove 'data:application/json;base64,' prefix
|
// Remove 'data:application/json;base64,' prefix
|
||||||
const base64 = reader.result.replace(
|
const base64 = reader.result.replace(
|
||||||
'data:application/json;base64,',
|
"data:application/json;base64,",
|
||||||
''
|
""
|
||||||
)
|
);
|
||||||
resolve(base64)
|
console.log(`base64 resolution: ${base64}`);
|
||||||
} else {
|
resolve(base64);
|
||||||
reject(
|
} else {
|
||||||
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"));
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
reader.onerror = () => {
|
};
|
||||||
reject(reader.error)
|
reader.onerror = () => {
|
||||||
}
|
reject(reader.error);
|
||||||
reader.readAsDataURL(blob)
|
};
|
||||||
})
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export function objectToUint8Array(obj: any) {
|
||||||
|
// Convert the object to a JSON string
|
||||||
|
const jsonString = JSON.stringify(obj);
|
||||||
|
|
||||||
|
// Encode the JSON string as a byte array using TextEncoder
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const byteArray = encoder.encode(jsonString);
|
||||||
|
|
||||||
|
// Create a new Uint8Array and set its content to the encoded byte array
|
||||||
|
const uint8Array = new Uint8Array(byteArray);
|
||||||
|
|
||||||
|
return uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uint8ArrayToBase64(uint8Array: Uint8Array): string {
|
||||||
|
const length = uint8Array.length;
|
||||||
|
let binaryString = "";
|
||||||
|
const chunkSize = 1024 * 1024; // Process 1MB at a time
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i += chunkSize) {
|
||||||
|
const chunkEnd = Math.min(i + chunkSize, length);
|
||||||
|
const chunk = uint8Array.subarray(i, chunkEnd);
|
||||||
|
binaryString += Array.from(chunk, byte => String.fromCharCode(byte)).join(
|
||||||
|
""
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function objectToUint8Array(obj: any) {
|
return btoa(binaryString);
|
||||||
// Convert the object to a JSON string
|
}
|
||||||
const jsonString = JSON.stringify(obj)
|
|
||||||
|
|
||||||
// Encode the JSON string as a byte array using TextEncoder
|
export function objectToUint8ArrayFromResponse(obj: any) {
|
||||||
const encoder = new TextEncoder()
|
const len = Object.keys(obj).length;
|
||||||
const byteArray = encoder.encode(jsonString)
|
const result = new Uint8Array(len);
|
||||||
|
|
||||||
// Create a new Uint8Array and set its content to the encoded byte array
|
for (let i = 0; i < len; i++) {
|
||||||
const uint8Array = new Uint8Array(byteArray)
|
result[i] = obj[i];
|
||||||
|
|
||||||
return uint8Array
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// export function uint8ArrayToBase64(arrayBuffer: Uint8Array): string {
|
||||||
|
// let binary = ''
|
||||||
|
// const bytes = new Uint8Array(arrayBuffer)
|
||||||
|
// const len = bytes.length
|
||||||
|
|
||||||
export function uint8ArrayToBase64(uint8Array: Uint8Array): string {
|
// for (let i = 0; i < len; i++) {
|
||||||
const length = uint8Array.length
|
// binary += String.fromCharCode(bytes[i])
|
||||||
let binaryString = ''
|
// }
|
||||||
const chunkSize = 1024 * 1024 // Process 1MB at a time
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i += chunkSize) {
|
// return btoa(binary)
|
||||||
const chunkEnd = Math.min(i + chunkSize, length)
|
// }
|
||||||
const chunk = uint8Array.subarray(i, chunkEnd)
|
|
||||||
binaryString += Array.from(chunk, (byte) =>
|
|
||||||
String.fromCharCode(byte)
|
|
||||||
).join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
return btoa(binaryString)
|
export function base64ToUint8Array(base64: string) {
|
||||||
}
|
const binaryString = atob(base64);
|
||||||
|
const len = binaryString.length;
|
||||||
|
const bytes = new Uint8Array(len);
|
||||||
|
|
||||||
export function objectToUint8ArrayFromResponse(obj: any) {
|
for (let i = 0; i < len; i++) {
|
||||||
const len = Object.keys(obj).length
|
bytes[i] = binaryString.charCodeAt(i);
|
||||||
const result = new Uint8Array(len)
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
result[i] = obj[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
// export function uint8ArrayToBase64(arrayBuffer: Uint8Array): string {
|
|
||||||
// let binary = ''
|
|
||||||
// const bytes = new Uint8Array(arrayBuffer)
|
|
||||||
// const len = bytes.length
|
|
||||||
|
|
||||||
// for (let i = 0; i < len; i++) {
|
|
||||||
// binary += String.fromCharCode(bytes[i])
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return btoa(binary)
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function base64ToUint8Array(base64: string) {
|
|
||||||
const binaryString = atob(base64)
|
|
||||||
const len = binaryString.length
|
|
||||||
const bytes = new Uint8Array(len)
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
bytes[i] = binaryString.charCodeAt(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function uint8ArrayToObject(uint8Array: Uint8Array) {
|
return bytes;
|
||||||
// Decode the byte array using TextDecoder
|
}
|
||||||
const decoder = new TextDecoder()
|
|
||||||
const jsonString = decoder.decode(uint8Array)
|
|
||||||
|
|
||||||
// Convert the JSON string back into an object
|
export function uint8ArrayToObject(uint8Array: Uint8Array) {
|
||||||
const obj = JSON.parse(jsonString)
|
// Decode the byte array using TextDecoder
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const jsonString = decoder.decode(uint8Array);
|
||||||
|
|
||||||
return obj
|
// Convert the JSON string back into an object
|
||||||
}
|
const obj = JSON.parse(jsonString);
|
||||||
|
|
||||||
export function processFileInChunks(file: File): Promise<Uint8Array> {
|
return obj;
|
||||||
return new Promise((resolve: (value: Uint8Array) => void, reject: (reason?: any) => void) => {
|
}
|
||||||
|
|
||||||
|
export function processFileInChunks(file: File): Promise<Uint8Array> {
|
||||||
|
return new Promise(
|
||||||
|
(resolve: (value: Uint8Array) => void, reject: (reason?: any) => void) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = function (event: ProgressEvent<FileReader>) {
|
reader.onload = function (event: ProgressEvent<FileReader>) {
|
||||||
@ -133,38 +129,38 @@ export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
|||||||
};
|
};
|
||||||
|
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
});
|
}
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// export async function processFileInChunks(file: File, chunkSize = 1024 * 1024): Promise<Uint8Array> {
|
// export async function processFileInChunks(file: File, chunkSize = 1024 * 1024): Promise<Uint8Array> {
|
||||||
// const fileStream = file.stream();
|
// const fileStream = file.stream();
|
||||||
// const reader = fileStream.getReader();
|
// const reader = fileStream.getReader();
|
||||||
// const totalLength = file.size;
|
// const totalLength = file.size;
|
||||||
|
|
||||||
// if (totalLength <= 0 || isNaN(totalLength)) {
|
// if (totalLength <= 0 || isNaN(totalLength)) {
|
||||||
// throw new Error('Invalid file size');
|
// throw new Error('Invalid file size');
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const combinedArray = new Uint8Array(totalLength);
|
// const combinedArray = new Uint8Array(totalLength);
|
||||||
// let offset = 0;
|
// let offset = 0;
|
||||||
|
|
||||||
// while (offset < totalLength) {
|
// while (offset < totalLength) {
|
||||||
// const { value, done } = await reader.read();
|
// const { value, done } = await reader.read();
|
||||||
|
|
||||||
// if (done) {
|
// if (done) {
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// const chunk = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
// const chunk = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
||||||
|
|
||||||
// // Set elements one by one instead of using combinedArray.set(chunk, offset)
|
// // Set elements one by one instead of using combinedArray.set(chunk, offset)
|
||||||
// for (let i = 0; i < chunk.length; i++) {
|
// for (let i = 0; i < chunk.length; i++) {
|
||||||
// combinedArray[offset + i] = chunk[i];
|
// combinedArray[offset + i] = chunk[i];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// offset += chunk.length;
|
// offset += chunk.length;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// return combinedArray;
|
// return combinedArray;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user