Publish Forum button changed to Manage Forums. It handles both publishing and editing forums.
This commit is contained in:
parent
0659433f03
commit
dd500a3ebb
28
src/App.tsx
28
src/App.tsx
@ -1,31 +1,20 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { signal } from "@preact/signals-react";
|
import { CssBaseline } from "@mui/material";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { Routes, Route } from "react-router-dom";
|
|
||||||
|
|
||||||
import { ThemeProvider } from "@mui/material/styles";
|
import { ThemeProvider } from "@mui/material/styles";
|
||||||
import { CssBaseline } from "@mui/material";
|
|
||||||
import { 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 { Provider } from "react-redux";
|
||||||
import { fetchForumData } from "./utils/QortalRequests";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
|
||||||
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
|
||||||
import Notification from "./components/common/Notification/Notification";
|
import Notification from "./components/common/Notification/Notification";
|
||||||
|
import { Home } from "./pages/Home/Home";
|
||||||
export const forums = signal<ForumData[]>([]);
|
import { store } from "./state/store";
|
||||||
|
import { darkTheme } from "./styles/theme";
|
||||||
|
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
||||||
|
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const themeColor = window._qdnTheme;
|
const themeColor = window._qdnTheme;
|
||||||
useEffect(() => {
|
|
||||||
fetchForumData().then(data => {
|
|
||||||
if (data) forums.value = data;
|
|
||||||
console.log("forums is : ", forums.value);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ThemeProvider theme={darkTheme}>
|
<ThemeProvider theme={darkTheme}>
|
||||||
@ -35,6 +24,7 @@ function App() {
|
|||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="/forum/:" element={<Home />} />
|
||||||
<Route path="/sponsorshipData" element={<Home />} />
|
<Route path="/sponsorshipData" element={<Home />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</GlobalWrapper>
|
</GlobalWrapper>
|
||||||
|
@ -4,28 +4,38 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
OutlinedInput,
|
OutlinedInput,
|
||||||
Select,
|
Select,
|
||||||
|
SelectChangeEvent,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { SxProps } from "@mui/system";
|
import { SxProps } from "@mui/system";
|
||||||
import { signal, Signal } from "@preact/signals-react";
|
import { signal, Signal, useSignal } from "@preact/signals-react";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
interface SelectFieldProps {
|
interface SelectFieldProps {
|
||||||
options: string[];
|
options: string[];
|
||||||
value: Signal<string>;
|
initialValue?: string;
|
||||||
label: string;
|
label: string;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
|
afterChange?: (e: string) => void;
|
||||||
|
width?: string;
|
||||||
}
|
}
|
||||||
export const SelectField = ({
|
export const SelectField = ({
|
||||||
options,
|
options,
|
||||||
value,
|
initialValue = "",
|
||||||
label,
|
label,
|
||||||
sx = {},
|
sx = {},
|
||||||
|
width = "100%",
|
||||||
|
afterChange,
|
||||||
}: SelectFieldProps) => {
|
}: SelectFieldProps) => {
|
||||||
useSignals();
|
useSignals();
|
||||||
|
|
||||||
|
const value = useSignal<string>(initialValue);
|
||||||
|
useEffect(() => {
|
||||||
|
value.value = initialValue;
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth sx={{ width: width }}>
|
||||||
<InputLabel
|
<InputLabel
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "100%",
|
fontSize: "100%",
|
||||||
@ -41,7 +51,9 @@ export const SelectField = ({
|
|||||||
value={value.value || ""}
|
value={value.value || ""}
|
||||||
variant={"standard"}
|
variant={"standard"}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
value.value = e.target.value;
|
const eventValue = e.target.value;
|
||||||
|
value.value = eventValue;
|
||||||
|
if (afterChange) afterChange(eventValue);
|
||||||
}}
|
}}
|
||||||
sx={{ color: "black", ...sx }}
|
sx={{ color: "black", ...sx }}
|
||||||
>
|
>
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
49
src/pages/Forum/Components/ActionBar.tsx
Normal file
49
src/pages/Forum/Components/ActionBar.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
||||||
|
import { Signal, signal } from "@preact/signals-react";
|
||||||
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { fetchForumData } from "../../../utils/QortalRequests";
|
||||||
|
import {
|
||||||
|
ComposeContainer,
|
||||||
|
ComposeP,
|
||||||
|
InstanceContainer,
|
||||||
|
} from "../../Home/Home-styles";
|
||||||
|
import { ForumData, ForumModal } from "../ForumModal";
|
||||||
|
|
||||||
|
export const forums = signal<ForumData[]>([]);
|
||||||
|
export const resetForumIndexes = (forums: Signal<ForumData[]>) => {
|
||||||
|
forums.value = forums.value.map((forum, index) => {
|
||||||
|
forum.listIndex = index;
|
||||||
|
return forum;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ActionBar = () => {
|
||||||
|
useSignals();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchForumData().then(data => {
|
||||||
|
if (data)
|
||||||
|
forums.value = data.map((forum, index) => {
|
||||||
|
forum.listIndex = index;
|
||||||
|
return forum;
|
||||||
|
});
|
||||||
|
// console.log("forums are : ", forums.value);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<InstanceContainer>
|
||||||
|
<ForumModal forumData={forums} />
|
||||||
|
<ComposeContainer
|
||||||
|
sx={{ width: "200px", marginLeft: "auto" }}
|
||||||
|
onClick={() => navigate("/sponsorshipData")}
|
||||||
|
>
|
||||||
|
<MenuBookIcon />
|
||||||
|
<ComposeP sx={{ fontSize: "70%" }}>{"Sponsorship Data"}</ComposeP>
|
||||||
|
</ComposeContainer>
|
||||||
|
</InstanceContainer>
|
||||||
|
);
|
||||||
|
};
|
121
src/pages/Forum/Components/ModalButton.tsx
Normal file
121
src/pages/Forum/Components/ModalButton.tsx
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { Box } from "@mui/material";
|
||||||
|
import { useSignal } from "@preact/signals-react";
|
||||||
|
import React, { PropsWithChildren } from "react";
|
||||||
|
import ComposeIconSVG from "../../../assets/svgs/ComposeIcon.svg";
|
||||||
|
import { CreateThreadIcon } from "../../../assets/svgs/CreateThreadIcon";
|
||||||
|
import ModalCloseSVG from "../../../assets/svgs/ModalClose.svg";
|
||||||
|
import { ReusableModal } from "../../../components/modals/ReusableModal";
|
||||||
|
import {
|
||||||
|
CloseContainer,
|
||||||
|
ComposeContainer,
|
||||||
|
ComposeIcon,
|
||||||
|
ComposeP,
|
||||||
|
InstanceFooter,
|
||||||
|
InstanceListContainer,
|
||||||
|
InstanceListHeader,
|
||||||
|
NewMessageCloseImg,
|
||||||
|
NewMessageHeaderP,
|
||||||
|
NewMessageSendButton,
|
||||||
|
NewMessageSendP,
|
||||||
|
} from "../../Home/Home-styles";
|
||||||
|
import { publishForum } from "../ForumModal-Data";
|
||||||
|
|
||||||
|
export interface ModalButtonProps {
|
||||||
|
onSubmit: () => Promise<boolean>;
|
||||||
|
onClose?: () => void;
|
||||||
|
isRenderModal: boolean;
|
||||||
|
modalLabel: string;
|
||||||
|
buttonLabel: string;
|
||||||
|
}
|
||||||
|
export const ModalButton = ({
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
isRenderModal,
|
||||||
|
modalLabel,
|
||||||
|
buttonLabel,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<ModalButtonProps>) => {
|
||||||
|
const isOpen = useSignal<boolean>(false);
|
||||||
|
const closeModal = () => {
|
||||||
|
isOpen.value = false;
|
||||||
|
if (onClose) onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isRenderModal && (
|
||||||
|
<ComposeContainer onClick={e => (isOpen.value = true)}>
|
||||||
|
<ComposeIcon src={ComposeIconSVG} />
|
||||||
|
<ComposeP sx={{ fontSize: "70%" }}>{modalLabel}</ComposeP>
|
||||||
|
</ComposeContainer>
|
||||||
|
)}
|
||||||
|
<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>{modalLabel}</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,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</InstanceListContainer>
|
||||||
|
<InstanceFooter
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "rgba(217, 217, 217, 1)",
|
||||||
|
padding: "20px 42px",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "90px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewMessageSendButton
|
||||||
|
onClick={() => {
|
||||||
|
onSubmit().then(success => {
|
||||||
|
if (success) closeModal();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewMessageSendP>{buttonLabel}</NewMessageSendP>
|
||||||
|
|
||||||
|
<CreateThreadIcon
|
||||||
|
color="red"
|
||||||
|
opacity={1}
|
||||||
|
height="25px"
|
||||||
|
width="25px"
|
||||||
|
/>
|
||||||
|
</NewMessageSendButton>
|
||||||
|
</InstanceFooter>
|
||||||
|
</ReusableModal>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -1,27 +1,33 @@
|
|||||||
import { Input } from "@mui/material";
|
import { Input } from "@mui/material";
|
||||||
import { SxProps } from "@mui/system";
|
import { SxProps } from "@mui/system";
|
||||||
import { Signal, useSignalEffect } from "@preact/signals-react";
|
import { Signal, useSignal, useSignalEffect } from "@preact/signals-react";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
interface QmailTextFieldProps {
|
interface QmailTextFieldProps {
|
||||||
value: Signal<string>;
|
|
||||||
label: string;
|
label: string;
|
||||||
sx?: SxProps;
|
sx?: SxProps;
|
||||||
filter?: RegExp;
|
filter?: RegExp;
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
|
initialValue?: string;
|
||||||
|
afterChange?: (s: string) => void;
|
||||||
}
|
}
|
||||||
export const QmailTextField = ({
|
export const QmailTextField = ({
|
||||||
value,
|
|
||||||
label,
|
label,
|
||||||
sx = {},
|
sx = {},
|
||||||
filter,
|
filter,
|
||||||
maxLength = 60,
|
maxLength = 60,
|
||||||
|
initialValue = "",
|
||||||
|
afterChange,
|
||||||
}: QmailTextFieldProps) => {
|
}: QmailTextFieldProps) => {
|
||||||
useSignals();
|
useSignals();
|
||||||
|
const value = useSignal<string>(initialValue);
|
||||||
useSignalEffect(() => {
|
useSignalEffect(() => {
|
||||||
if (filter) value.value = value.value.replace(filter, "");
|
if (filter) value.value = value.value.replace(filter, "");
|
||||||
});
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
value.value = initialValue;
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -29,7 +35,9 @@ export const QmailTextField = ({
|
|||||||
id="standard-adornment-name"
|
id="standard-adornment-name"
|
||||||
value={value.value}
|
value={value.value}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
if (e.target.value.length <= maxLength) value.value = e.target.value;
|
const eventValue = e.target.value;
|
||||||
|
if (eventValue.length <= maxLength) value.value = eventValue;
|
||||||
|
if (afterChange) afterChange(value.value);
|
||||||
}}
|
}}
|
||||||
placeholder={label}
|
placeholder={label}
|
||||||
disableUnderline
|
disableUnderline
|
@ -21,13 +21,13 @@ export const Forum = ({
|
|||||||
}: ForumData) => {
|
}: ForumData) => {
|
||||||
const [currentThread, setCurrentThread] = useState<any>(null);
|
const [currentThread, setCurrentThread] = useState<any>(null);
|
||||||
|
|
||||||
const openNewThread = () => {
|
// const openNewThread = () => {
|
||||||
if (currentThread) {
|
// if (currentThread) {
|
||||||
executeEvent("openNewThreadMessageModal", {});
|
// executeEvent("openNewThreadMessageModal", {});
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
executeEvent("openNewThreadModal", {});
|
// executeEvent("openNewThreadModal", {});
|
||||||
};
|
// };
|
||||||
|
|
||||||
const forumWidth = 95;
|
const forumWidth = 95;
|
||||||
const forumMarginLeft = (100 - forumWidth) / 2;
|
const forumMarginLeft = (100 - forumWidth) / 2;
|
||||||
@ -51,8 +51,8 @@ export const Forum = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
<Box>{descriptionText.trim()}</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box>{descriptionText.trim()}</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
// <Box>
|
// <Box>
|
||||||
|
@ -1,30 +1,18 @@
|
|||||||
import { ReadonlySignal } from "@preact/signals-react";
|
import { FORUMS_ID } from "../../constants/Identifiers";
|
||||||
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 { setNotification } from "../../state/features/notificationsSlice";
|
||||||
import { store } from "../../state/store";
|
import { store } from "../../state/store";
|
||||||
import { getGroup } from "../../utils/QortalRequests";
|
import { getGroup } from "../../utils/QortalRequests";
|
||||||
import { objectToBase64, toBase64 } from "../../utils/toBase64";
|
import { objectToBase64 } from "../../utils/toBase64";
|
||||||
|
import { ForumData } from "./ForumModal";
|
||||||
import { Group } from "./GroupPermissionsForm";
|
import { Group } from "./GroupPermissionsForm";
|
||||||
import { descriptionMaxLength, ForumData } from "./ForumModal";
|
|
||||||
|
|
||||||
const getGroupNames = async (groups: Group[]) => {
|
const getGroupsData = async (groups: Group[]) => {
|
||||||
const groupPromises = groups.map(group => getGroup(group.id.value));
|
const groupPromises = groups.map(group => getGroup(group.id));
|
||||||
return await Promise.all(groupPromises);
|
return await Promise.all(groupPromises);
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyData = async (formData: ForumData) => {
|
const verifyData = async (formData: ForumData) => {
|
||||||
|
debugger;
|
||||||
const userName = store.getState()?.auth?.user?.name;
|
const userName = store.getState()?.auth?.user?.name;
|
||||||
const { title, encryption, groups, descriptionHTML, descriptionText } =
|
const { title, encryption, groups, descriptionHTML, descriptionText } =
|
||||||
formData;
|
formData;
|
||||||
@ -38,51 +26,33 @@ const verifyData = async (formData: ForumData) => {
|
|||||||
if (groups.filter(group => !!group.permissions).length < groups.length)
|
if (groups.filter(group => !!group.permissions).length < groups.length)
|
||||||
errorMsg = "A group has empty permissions";
|
errorMsg = "A group has empty permissions";
|
||||||
|
|
||||||
const groupsWithNames = await getGroupNames(groups);
|
const groupsWithNames = await getGroupsData(groups);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
groupsWithNames.filter(group => !!group?.groupName).length < groups.length
|
groupsWithNames.filter(group => !!group?.groupName).length < groups.length
|
||||||
)
|
)
|
||||||
errorMsg = "A group ID provided doesn't exist";
|
errorMsg = "A group ID provided doesn't exist";
|
||||||
|
|
||||||
if (errorMsg) {
|
|
||||||
store.dispatch(
|
|
||||||
setNotification({
|
|
||||||
msg: errorMsg,
|
|
||||||
alertType: "error",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return errorMsg;
|
return errorMsg;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addForum = async (formData: ForumData) => {
|
const verifyAllData = async (formData: ForumData[]) => {
|
||||||
const errorMsg = await verifyData(formData);
|
const errorListAll = await Promise.all(
|
||||||
if (errorMsg) return;
|
formData.map(data => verifyData(data))
|
||||||
forums.value = [...forums.value, formData];
|
);
|
||||||
|
// don't loop through all errors, take the first one and use that.
|
||||||
|
const firstErrorIndex = errorListAll.findIndex(data => !!data);
|
||||||
|
if (firstErrorIndex === -1) return "";
|
||||||
|
return `Forum ${firstErrorIndex + 1} has error: ${
|
||||||
|
errorListAll[firstErrorIndex]
|
||||||
|
}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const editForums = async (newForums: ForumData[]) => {
|
export const publishForum = async (formData: ForumData[]): Promise<boolean> => {
|
||||||
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 {
|
try {
|
||||||
await addForum(formData);
|
console.log("formData is: ", formData);
|
||||||
|
const errorMsg = await verifyAllData(formData);
|
||||||
const publishDescription =
|
if (errorMsg) throw new Error(errorMsg);
|
||||||
formData.title +
|
|
||||||
"_" +
|
|
||||||
formData.descriptionText.substring(0, descriptionMaxLength);
|
|
||||||
|
|
||||||
const userName = store.getState()?.auth?.user?.name;
|
const userName = store.getState()?.auth?.user?.name;
|
||||||
await qortalRequest({
|
await qortalRequest({
|
||||||
@ -90,40 +60,51 @@ export const publishForum = async (formData: ForumData) => {
|
|||||||
name: userName,
|
name: userName,
|
||||||
service: "METADATA",
|
service: "METADATA",
|
||||||
identifier: FORUMS_ID,
|
identifier: FORUMS_ID,
|
||||||
description: publishDescription,
|
data64: await objectToBase64(formData),
|
||||||
data64: await objectToBase64(forums),
|
|
||||||
});
|
});
|
||||||
success = true;
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
setNotification({
|
setNotification({
|
||||||
msg: "Forum published",
|
msg: "Forum published",
|
||||||
alertType: "success",
|
alertType: "success",
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
let notificationObj = null;
|
console.log("error is: ", error);
|
||||||
const defaultErrorMessage = "Failed to submit forum data";
|
formErrorHandler(error);
|
||||||
|
// throw new Error(defaultErrorMessage);
|
||||||
if (typeof error === "string") {
|
return false;
|
||||||
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;
|
};
|
||||||
|
|
||||||
|
const formErrorHandler = (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) store.dispatch(setNotification(notificationObj));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const forumToString = (forum: ForumData) =>
|
||||||
|
`${forum?.listIndex !== undefined ? forum.listIndex + 1 : "?"} - ${
|
||||||
|
forum?.title
|
||||||
|
}`;
|
||||||
|
|
||||||
|
export const deepCopyArray = (array: object[]) => {
|
||||||
|
return JSON.parse(JSON.stringify(array));
|
||||||
};
|
};
|
||||||
|
@ -1,45 +1,25 @@
|
|||||||
import MenuBookIcon from "@mui/icons-material/MenuBook";
|
import { Box, Button } from "@mui/material";
|
||||||
import { Box } from "@mui/material";
|
|
||||||
import {
|
import {
|
||||||
ReadonlySignal,
|
|
||||||
Signal,
|
Signal,
|
||||||
signal,
|
|
||||||
useComputed,
|
useComputed,
|
||||||
useSignal,
|
useSignal,
|
||||||
|
useSignalEffect,
|
||||||
} from "@preact/signals-react";
|
} from "@preact/signals-react";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useSelector } from "react-redux";
|
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 { SelectField } from "../../components/common/SelectField";
|
import { SelectField } from "../../components/common/SelectField";
|
||||||
import { Spacer } from "../../components/common/Spacer";
|
import { Spacer } from "../../components/common/Spacer";
|
||||||
import { TextEditor } from "../../components/common/TextEditor/TextEditor";
|
import { TextEditor } from "../../components/common/TextEditor/TextEditor";
|
||||||
import { ReusableModal } from "../../components/modals/ReusableModal";
|
|
||||||
import { useTestIdentifiers } from "../../constants/Identifiers";
|
import { useTestIdentifiers } from "../../constants/Identifiers";
|
||||||
import { appOwner } from "../../constants/Misc";
|
import { appOwner } from "../../constants/Misc";
|
||||||
import { RootState } from "../../state/store";
|
import { RootState } from "../../state/store";
|
||||||
import {
|
import { NewMessageInputRow } from "../Home/Home-styles";
|
||||||
CloseContainer,
|
import { resetForumIndexes } from "./Components/ActionBar";
|
||||||
ComposeContainer,
|
import { ModalButton } from "./Components/ModalButton";
|
||||||
ComposeIcon,
|
import { QmailTextField } from "./Components/QmailTextField";
|
||||||
ComposeP,
|
import { deepCopyArray, forumToString, publishForum } from "./ForumModal-Data";
|
||||||
InstanceContainer,
|
|
||||||
InstanceFooter,
|
|
||||||
InstanceListContainer,
|
|
||||||
InstanceListHeader,
|
|
||||||
NewMessageCloseImg,
|
|
||||||
NewMessageHeaderP,
|
|
||||||
NewMessageInputRow,
|
|
||||||
NewMessageSendButton,
|
|
||||||
NewMessageSendP,
|
|
||||||
} from "../Home/Home-styles";
|
|
||||||
import { Group, GroupPermissionsForm } from "./GroupPermissionsForm";
|
import { Group, GroupPermissionsForm } from "./GroupPermissionsForm";
|
||||||
import { publishForum } from "./ForumModal-Data";
|
|
||||||
import { QmailTextField } from "./QmailTextField";
|
|
||||||
|
|
||||||
export type EncryptionType = "None" | "Group" | "GroupAdmin" | "";
|
export type EncryptionType = "None" | "Group" | "GroupAdmin" | "";
|
||||||
export interface ForumData {
|
export interface ForumData {
|
||||||
@ -48,6 +28,7 @@ export interface ForumData {
|
|||||||
groups: Group[];
|
groups: Group[];
|
||||||
descriptionHTML: string;
|
descriptionHTML: string;
|
||||||
descriptionText: string;
|
descriptionText: string;
|
||||||
|
listIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupPermissionType = "Read" | "Write";
|
export type GroupPermissionType = "Read" | "Write";
|
||||||
@ -60,159 +41,181 @@ export const titleMaxLength = 60;
|
|||||||
export const descriptionMaxLength = 160;
|
export const descriptionMaxLength = 160;
|
||||||
|
|
||||||
interface NewForumModalProps {
|
interface NewForumModalProps {
|
||||||
forumData?: ForumData[];
|
forumData: Signal<ForumData[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ForumModal = ({ forumData }: NewForumModalProps) => {
|
export const ForumModal = ({ forumData }: NewForumModalProps) => {
|
||||||
useSignals();
|
useSignals();
|
||||||
const isOpen = useSignal<boolean>(false);
|
|
||||||
|
|
||||||
const forumTitle = useSignal<string>("");
|
const tempData = useSignal<ForumData[]>([]);
|
||||||
const descriptionHTML = useSignal<string>("");
|
|
||||||
const descriptionText = useSignal<string>("");
|
|
||||||
const groups = useSignal<Group[]>([
|
|
||||||
{
|
|
||||||
id: signal<string>(""),
|
|
||||||
permissions: signal<GroupPermissionType>("Read"),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const selectedEncryptionType = useSignal<EncryptionType | "">("None");
|
const emptyForum: ForumData = {
|
||||||
|
title: "",
|
||||||
const { user } = useSelector((state: RootState) => state.auth);
|
encryption: "None",
|
||||||
|
groups: [],
|
||||||
const formData: ReadonlySignal<ForumData> = useComputed(() => {
|
descriptionHTML: "",
|
||||||
return {
|
descriptionText: "",
|
||||||
title: forumTitle.value,
|
listIndex: 0,
|
||||||
encryption: selectedEncryptionType.value,
|
};
|
||||||
groups: groups.value,
|
|
||||||
descriptionHTML: descriptionHTML.value,
|
const { user } = useSelector((state: RootState) => state.auth);
|
||||||
descriptionText: descriptionText.value,
|
const isRenderModal = user?.name === appOwner || useTestIdentifiers;
|
||||||
};
|
|
||||||
});
|
const selectedForumIndex = useSignal<number>(0);
|
||||||
|
const selectedForum = useComputed<ForumData>(() => {
|
||||||
const closeModal = () => {
|
return tempData.value[selectedForumIndex.value];
|
||||||
isOpen.value = false;
|
});
|
||||||
|
|
||||||
|
const initializeTempData = () => {
|
||||||
|
tempData.value =
|
||||||
|
forumData.value.length === 0
|
||||||
|
? [emptyForum]
|
||||||
|
: deepCopyArray(forumData.value);
|
||||||
|
selectedForumIndex.value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
initializeTempData();
|
||||||
|
}, [forumData.value]);
|
||||||
|
|
||||||
|
const addForum = () => {
|
||||||
|
const newForum = { ...emptyForum };
|
||||||
|
newForum.listIndex = tempData.value.length;
|
||||||
|
tempData.value = [...tempData.value, newForum];
|
||||||
|
selectedForumIndex.value = newForum.listIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeForum = () => {
|
||||||
|
if (tempData.value.length <= 1) {
|
||||||
|
tempData.value = [{ ...emptyForum }];
|
||||||
|
selectedForumIndex.value = 0;
|
||||||
|
} else {
|
||||||
|
tempData.value = tempData.value.filter(
|
||||||
|
(group, index) => index !== selectedForumIndex.value
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectedForumIndex.value > 0) selectedForumIndex.value -= 1;
|
||||||
|
else selectedForumIndex.value = 0;
|
||||||
|
}
|
||||||
|
resetForumIndexes(tempData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateForums = () => {
|
||||||
|
tempData.value = [...tempData.value];
|
||||||
};
|
};
|
||||||
|
|
||||||
const forumText = !!forumData ? "Edit Forums" : "Add Forum";
|
|
||||||
const publishText = !!forumData ? "Edit Forums" : "Create Forum";
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<ModalButton
|
||||||
sx={{
|
isRenderModal={isRenderModal}
|
||||||
display: "flex",
|
onClose={() => initializeTempData()}
|
||||||
|
onSubmit={async () => {
|
||||||
|
updateForums();
|
||||||
|
const pub = await publishForum(tempData.value);
|
||||||
|
|
||||||
|
if (pub) forumData.value = tempData.value;
|
||||||
|
return pub;
|
||||||
}}
|
}}
|
||||||
|
modalLabel={"Manage Forums"}
|
||||||
|
buttonLabel={"Publish Forums"}
|
||||||
>
|
>
|
||||||
{(user?.name === appOwner || useTestIdentifiers) && (
|
<Box
|
||||||
<ComposeContainer onClick={e => (isOpen.value = true)}>
|
sx={{
|
||||||
<ComposeIcon src={ComposeIconSVG} />
|
display: "flex",
|
||||||
<ComposeP sx={{ fontSize: "70%" }}>{forumText}</ComposeP>
|
width: "100%",
|
||||||
</ComposeContainer>
|
alignItems: "center",
|
||||||
)}
|
gap: "10px",
|
||||||
<ReusableModal
|
marginBottom: "10px",
|
||||||
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
|
<SelectField
|
||||||
sx={{
|
options={tempData.value.map((forum, index) => forumToString(forum))}
|
||||||
backgroundColor: "unset",
|
label={"Forum #"}
|
||||||
height: "50px",
|
initialValue={forumToString(selectedForum.value)}
|
||||||
padding: "20px 42px",
|
afterChange={s => {
|
||||||
flexDirection: "row",
|
//tempData.value[selectedForumIndex.value] = selectedForum.value;
|
||||||
justifyContent: "space-between",
|
tempData.value = [...tempData.value];
|
||||||
alignItems: "center",
|
selectedForumIndex.value = +s.split(" ")[0] - 1;
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<NewMessageHeaderP>{forumText}</NewMessageHeaderP>
|
|
||||||
<CloseContainer onClick={closeModal}>
|
|
||||||
<NewMessageCloseImg src={ModalCloseSVG} />
|
|
||||||
</CloseContainer>
|
|
||||||
</InstanceListHeader>
|
|
||||||
<InstanceListContainer
|
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "rgba(217, 217, 217, 1)",
|
"& .MuiSvgIcon-root": {
|
||||||
padding: "20px 42px",
|
color: "black",
|
||||||
height: "calc(100% - 150px)",
|
},
|
||||||
flexShrink: 0,
|
|
||||||
}}
|
}}
|
||||||
|
width={"50%"}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant={"contained"}
|
||||||
|
sx={{ width: "25%", height: "35px", color: "white" }}
|
||||||
|
color={"success"}
|
||||||
|
onClick={addForum}
|
||||||
>
|
>
|
||||||
<NewMessageInputRow sx={{ height: "80px", alignItems: "end" }}>
|
Add Forum
|
||||||
<QmailTextField
|
</Button>
|
||||||
value={forumTitle}
|
<Button
|
||||||
label={"Forum Title"}
|
variant={"contained"}
|
||||||
sx={{
|
sx={{ width: "25%", height: "35px", color: "white" }}
|
||||||
height: "60px",
|
color={"error"}
|
||||||
borderBottom: "1px solid gray",
|
onClick={removeForum}
|
||||||
}}
|
>
|
||||||
maxLength={titleMaxLength}
|
Remove Selected Forum
|
||||||
/>
|
</Button>
|
||||||
<SelectField
|
</Box>
|
||||||
options={["None", "Group", "GroupAdmin"]}
|
<NewMessageInputRow sx={{ height: "80px", alignItems: "end" }}>
|
||||||
label={"Encryption Type"}
|
<QmailTextField
|
||||||
value={selectedEncryptionType}
|
initialValue={selectedForum.value?.title}
|
||||||
sx={{
|
afterChange={s => {
|
||||||
"& .MuiSvgIcon-root": {
|
tempData.value[selectedForumIndex.value].title = s;
|
||||||
color: "gray",
|
updateForums();
|
||||||
},
|
}}
|
||||||
}}
|
label={"Forum Title"}
|
||||||
/>
|
|
||||||
</NewMessageInputRow>
|
|
||||||
<GroupPermissionsForm groups={groups} />
|
|
||||||
<Spacer height="40px" />
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
maxHeight: "40vh",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p style={{ color: "black" }}> Description </p>
|
|
||||||
<TextEditor
|
|
||||||
inlineContent={descriptionHTML.value}
|
|
||||||
setInlineContent={(
|
|
||||||
value: any,
|
|
||||||
delta: any,
|
|
||||||
source: any,
|
|
||||||
editor: any
|
|
||||||
) => {
|
|
||||||
descriptionHTML.value = value;
|
|
||||||
descriptionText.value = editor.getText(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</InstanceListContainer>
|
|
||||||
<InstanceFooter
|
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "rgba(217, 217, 217, 1)",
|
height: "60px",
|
||||||
padding: "20px 42px",
|
borderBottom: "1px solid gray",
|
||||||
alignItems: "center",
|
|
||||||
height: "90px",
|
|
||||||
}}
|
}}
|
||||||
>
|
maxLength={titleMaxLength}
|
||||||
<NewMessageSendButton
|
/>
|
||||||
onClick={() => {
|
<SelectField
|
||||||
publishForum(formData.value).then(success => {
|
options={["None", "Group", "GroupAdmin"]}
|
||||||
if (success) closeModal();
|
label={"Encryption Type"}
|
||||||
});
|
initialValue={selectedForum.value?.encryption}
|
||||||
}}
|
afterChange={s =>
|
||||||
>
|
(selectedForum.value.encryption = s as EncryptionType)
|
||||||
<NewMessageSendP>{publishText}</NewMessageSendP>
|
}
|
||||||
|
sx={{
|
||||||
<CreateThreadIcon
|
"& .MuiSvgIcon-root": {
|
||||||
color="red"
|
color: "gray",
|
||||||
opacity={1}
|
height: "60px",
|
||||||
height="25px"
|
},
|
||||||
width="25px"
|
}}
|
||||||
/>
|
/>
|
||||||
</NewMessageSendButton>
|
</NewMessageInputRow>
|
||||||
</InstanceFooter>
|
<GroupPermissionsForm
|
||||||
</ReusableModal>
|
initialGroups={selectedForum.value?.groups}
|
||||||
</Box>
|
afterChange={g => {
|
||||||
|
tempData.value[selectedForumIndex.value].groups = g;
|
||||||
|
//updateForums();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Spacer height="40px" />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
maxHeight: "40vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p style={{ color: "black" }}> Description </p>
|
||||||
|
<TextEditor
|
||||||
|
inlineContent={selectedForum.value?.descriptionHTML}
|
||||||
|
setInlineContent={(
|
||||||
|
value: any,
|
||||||
|
delta: any,
|
||||||
|
source: any,
|
||||||
|
editor: any
|
||||||
|
) => {
|
||||||
|
selectedForum.value.descriptionHTML = value;
|
||||||
|
selectedForum.value.descriptionText = editor.getText(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</ModalButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
export const ForumThreads = () => {
|
import { useSignal } from "@preact/signals-react";
|
||||||
return <></>;
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { GroupData } from "../../utils/QortalRequests";
|
||||||
|
import { GroupMail } from "../Mail/GroupMail";
|
||||||
|
import { Thread } from "../Mail/Thread";
|
||||||
|
|
||||||
|
export interface ForumThreadsProps {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
export const ForumThreads = ({ id }: ForumThreadsProps) => {
|
||||||
|
useSignals();
|
||||||
|
const [currentThread, setCurrentThread] = useState<any>(null);
|
||||||
|
const [filterMode, setFilterMode] = useState<string>("Recently active");
|
||||||
|
|
||||||
|
const groupInfo = useSignal<GroupData | undefined>(undefined);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GroupMail
|
||||||
|
groupInfo={groupInfo.value}
|
||||||
|
currentThread={currentThread}
|
||||||
|
setCurrentThread={setCurrentThread}
|
||||||
|
setFilterMode={setFilterMode}
|
||||||
|
filterMode={filterMode}
|
||||||
|
/>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,41 +1,46 @@
|
|||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import RemoveIcon from "@mui/icons-material/Remove";
|
import RemoveIcon from "@mui/icons-material/Remove";
|
||||||
import {
|
import { Box, IconButton } from "@mui/material";
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
IconButton,
|
|
||||||
MenuItem,
|
|
||||||
OutlinedInput,
|
|
||||||
Select,
|
|
||||||
SelectChangeEvent,
|
|
||||||
} from "@mui/material";
|
|
||||||
import { styled } from "@mui/material/styles";
|
import { styled } from "@mui/material/styles";
|
||||||
import { signal, Signal } from "@preact/signals-react";
|
import { Signal, useSignal } from "@preact/signals-react";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { SelectField } from "../../components/common/SelectField";
|
import { SelectField } from "../../components/common/SelectField";
|
||||||
import { NewMessageInputRow } from "../Home/Home-styles";
|
import { NewMessageInputRow } from "../Home/Home-styles";
|
||||||
|
import { QmailTextField } from "./Components/QmailTextField";
|
||||||
import { GroupPermissionType } from "./ForumModal";
|
import { GroupPermissionType } from "./ForumModal";
|
||||||
import { QmailTextField } from "./QmailTextField";
|
|
||||||
|
|
||||||
export interface Group {
|
export interface Group {
|
||||||
id: Signal<string>;
|
id: string;
|
||||||
permissions: Signal<GroupPermissionType>;
|
permissions: GroupPermissionType;
|
||||||
}
|
}
|
||||||
export interface GroupPermissionsFormProps {
|
export interface GroupPermissionsFormProps {
|
||||||
groups: Signal<Group[]>;
|
initialGroups?: Group[];
|
||||||
|
afterChange?: (g: Group[]) => void;
|
||||||
}
|
}
|
||||||
export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => {
|
export const GroupPermissionsForm = ({
|
||||||
|
initialGroups,
|
||||||
|
afterChange,
|
||||||
|
}: GroupPermissionsFormProps) => {
|
||||||
useSignals();
|
useSignals();
|
||||||
|
|
||||||
const uid = new ShortUniqueId();
|
|
||||||
const newGroup = {
|
const newGroup = {
|
||||||
id: signal<string>(""),
|
id: "",
|
||||||
permissions: signal<GroupPermissionType>("Read"),
|
permissions: "Read",
|
||||||
} as Group;
|
} as Group;
|
||||||
|
const groups = useSignal<Group[]>(initialGroups || [newGroup]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialGroups)
|
||||||
|
groups.value = initialGroups?.length > 0 ? initialGroups : [newGroup];
|
||||||
|
}, [initialGroups]);
|
||||||
|
|
||||||
|
const uid = new ShortUniqueId();
|
||||||
|
|
||||||
const addGroup = () => {
|
const addGroup = () => {
|
||||||
groups.value = [...groups.value, newGroup];
|
groups.value = [...groups.value, newGroup];
|
||||||
|
if (afterChange) afterChange(groups.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeGroup = (groupIndex: number) => {
|
const removeGroup = (groupIndex: number) => {
|
||||||
@ -44,8 +49,12 @@ export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => {
|
|||||||
(group, index) => index !== groupIndex
|
(group, index) => index !== groupIndex
|
||||||
);
|
);
|
||||||
else groups.value = [newGroup];
|
else groups.value = [newGroup];
|
||||||
|
if (afterChange) afterChange(groups.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateGroups = () => {
|
||||||
|
if (afterChange) afterChange(groups.value);
|
||||||
|
};
|
||||||
const buttonStyle = {
|
const buttonStyle = {
|
||||||
width: "70px",
|
width: "70px",
|
||||||
height: "70px",
|
height: "70px",
|
||||||
@ -99,7 +108,11 @@ export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => {
|
|||||||
<RemoveIcon sx={{ ...iconStyle, fontSize: 50 }} />
|
<RemoveIcon sx={{ ...iconStyle, fontSize: 50 }} />
|
||||||
</StyledIconButton>
|
</StyledIconButton>
|
||||||
<QmailTextField
|
<QmailTextField
|
||||||
value={group.id}
|
afterChange={s => {
|
||||||
|
group.id = s;
|
||||||
|
updateGroups();
|
||||||
|
}}
|
||||||
|
initialValue={group.id}
|
||||||
label={"Group ID"}
|
label={"Group ID"}
|
||||||
filter={/[^0-9]/}
|
filter={/[^0-9]/}
|
||||||
sx={{
|
sx={{
|
||||||
@ -110,10 +123,13 @@ export const GroupPermissionsForm = ({ groups }: GroupPermissionsFormProps) => {
|
|||||||
maxLength={10}
|
maxLength={10}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<SelectField
|
<SelectField
|
||||||
options={options}
|
options={options}
|
||||||
value={group.permissions}
|
initialValue={group.permissions}
|
||||||
|
afterChange={s => {
|
||||||
|
group.permissions = s as GroupPermissionType;
|
||||||
|
updateGroups();
|
||||||
|
}}
|
||||||
label={"Group Permissions"}
|
label={"Group Permissions"}
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiSvgIcon-root": {
|
"& .MuiSvgIcon-root": {
|
||||||
|
@ -84,7 +84,7 @@ export const MailBodyInnerScroll = styled(Box)`
|
|||||||
|
|
||||||
export const ComposeContainer = styled(Box)(({ theme }) => ({
|
export const ComposeContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
width: "150px",
|
width: "100%",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "7px",
|
gap: "7px",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { Box } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { forums } from "../../App";
|
import { ActionBar, forums } from "../Forum/Components/ActionBar";
|
||||||
import { ActionBar } from "../Forum/ActionBar";
|
|
||||||
import { Forum } from "../Forum/Forum";
|
import { Forum } from "../Forum/Forum";
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { FORUMS_ID } from "../constants/Identifiers";
|
import { FORUMS_ID } from "../constants/Identifiers";
|
||||||
import { appOwner } from "../constants/Misc";
|
import { appOwner } from "../constants/Misc";
|
||||||
|
import { ForumData } from "../pages/Forum/ForumModal";
|
||||||
|
|
||||||
export interface GroupData {
|
export interface GroupData {
|
||||||
groupId: number;
|
groupId: number;
|
||||||
@ -19,6 +20,7 @@ export const listGroups = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getGroup = async (groupID: number | string) => {
|
export const getGroup = async (groupID: number | string) => {
|
||||||
|
if (!groupID) return undefined;
|
||||||
const url = `/groups/${groupID.toString()}`;
|
const url = `/groups/${groupID.toString()}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -36,12 +38,12 @@ export const getGroup = async (groupID: number | string) => {
|
|||||||
|
|
||||||
export const fetchForumData = async () => {
|
export const fetchForumData = async () => {
|
||||||
try {
|
try {
|
||||||
return await qortalRequest({
|
return (await qortalRequest({
|
||||||
action: "FETCH_QDN_RESOURCE",
|
action: "FETCH_QDN_RESOURCE",
|
||||||
name: appOwner,
|
name: appOwner,
|
||||||
service: "METADATA",
|
service: "METADATA",
|
||||||
identifier: FORUMS_ID,
|
identifier: FORUMS_ID,
|
||||||
});
|
})) as ForumData[];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user