|
|
|
@ -1,7 +1,10 @@
|
|
|
|
|
import useLocalStorageState from "use-local-storage-state";
|
|
|
|
|
|
|
|
|
|
import { FC, useMemo, useState } from "react";
|
|
|
|
|
import { FC, useEffect, useMemo, useState } from "react";
|
|
|
|
|
|
|
|
|
|
import { AxiosError } from "axios";
|
|
|
|
|
|
|
|
|
|
import Alert from "@mui/material/Alert";
|
|
|
|
|
import Box from "@mui/material/Box";
|
|
|
|
|
import Button from "@mui/material/Button";
|
|
|
|
|
import FormControl from "@mui/material/FormControl";
|
|
|
|
@ -21,9 +24,12 @@ import MailBox from "@interfaces/box";
|
|
|
|
|
import modalStyles from "@styles/modal";
|
|
|
|
|
import scrollbarStyles from "@styles/scrollbar";
|
|
|
|
|
|
|
|
|
|
import flattenBoxesArray from "@utils/flattenBoxesArray";
|
|
|
|
|
import flattenBoxes from "@utils/flattenBoxes";
|
|
|
|
|
import useHttpClient from "@utils/hooks/useFetch";
|
|
|
|
|
import useSnackbar from "@utils/hooks/useSnackbar";
|
|
|
|
|
import useStore from "@utils/hooks/useStore";
|
|
|
|
|
import useTheme from "@utils/hooks/useTheme";
|
|
|
|
|
import nestBoxes from "@utils/nestBoxes";
|
|
|
|
|
|
|
|
|
|
import FolderTree, {
|
|
|
|
|
checkedBoxesStore,
|
|
|
|
@ -37,12 +43,18 @@ const AddBox: FC = () => {
|
|
|
|
|
|
|
|
|
|
const [boxes, setBoxes] = useLocalStorageState<MailBox[]>("boxes");
|
|
|
|
|
|
|
|
|
|
const showSnackbar = useSnackbar();
|
|
|
|
|
|
|
|
|
|
const setShowAddBox = useStore((state) => state.setShowAddBox);
|
|
|
|
|
const showAddBox = useStore((state) => state.showAddBox);
|
|
|
|
|
|
|
|
|
|
const unifiedBoxes = checkedBoxesStore((state) => state.checkedBoxes);
|
|
|
|
|
const addUnifiedBox = checkedBoxesStore((state) => state.setChecked);
|
|
|
|
|
|
|
|
|
|
const fetcher = useHttpClient();
|
|
|
|
|
|
|
|
|
|
const [error, setError] = useState<string>();
|
|
|
|
|
|
|
|
|
|
const checkedBoxes = useMemo(
|
|
|
|
|
() => Object.entries(unifiedBoxes).filter(([, checked]) => checked),
|
|
|
|
|
[unifiedBoxes]
|
|
|
|
@ -50,14 +62,16 @@ const AddBox: FC = () => {
|
|
|
|
|
|
|
|
|
|
const [folderType, setFolderType] = useState<FolderType>("none");
|
|
|
|
|
|
|
|
|
|
const [parentFolder, setParentFolder] = useState<string>("none");
|
|
|
|
|
const [parentFolder, setParentFolder] = useState<MailBox>();
|
|
|
|
|
|
|
|
|
|
const [folderName, setFolderName] = useState<string>("");
|
|
|
|
|
|
|
|
|
|
const handleClose = (): void => setShowAddBox(false);
|
|
|
|
|
|
|
|
|
|
const boxIDs = useMemo(() => {
|
|
|
|
|
if (boxes) return flattenBoxesArray(boxes);
|
|
|
|
|
useEffect(() => setError(undefined), [folderType, parentFolder, folderName]);
|
|
|
|
|
|
|
|
|
|
const flattenedBoxes = useMemo(() => {
|
|
|
|
|
if (boxes) return flattenBoxes(boxes);
|
|
|
|
|
else return [];
|
|
|
|
|
}, boxes);
|
|
|
|
|
|
|
|
|
@ -67,166 +81,194 @@ const AddBox: FC = () => {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const checkAllBoxes = (checked: boolean): void => {
|
|
|
|
|
const ids = boxIDs.map((box) => box.id);
|
|
|
|
|
const ids = flattenedBoxes.map((box) => box.id);
|
|
|
|
|
|
|
|
|
|
ids.forEach((id) => addUnifiedBox(id, checked));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const createBox = (box: MailBox): void => {
|
|
|
|
|
if (boxes) setBoxes([...boxes, box]);
|
|
|
|
|
const createBox = async (box: MailBox): Promise<void> => {
|
|
|
|
|
if (flattenedBoxes) {
|
|
|
|
|
flattenedBoxes.push(box);
|
|
|
|
|
|
|
|
|
|
setBoxes(nestBoxes(flattenedBoxes));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!box.unifies)
|
|
|
|
|
await fetcher
|
|
|
|
|
.createBox(box.id)
|
|
|
|
|
.then(() => {
|
|
|
|
|
showSnackbar(`Folder '${box.name}' created`);
|
|
|
|
|
setShowAddBox(false);
|
|
|
|
|
})
|
|
|
|
|
.catch((error: AxiosError<{ message: string }>) => {
|
|
|
|
|
const message = error.response?.data.message;
|
|
|
|
|
|
|
|
|
|
if (message) setError(message);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Modal open={showAddBox} onClose={handleClose}>
|
|
|
|
|
<Stack
|
|
|
|
|
spacing={2}
|
|
|
|
|
direction="column"
|
|
|
|
|
sx={{
|
|
|
|
|
...modalSx,
|
|
|
|
|
...scrollbarSx,
|
|
|
|
|
maxHeight: "90%",
|
|
|
|
|
overflowY: "scroll"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Typography gutterBottom variant="h3">
|
|
|
|
|
Create a new folder
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
<FormControl fullWidth>
|
|
|
|
|
<InputLabel id="folder-type-label">Folder type</InputLabel>
|
|
|
|
|
<Select
|
|
|
|
|
labelId="folder-type-label"
|
|
|
|
|
id="folder-type"
|
|
|
|
|
value={folderType}
|
|
|
|
|
label="Folder type"
|
|
|
|
|
onChange={(e) => setFolderType(e.target.value as FolderType)}
|
|
|
|
|
>
|
|
|
|
|
<MenuItem value="none">None</MenuItem>
|
|
|
|
|
<MenuItem value="unified">
|
|
|
|
|
Unified
|
|
|
|
|
<Typography
|
|
|
|
|
sx={{
|
|
|
|
|
ml: 2,
|
|
|
|
|
display: "inline",
|
|
|
|
|
color: theme.palette.text.secondary
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Create a (local) inbox that unifies together multiple inboxes
|
|
|
|
|
</Typography>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
<MenuItem value="normal">
|
|
|
|
|
Normal
|
|
|
|
|
<Typography
|
|
|
|
|
sx={{
|
|
|
|
|
ml: 2,
|
|
|
|
|
display: "inline",
|
|
|
|
|
color: theme.palette.text.secondary
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Create a new inbox on the server
|
|
|
|
|
</Typography>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
</Select>
|
|
|
|
|
</FormControl>
|
|
|
|
|
|
|
|
|
|
{folderType != "none" && (
|
|
|
|
|
<TextField
|
|
|
|
|
fullWidth
|
|
|
|
|
value={folderName}
|
|
|
|
|
onChange={(e) => setFolderName(e.target.value)}
|
|
|
|
|
label="Folder name"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{folderType != "none" && (
|
|
|
|
|
<>
|
|
|
|
|
<Modal open={showAddBox} onClose={handleClose}>
|
|
|
|
|
<Stack
|
|
|
|
|
spacing={2}
|
|
|
|
|
direction="column"
|
|
|
|
|
sx={{
|
|
|
|
|
...modalSx,
|
|
|
|
|
...scrollbarSx,
|
|
|
|
|
maxHeight: "90%",
|
|
|
|
|
overflowY: "scroll"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Typography gutterBottom variant="h3">
|
|
|
|
|
Create a new folder
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
<FormControl fullWidth>
|
|
|
|
|
<InputLabel id="parent-folder-label">Parent folder</InputLabel>
|
|
|
|
|
<InputLabel id="folder-type-label">Folder type</InputLabel>
|
|
|
|
|
<Select
|
|
|
|
|
labelId="parent-folder-label"
|
|
|
|
|
id="parent-folder"
|
|
|
|
|
value={parentFolder}
|
|
|
|
|
label="Parent folder"
|
|
|
|
|
onChange={(e) => setParentFolder(e.target.value)}
|
|
|
|
|
MenuProps={{
|
|
|
|
|
sx: {
|
|
|
|
|
maxHeight: 300
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
labelId="folder-type-label"
|
|
|
|
|
id="folder-type"
|
|
|
|
|
value={folderType}
|
|
|
|
|
label="Folder type"
|
|
|
|
|
onChange={(e) => setFolderType(e.target.value as FolderType)}
|
|
|
|
|
>
|
|
|
|
|
<MenuItem value="none">None</MenuItem>
|
|
|
|
|
{boxIDs.map((box, i) => (
|
|
|
|
|
<MenuItem key={box.id + i} value={box.id}>
|
|
|
|
|
{box.name}
|
|
|
|
|
</MenuItem>
|
|
|
|
|
))}
|
|
|
|
|
<MenuItem value="unified">
|
|
|
|
|
Unified
|
|
|
|
|
<Typography
|
|
|
|
|
sx={{
|
|
|
|
|
ml: 2,
|
|
|
|
|
display: "inline",
|
|
|
|
|
color: theme.palette.text.secondary
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Create a (local) inbox that unifies together multiple inboxes
|
|
|
|
|
</Typography>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
<MenuItem value="normal">
|
|
|
|
|
Normal
|
|
|
|
|
<Typography
|
|
|
|
|
sx={{
|
|
|
|
|
ml: 2,
|
|
|
|
|
display: "inline",
|
|
|
|
|
color: theme.palette.text.secondary
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Create a new inbox on the server
|
|
|
|
|
</Typography>
|
|
|
|
|
</MenuItem>
|
|
|
|
|
</Select>
|
|
|
|
|
</FormControl>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{folderType == "unified" && boxes && (
|
|
|
|
|
<>
|
|
|
|
|
<Stack direction="column" justifyContent="left" spacing={2}>
|
|
|
|
|
<Typography variant="h5">
|
|
|
|
|
Select the folders you want to be unified
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
<Stack direction="row" alignItems="center" spacing={2}>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => checkAllBoxes(true)}
|
|
|
|
|
variant="outlined"
|
|
|
|
|
startIcon={<SelectAllIcon />}
|
|
|
|
|
>
|
|
|
|
|
Select all folders
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => checkAllBoxes(false)}
|
|
|
|
|
variant="outlined"
|
|
|
|
|
startIcon={<DeselectAllIcon />}
|
|
|
|
|
>
|
|
|
|
|
Deselect all folders
|
|
|
|
|
</Button>
|
|
|
|
|
{folderType != "none" && (
|
|
|
|
|
<TextField
|
|
|
|
|
fullWidth
|
|
|
|
|
value={folderName}
|
|
|
|
|
onChange={(e) => setFolderName(e.target.value)}
|
|
|
|
|
label="Folder name"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{folderType != "none" && (
|
|
|
|
|
<FormControl fullWidth>
|
|
|
|
|
<InputLabel id="parent-folder-label">Parent folder</InputLabel>
|
|
|
|
|
<Select
|
|
|
|
|
labelId="parent-folder-label"
|
|
|
|
|
id="parent-folder"
|
|
|
|
|
value={parentFolder?.id ?? "none"}
|
|
|
|
|
label="Parent folder"
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const parentFolder = flattenedBoxes.find(
|
|
|
|
|
(box) => box.id == e.target.value
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
setParentFolder(parentFolder);
|
|
|
|
|
}}
|
|
|
|
|
MenuProps={{
|
|
|
|
|
sx: {
|
|
|
|
|
maxHeight: 300
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<MenuItem value="none">None</MenuItem>
|
|
|
|
|
{flattenedBoxes.map((box, i) => (
|
|
|
|
|
<MenuItem key={box.id + i} value={box.id}>
|
|
|
|
|
{box.id.split(box.delimiter).join(" / ")}
|
|
|
|
|
</MenuItem>
|
|
|
|
|
))}
|
|
|
|
|
</Select>
|
|
|
|
|
</FormControl>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{folderType == "unified" && boxes && (
|
|
|
|
|
<>
|
|
|
|
|
<Stack direction="column" justifyContent="left" spacing={2}>
|
|
|
|
|
<Typography variant="h5">
|
|
|
|
|
Select the folders you want to be unified
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
<Stack direction="row" alignItems="center" spacing={2}>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => checkAllBoxes(true)}
|
|
|
|
|
variant="outlined"
|
|
|
|
|
startIcon={<SelectAllIcon />}
|
|
|
|
|
>
|
|
|
|
|
Select all folders
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => checkAllBoxes(false)}
|
|
|
|
|
variant="outlined"
|
|
|
|
|
startIcon={<DeselectAllIcon />}
|
|
|
|
|
>
|
|
|
|
|
Deselect all folders
|
|
|
|
|
</Button>
|
|
|
|
|
</Stack>
|
|
|
|
|
</Stack>
|
|
|
|
|
</Stack>
|
|
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
...scrollbarSx,
|
|
|
|
|
maxHeight: "15rem",
|
|
|
|
|
overflowY: "scroll"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<CheckedBoxesContext.Provider value={checkedBoxesStore}>
|
|
|
|
|
<FolderTree showCheckBox boxes={boxes} />
|
|
|
|
|
</CheckedBoxesContext.Provider>
|
|
|
|
|
</Box>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
disabled={
|
|
|
|
|
folderType == "none" ||
|
|
|
|
|
folderName == "" ||
|
|
|
|
|
parentFolder == "none" ||
|
|
|
|
|
(folderType == "unified" && checkedBoxes.length == 0)
|
|
|
|
|
}
|
|
|
|
|
onClick={() =>
|
|
|
|
|
createBox({
|
|
|
|
|
name: folderName,
|
|
|
|
|
id: folderName,
|
|
|
|
|
unifies:
|
|
|
|
|
folderType == "unified"
|
|
|
|
|
? boxIDs.length == checkedBoxes.length
|
|
|
|
|
? "all"
|
|
|
|
|
: checkedBoxes.map((box) => box[0])
|
|
|
|
|
: undefined
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
variant="contained"
|
|
|
|
|
>
|
|
|
|
|
Create
|
|
|
|
|
</Button>
|
|
|
|
|
</Stack>
|
|
|
|
|
</Modal>
|
|
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
...scrollbarSx,
|
|
|
|
|
maxHeight: "15rem",
|
|
|
|
|
overflowY: "scroll"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<CheckedBoxesContext.Provider value={checkedBoxesStore}>
|
|
|
|
|
<FolderTree showCheckBox boxes={boxes} />
|
|
|
|
|
</CheckedBoxesContext.Provider>
|
|
|
|
|
</Box>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
disabled={
|
|
|
|
|
folderType == "none" ||
|
|
|
|
|
folderName == "" ||
|
|
|
|
|
parentFolder == undefined ||
|
|
|
|
|
(folderType == "unified" && checkedBoxes.length == 0)
|
|
|
|
|
}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (!parentFolder) return;
|
|
|
|
|
|
|
|
|
|
createBox({
|
|
|
|
|
name: folderName,
|
|
|
|
|
id: parentFolder.id + parentFolder.delimiter + folderName,
|
|
|
|
|
delimiter: parentFolder.delimiter,
|
|
|
|
|
unifies:
|
|
|
|
|
folderType == "unified"
|
|
|
|
|
? checkedBoxes.map((box) => box[0])
|
|
|
|
|
: undefined
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
variant="contained"
|
|
|
|
|
>
|
|
|
|
|
Create
|
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
{error && <Alert severity="error">{error}</Alert>}
|
|
|
|
|
</Stack>
|
|
|
|
|
</Modal>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|