refactor(dashboard): move all filesystem settings into one new page

This commit is contained in:
Aaron Liu 2025-07-12 11:41:32 +08:00
parent ada49fd21d
commit 772333248e
18 changed files with 394 additions and 325 deletions

View File

@ -1,5 +1,6 @@
import EntitySetting from "./Entity/EntitySetting";
import FileSetting from "./File/FileSetting";
import FileSystem from "./FileSystem/Filesystem";
import EditGroup from "./Group/EditGroup/EditGroup";
import GroupSetting from "./Group/GroupSetting";
import Home from "./Home/Home";
@ -12,15 +13,14 @@ import OauthCallback from "./StoragePolicy/OauthCallback";
import StoragePolicySetting from "./StoragePolicy/StoragePolicySetting";
import TaskList from "./Task/TaskList";
import UserSetting from "./User/UserSetting";
import FileSystem from "./FileSystem/FileSyste";
export {
EditGroup,
FileSystem,
EditNode,
EditStoragePolicy,
EntitySetting,
FileSetting,
FileSystem,
GroupSetting,
Home,
NodeSetting,

View File

@ -22,7 +22,6 @@ import Add from "../../../Icons/Add";
import { ProChip } from "../../../Pages/Setting/SettingForm";
import ProDialog from "../../Common/ProDialog";
import { SettingContext } from "../../Settings/SettingWrapper";
import { SettingSection } from "../../Settings/Settings";
import DraggableCustomPropsRow, { FieldTypes } from "./DraggableCustomPropsRow";
import EditPropsDialog from "./EditPropsDialog";
@ -117,94 +116,88 @@ const CustomPropsSetting = () => {
return (
<Box component={"form"} ref={formRef} onSubmit={(e) => e.preventDefault()}>
<Stack spacing={5}>
<Stack spacing={1}>
<ProDialog open={proOpen} onClose={() => setProOpen(false)} />
<SettingSection>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1 }}>
<SecondaryButton variant="contained" startIcon={<Add />} {...bindTrigger(newPropsPopupState)}>
{t("customProps.add")}
</SecondaryButton>
<Menu
onClose={onClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
{...menuProps}
>
{(Object.keys(FieldTypes) as CustomPropsType[]).map((type, index) => {
const fieldType = FieldTypes[type];
const Icon = fieldType.icon;
return (
<SquareMenuItem
dense
key={index}
onClick={() => (fieldType.pro ? setProOpen(true) : onNewProp(type))}
>
<ListItemIcon>
<Icon />
</ListItemIcon>
{t(fieldType.title)}
{fieldType.pro && <ProChip label="Pro" color="primary" size="small" />}
</SquareMenuItem>
);
})}
</Menu>
</Box>
<TableContainer component={StyledTableContainerPaper}>
<DndProvider backend={HTML5Backend}>
<Table sx={{ width: "100%" }} size="small">
<TableHead>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1 }}>
<SecondaryButton variant="contained" startIcon={<Add />} {...bindTrigger(newPropsPopupState)}>
{t("customProps.add")}
</SecondaryButton>
<Menu
onClose={onClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
{...menuProps}
>
{(Object.keys(FieldTypes) as CustomPropsType[]).map((type, index) => {
const fieldType = FieldTypes[type];
const Icon = fieldType.icon;
return (
<SquareMenuItem dense key={index} onClick={() => (fieldType.pro ? setProOpen(true) : onNewProp(type))}>
<ListItemIcon>
<Icon />
</ListItemIcon>
{t(fieldType.title)}
{fieldType.pro && <ProChip label="Pro" color="primary" size="small" />}
</SquareMenuItem>
);
})}
</Menu>
</Box>
<TableContainer component={StyledTableContainerPaper}>
<DndProvider backend={HTML5Backend}>
<Table sx={{ width: "100%" }} size="small">
<TableHead>
<TableRow>
<NoWrapCell>{t("settings.displayName")}</NoWrapCell>
<NoWrapCell>{t("customProps.type")}</NoWrapCell>
<NoWrapCell>{t("customProps.default")}</NoWrapCell>
<NoWrapCell>{t("settings.actions")}</NoWrapCell>
<NoWrapCell></NoWrapCell>
</TableRow>
</TableHead>
<TableBody>
{customProps.map((prop, idx) => {
const rowRef = createRef<HTMLTableRowElement>();
return (
<DraggableCustomPropsRow
key={prop.id}
ref={rowRef}
customProps={prop}
index={idx}
moveRow={moveRow}
onEdit={(props) => {
setEditProps(props);
setIsNew(false);
setOpen(true);
}}
onDelete={handleDeleteProduct}
onMoveUp={() => handleMoveUp(idx)}
onMoveDown={() => handleMoveDown(idx)}
isFirst={idx === 0}
isLast={idx === customProps.length - 1}
t={t}
/>
);
})}
{customProps.length === 0 && (
<TableRow>
<NoWrapCell>{t("settings.displayName")}</NoWrapCell>
<NoWrapCell>{t("customProps.type")}</NoWrapCell>
<NoWrapCell>{t("customProps.default")}</NoWrapCell>
<NoWrapCell>{t("settings.actions")}</NoWrapCell>
<NoWrapCell></NoWrapCell>
<NoWrapCell colSpan={6} align="center">
<Typography variant="caption" color="text.secondary">
{t("application:setting.listEmpty")}
</Typography>
</NoWrapCell>
</TableRow>
</TableHead>
<TableBody>
{customProps.map((prop, idx) => {
const rowRef = createRef<HTMLTableRowElement>();
return (
<DraggableCustomPropsRow
key={prop.id}
ref={rowRef}
customProps={prop}
index={idx}
moveRow={moveRow}
onEdit={(props) => {
setEditProps(props);
setIsNew(false);
setOpen(true);
}}
onDelete={handleDeleteProduct}
onMoveUp={() => handleMoveUp(idx)}
onMoveDown={() => handleMoveDown(idx)}
isFirst={idx === 0}
isLast={idx === customProps.length - 1}
t={t}
/>
);
})}
{customProps.length === 0 && (
<TableRow>
<NoWrapCell colSpan={6} align="center">
<Typography variant="caption" color="text.secondary">
{t("application:setting.listEmpty")}
</Typography>
</NoWrapCell>
</TableRow>
)}
</TableBody>
</Table>
</DndProvider>
</TableContainer>
</SettingSection>
)}
</TableBody>
</Table>
</DndProvider>
</TableContainer>
</Stack>
<EditPropsDialog open={open} onClose={() => setOpen(false)} onSave={handleSave} isNew={isNew} props={editProps} />
</Box>

View File

@ -1,68 +0,0 @@
import { Box, Container } from "@mui/material";
import { useQueryState } from "nuqs";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { CSSTransition, SwitchTransition } from "react-transition-group";
import ResponsiveTabs, { Tab } from "../../Common/ResponsiveTabs.tsx";
import TextBulletListSquareEdit from "../../Icons/TextBulletListSquareEdit.tsx";
import PageContainer from "../../Pages/PageContainer.tsx";
import PageHeader, { PageTabQuery } from "../../Pages/PageHeader.tsx";
import SettingsWrapper from "../Settings/SettingWrapper.tsx";
import CustomPropsSetting from "./CustomProps/CustomPropsSetting.tsx";
export enum SettingsPageTab {
Parameters = "parameters",
CustomProps = "customProps",
Icon = "icon",
FileApp = "fileApp",
}
const FileSystem = () => {
const { t } = useTranslation("dashboard");
const [tab, setTab] = useQueryState(PageTabQuery);
const tabs: Tab<SettingsPageTab>[] = useMemo(() => {
const res = [];
res.push(
...[
{
label: t("nav.customProps"),
value: SettingsPageTab.CustomProps,
icon: <TextBulletListSquareEdit />,
},
],
);
return res;
}, [t]);
return (
<PageContainer>
<Container maxWidth="xl">
<PageHeader title={t("dashboard:nav.fileSystem")} />
<ResponsiveTabs
value={tab ?? SettingsPageTab.Parameters}
onChange={(_e, newValue) => setTab(newValue)}
tabs={tabs}
/>
<SwitchTransition>
<CSSTransition
addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
classNames="fade"
key={`${tab}`}
>
<Box>
{(!tab || tab === SettingsPageTab.Parameters) && <div></div>}
{tab === SettingsPageTab.CustomProps && (
<SettingsWrapper settings={["custom_props"]}>
<CustomPropsSetting />
</SettingsWrapper>
)}
</Box>
</CSSTransition>
</SwitchTransition>
</Container>
</PageContainer>
);
};
export default FileSystem;

View File

@ -0,0 +1,132 @@
import { Box, Container } from "@mui/material";
import { useQueryState } from "nuqs";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { CSSTransition, SwitchTransition } from "react-transition-group";
import ResponsiveTabs, { Tab } from "../../Common/ResponsiveTabs.tsx";
import AppGeneric from "../../Icons/AppGeneric.tsx";
import Icons from "../../Icons/Icons.tsx";
import SettingsOutlined from "../../Icons/SettingsOutlined.tsx";
import TextBulletListSquareEdit from "../../Icons/TextBulletListSquareEdit.tsx";
import PageContainer from "../../Pages/PageContainer.tsx";
import PageHeader, { PageTabQuery } from "../../Pages/PageHeader.tsx";
import SettingsWrapper from "../Settings/SettingWrapper.tsx";
import CustomPropsSetting from "./CustomProps/CustomPropsSetting.tsx";
import FileIcons from "./Icons/FileIcons.tsx";
import Parameters from "./Parameters.tsx";
import ViewerSetting from "./ViewerSetting/ViewerSetting.tsx";
export enum SettingsPageTab {
Parameters = "parameters",
CustomProps = "customProps",
Icon = "icon",
FileApp = "fileApp",
}
const FileSystem = () => {
const { t } = useTranslation("dashboard");
const [tab, setTab] = useQueryState(PageTabQuery);
const tabs: Tab<SettingsPageTab>[] = useMemo(() => {
const res = [];
res.push(
...[
{
label: t("nav.settings"),
value: SettingsPageTab.Parameters,
icon: <SettingsOutlined />,
},
{
label: t("settings.fileIcons"),
value: SettingsPageTab.Icon,
icon: <Icons />,
},
{
label: t("settings.fileViewers"),
value: SettingsPageTab.FileApp,
icon: <AppGeneric />,
},
{
label: t("nav.customProps"),
value: SettingsPageTab.CustomProps,
icon: <TextBulletListSquareEdit />,
},
],
);
return res;
}, [t]);
return (
<PageContainer>
<Container maxWidth="xl">
<PageHeader title={t("dashboard:nav.fileSystem")} />
<ResponsiveTabs
value={tab ?? SettingsPageTab.Parameters}
onChange={(_e, newValue) => setTab(newValue)}
tabs={tabs}
/>
<SwitchTransition>
<CSSTransition
addEndListener={(node, done) => node.addEventListener("transitionend", done, false)}
classNames="fade"
key={`${tab}`}
>
<Box>
{(!tab || tab === SettingsPageTab.Parameters) && (
<SettingsWrapper
settings={[
"maxEditSize",
"cron_trash_bin_collect",
"cron_entity_collect",
"public_resource_maxage",
"use_cursor_pagination",
"max_page_size",
"max_recursive_searched_folder",
"max_batched_file",
"map_provider",
"map_google_tile_type",
"mime_mapping",
"explorer_category_image_query",
"explorer_category_video_query",
"explorer_category_audio_query",
"explorer_category_document_query",
"archive_timeout",
"upload_session_timeout",
"slave_api_timeout",
"folder_props_timeout",
"chunk_retries",
"use_temp_chunk_buffer",
"max_parallel_transfer",
"cron_oauth_cred_refresh",
"viewer_session_timeout",
"entity_url_default_ttl",
"entity_url_cache_margin",
]}
>
<Parameters />
</SettingsWrapper>
)}
{tab === SettingsPageTab.Icon && (
<SettingsWrapper settings={["explorer_icons", "emojis"]}>
<FileIcons />
</SettingsWrapper>
)}
{tab === SettingsPageTab.FileApp && (
<SettingsWrapper settings={["file_viewers"]}>
<ViewerSetting />
</SettingsWrapper>
)}
{tab === SettingsPageTab.CustomProps && (
<SettingsWrapper settings={["custom_props"]}>
<CustomPropsSetting />
</SettingsWrapper>
)}
</Box>
</CSSTransition>
</SwitchTransition>
</Container>
</PageContainer>
);
};
export default FileSystem;

View File

@ -1,8 +1,8 @@
import { DenseFilledTextField } from "../../../Common/StyledComponents.tsx";
import { DenseFilledTextField } from "../../Common/StyledComponents.tsx";
import { InputAdornment } from "@mui/material";
import CircleColorSelector, {
customizeMagicColor,
} from "../../../FileManager/FileInfo/ColorCircle/CircleColorSelector.tsx";
} from "../../FileManager/FileInfo/ColorCircle/CircleColorSelector.tsx";
import * as React from "react";
export interface HexColorInputProps {

View File

@ -1,6 +1,7 @@
import React from "react";
import { Box, IconButton, Stack, Table, TableBody, TableContainer, TableHead, TableRow } from "@mui/material";
import { memo, useMemo, useState } from "react";
import React, { memo, useMemo, useState } from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useTranslation } from "react-i18next";
import {
DenseFilledTextField,
@ -10,10 +11,8 @@ import {
StyledTableContainerPaper,
} from "../../../Common/StyledComponents.tsx";
import Add from "../../../Icons/Add.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import Dismiss from "../../../Icons/Dismiss.tsx";
export interface EmojiListProps {
config: string;

View File

@ -1,7 +1,7 @@
import { Box, IconButton, Table, TableBody, TableContainer, TableHead, TableRow } from "@mui/material";
import * as React from "react";
import { useTheme } from "@mui/material/styles";
import { memo, useMemo, useState } from "react";
import { builtInIcons, FileTypeIconSetting } from "../../../FileManager/Explorer/FileTypeIcon.tsx";
import { useTranslation } from "react-i18next";
import {
DenseFilledTextField,
NoWrapCell,
@ -9,11 +9,10 @@ import {
SecondaryButton,
StyledTableContainerPaper,
} from "../../../Common/StyledComponents.tsx";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material/styles";
import HexColorInput from "./HexColorInput.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import { builtInIcons, FileTypeIconSetting } from "../../../FileManager/Explorer/FileTypeIcon.tsx";
import Add from "../../../Icons/Add.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import HexColorInput from "../HexColorInput.tsx";
export interface FileIconListProps {
config: string;

View File

@ -0,0 +1,58 @@
import { Box, Stack, Typography } from "@mui/material";
import { useSnackbar } from "notistack";
import { useCallback, useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAppDispatch } from "../../../../redux/hooks";
import { SettingSection, SettingSectionContent } from "../../Settings/Settings";
import { SettingContext } from "../../Settings/SettingWrapper";
import EmojiList from "./EmojiList";
import FileIconList from "./FileIconList";
const FileIcons = () => {
const { t } = useTranslation("dashboard");
const { formRef, setSettings, values } = useContext(SettingContext);
const [loading, setLoading] = useState(false);
const dispatch = useAppDispatch();
const { enqueueSnackbar } = useSnackbar();
const iconOnChange = useCallback(
(s: string) =>
setSettings({
explorer_icons: s,
}),
[],
);
const onEmojiChange = useCallback(
(s: string) =>
setSettings({
emojis: s,
}),
[],
);
return (
<Box component={"form"} ref={formRef} onSubmit={(e) => e.preventDefault()}>
<Stack spacing={5}>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.fileIcons")}
</Typography>
<SettingSectionContent>
<FileIconList config={values.explorer_icons} onChange={iconOnChange} />
</SettingSectionContent>
</SettingSection>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.emojiOptions")}
</Typography>
<SettingSectionContent>
<EmojiList config={values.emojis} onChange={onEmojiChange} />
</SettingSectionContent>
</SettingSection>
</Stack>
</Box>
);
};
export default FileIcons;

View File

@ -14,21 +14,18 @@ import { useSnackbar } from "notistack";
import * as React from "react";
import { useCallback, useContext, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { sendClearBlobUrlCache } from "../../../../api/api.ts";
import { useAppDispatch } from "../../../../redux/hooks.ts";
import { isTrueVal } from "../../../../session/utils.ts";
import SizeInput from "../../../Common/SizeInput.tsx";
import { DefaultCloseAction } from "../../../Common/Snackbar/snackbar.tsx";
import { DenseFilledTextField, DenseSelect, SecondaryButton } from "../../../Common/StyledComponents.tsx";
import { SquareMenuItem } from "../../../FileManager/ContextMenu/ContextMenu.tsx";
import SettingForm from "../../../Pages/Setting/SettingForm.tsx";
import { NoMarginHelperText, SettingSection, SettingSectionContent } from "../Settings.tsx";
import { SettingContext } from "../SettingWrapper.tsx";
import EmojiList from "./EmojiList.tsx";
import FileIconList from "./FileIconList.tsx";
import FileViewerList from "./ViewerSetting/FileViewerList.tsx";
import { sendClearBlobUrlCache } from "../../../api/api.ts";
import { useAppDispatch } from "../../../redux/hooks.ts";
import { isTrueVal } from "../../../session/utils.ts";
import SizeInput from "../../Common/SizeInput.tsx";
import { DefaultCloseAction } from "../../Common/Snackbar/snackbar.tsx";
import { DenseFilledTextField, DenseSelect, SecondaryButton } from "../../Common/StyledComponents.tsx";
import { SquareMenuItem } from "../../FileManager/ContextMenu/ContextMenu.tsx";
import SettingForm from "../../Pages/Setting/SettingForm.tsx";
import { NoMarginHelperText, SettingSection, SettingSectionContent } from "../Settings/Settings.tsx";
import { SettingContext } from "../Settings/SettingWrapper.tsx";
const Filesystem = () => {
const Parameters = () => {
const { t } = useTranslation("dashboard");
const { formRef, setSettings, values } = useContext(SettingContext);
const [loading, setLoading] = useState(false);
@ -47,36 +44,12 @@ const Filesystem = () => {
});
};
const iconOnChange = useCallback(
(s: string) =>
setSettings({
explorer_icons: s,
}),
[],
);
const viewerOnChange = useCallback(
(s: string) =>
setSettings({
file_viewers: s,
}),
[],
);
const onMimeMappingChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setSettings({
mime_mapping: e.target.value,
});
}, []);
const onEmojiChange = useCallback(
(s: string) =>
setSettings({
emojis: s,
}),
[],
);
return (
<Box component={"form"} ref={formRef} onSubmit={(e) => e.preventDefault()}>
<Stack spacing={5}>
@ -358,22 +331,6 @@ const Filesystem = () => {
</SettingForm>
</SettingSectionContent>
</SettingSection>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.fileIcons")}
</Typography>
<SettingSectionContent>
<FileIconList config={values.explorer_icons} onChange={iconOnChange} />
</SettingSectionContent>
</SettingSection>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.fileViewers")}
</Typography>
<SettingSectionContent>
<FileViewerList config={values.file_viewers} onChange={viewerOnChange} />
</SettingSectionContent>
</SettingSection>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.searchQuery")}
@ -429,14 +386,6 @@ const Filesystem = () => {
</SettingForm>
</SettingSectionContent>
</SettingSection>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.emojiOptions")}
</Typography>
<SettingSectionContent>
<EmojiList config={values.emojis} onChange={onEmojiChange} />
</SettingSectionContent>
</SettingSection>
<SettingSection>
<Typography variant="h6" gutterBottom>
{t("settings.advanceOptions")}
@ -630,4 +579,4 @@ const Filesystem = () => {
);
};
export default Filesystem;
export default Parameters;

View File

@ -4,6 +4,7 @@ import {
IconButton,
Link,
ListItemText,
SelectChangeEvent,
Switch,
Table,
TableBody,
@ -17,33 +18,32 @@ import Grid from "@mui/material/Grid2";
import { useSnackbar } from "notistack";
import React, { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { Viewer, ViewerPlatform, ViewerType } from "../../../../../api/explorer.ts";
import { builtInViewers } from "../../../../../redux/thunks/viewer.ts";
import { isTrueVal } from "../../../../../session/utils.ts";
import CircularProgress from "../../../../Common/CircularProgress.tsx";
import SizeInput from "../../../../Common/SizeInput.tsx";
import { DefaultCloseAction } from "../../../../Common/Snackbar/snackbar.tsx";
import { Viewer, ViewerPlatform, ViewerType } from "../../../../api/explorer.ts";
import { builtInViewers } from "../../../../redux/thunks/viewer.ts";
import { isTrueVal } from "../../../../session/utils.ts";
import CircularProgress from "../../../Common/CircularProgress.tsx";
import SizeInput from "../../../Common/SizeInput.tsx";
import { DefaultCloseAction } from "../../../Common/Snackbar/snackbar.tsx";
import {
DenseFilledTextField,
DenseSelect,
NoWrapTableCell,
SecondaryButton,
StyledTableContainerPaper,
} from "../../../../Common/StyledComponents.tsx";
import DraggableDialog from "../../../../Dialogs/DraggableDialog.tsx";
import { SquareMenuItem } from "../../../../FileManager/ContextMenu/ContextMenu.tsx";
import { ViewerIDWithDefaultIcons } from "../../../../FileManager/Dialogs/OpenWith.tsx";
import Add from "../../../../Icons/Add.tsx";
import Dismiss from "../../../../Icons/Dismiss.tsx";
import SettingForm from "../../../../Pages/Setting/SettingForm.tsx";
import MagicVarDialog, { MagicVar } from "../../../Common/MagicVarDialog.tsx";
import { NoMarginHelperText } from "../../Settings.tsx";
import ArrowDown from "../../../../Icons/ArrowDown.tsx";
} from "../../../Common/StyledComponents.tsx";
import DraggableDialog from "../../../Dialogs/DraggableDialog.tsx";
import { SquareMenuItem } from "../../../FileManager/ContextMenu/ContextMenu.tsx";
import { ViewerIDWithDefaultIcons } from "../../../FileManager/Dialogs/OpenWith.tsx";
import Add from "../../../Icons/Add.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import SettingForm from "../../../Pages/Setting/SettingForm.tsx";
import MagicVarDialog, { MagicVar } from "../../Common/MagicVarDialog.tsx";
import { NoMarginHelperText } from "../../Settings/Settings.tsx";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { SelectChangeEvent } from "@mui/material";
const MonacoEditor = lazy(() => import("../../../../Viewers/CodeViewer/MonacoEditor.tsx"));
const MonacoEditor = lazy(() => import("../../../Viewers/CodeViewer/MonacoEditor.tsx"));
export interface FileViewerEditDialogProps {
viewer: Viewer;

View File

@ -15,20 +15,20 @@ import {
import { bindMenu, bindTrigger, usePopupState } from "material-ui-popup-state/hooks";
import * as React from "react";
import { memo, useCallback, useMemo, useState } from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useTranslation } from "react-i18next";
import { Viewer, ViewerGroup, ViewerType } from "../../../../../api/explorer.ts";
import { uuidv4 } from "../../../../../util";
import { NoWrapTableCell, SecondaryButton } from "../../../../Common/StyledComponents.tsx";
import { SquareMenuItem } from "../../../../FileManager/ContextMenu/ContextMenu.tsx";
import Add from "../../../../Icons/Add.tsx";
import DesktopFlow from "../../../../Icons/DesktopFlow.tsx";
import DocumentDataLink from "../../../../Icons/DocumentDataLink.tsx";
import { AccordionSummary, StyledAccordion } from "../../UserSession/SSOSettings.tsx";
import { Viewer, ViewerGroup, ViewerType } from "../../../../api/explorer.ts";
import { uuidv4 } from "../../../../util";
import { NoWrapTableCell, SecondaryButton } from "../../../Common/StyledComponents.tsx";
import { SquareMenuItem } from "../../../FileManager/ContextMenu/ContextMenu.tsx";
import Add from "../../../Icons/Add.tsx";
import DesktopFlow from "../../../Icons/DesktopFlow.tsx";
import DocumentDataLink from "../../../Icons/DocumentDataLink.tsx";
import { AccordionSummary, StyledAccordion } from "../../Settings/UserSession/SSOSettings.tsx";
import FileViewerEditDialog from "./FileViewerEditDialog.tsx";
import FileViewerRow from "./FileViewerRow.tsx";
import ImportWopiDialog from "./ImportWopiDialog.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
interface ViewerGroupProps {
group: ViewerGroup;
@ -288,6 +288,9 @@ const FileViewerList = memo(({ config, onChange }: FileViewerListProps) => {
return (
<Box>
<SecondaryButton variant={"contained"} {...bindTrigger(addNewPopupState)} startIcon={<Add />} sx={{ mb: 1 }}>
{t("settings.addViewer")}
</SecondaryButton>
{configParsed?.length > 0 &&
configParsed.map((item: ViewerGroup, index) => (
<ViewerGroupRow
@ -299,9 +302,6 @@ const FileViewerList = memo(({ config, onChange }: FileViewerListProps) => {
dndType={`viewer-row-${index}`}
/>
))}
<SecondaryButton variant={"contained"} {...bindTrigger(addNewPopupState)} startIcon={<Add />} sx={{ mt: 1 }}>
{t("settings.addViewer")}
</SecondaryButton>
<Menu
onClose={onClose}
anchorOrigin={{

View File

@ -1,15 +1,15 @@
import * as React from "react";
import { memo, useCallback, useState } from "react";
import { Viewer, ViewerPlatform, ViewerType } from "../../../../../api/explorer.ts";
import { useCallback, useState } from "react";
import { Viewer, ViewerPlatform, ViewerType } from "../../../../api/explorer.ts";
import { useTranslation } from "react-i18next";
import { IconButton, TableRow, ListItemText } from "@mui/material";
import { DenseFilledTextField, NoWrapCell, StyledCheckbox, DenseSelect } from "../../../../Common/StyledComponents.tsx";
import { SquareMenuItem } from "../../../../FileManager/ContextMenu/ContextMenu.tsx";
import { ViewerIcon } from "../../../../FileManager/Dialogs/OpenWith.tsx";
import Dismiss from "../../../../Icons/Dismiss.tsx";
import Edit from "../../../../Icons/Edit.tsx";
import { IconButton, ListItemText, TableRow } from "@mui/material";
import { DenseFilledTextField, DenseSelect, NoWrapCell, StyledCheckbox } from "../../../Common/StyledComponents.tsx";
import { SquareMenuItem } from "../../../FileManager/ContextMenu/ContextMenu.tsx";
import { ViewerIcon } from "../../../FileManager/Dialogs/OpenWith.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import Edit from "../../../Icons/Edit.tsx";
import FileViewerEditDialog from "./FileViewerEditDialog.tsx";
import ArrowDown from "../../../../Icons/ArrowDown.tsx";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
export interface FileViewerRowProps {
viewer: Viewer;

View File

@ -1,14 +1,14 @@
import { DialogContent, Link } from "@mui/material";
import { useCallback, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { getWopiDiscovery } from "../../../../../api/api.ts";
import { ViewerGroup } from "../../../../../api/explorer.ts";
import { useAppDispatch } from "../../../../../redux/hooks.ts";
import { DenseFilledTextField } from "../../../../Common/StyledComponents.tsx";
import DraggableDialog from "../../../../Dialogs/DraggableDialog.tsx";
import SettingForm from "../../../../Pages/Setting/SettingForm.tsx";
import { Code } from "../../../Common/Code.tsx";
import { NoMarginHelperText } from "../../Settings.tsx";
import { getWopiDiscovery } from "../../../../api/api.ts";
import { ViewerGroup } from "../../../../api/explorer.ts";
import { useAppDispatch } from "../../../../redux/hooks.ts";
import { DenseFilledTextField } from "../../../Common/StyledComponents.tsx";
import DraggableDialog from "../../../Dialogs/DraggableDialog.tsx";
import SettingForm from "../../../Pages/Setting/SettingForm.tsx";
import { Code } from "../../Common/Code.tsx";
import { NoMarginHelperText } from "../../Settings/Settings.tsx";
export interface ImportWopiDialogProps {
open: boolean;

View File

@ -0,0 +1,33 @@
import { Box, Stack } from "@mui/material";
import { useSnackbar } from "notistack";
import { useCallback, useContext, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAppDispatch } from "../../../../redux/hooks";
import { SettingContext } from "../../Settings/SettingWrapper";
import FileViewerList from "./FileViewerList";
const ViewerSetting = () => {
const { t } = useTranslation("dashboard");
const { formRef, setSettings, values } = useContext(SettingContext);
const [loading, setLoading] = useState(false);
const dispatch = useAppDispatch();
const { enqueueSnackbar } = useSnackbar();
const viewerOnChange = useCallback(
(s: string) =>
setSettings({
file_viewers: s,
}),
[],
);
return (
<Box component={"form"} ref={formRef} onSubmit={(e) => e.preventDefault()}>
<Stack spacing={5}>
<FileViewerList config={values.file_viewers} onChange={viewerOnChange} />
</Stack>
</Box>
);
};
export default ViewerSetting;

View File

@ -23,7 +23,7 @@ import {
import Add from "../../../Icons/Add";
import Delete from "../../../Icons/Delete";
import Edit from "../../../Icons/Edit";
import HexColorInput from "../../Settings/Filesystem/HexColorInput";
import HexColorInput from "../../FileSystem/HexColorInput.tsx";
import ThemeOptionEditDialog from "./ThemeOptionEditDialog";
export interface ThemeOptionsProps {

View File

@ -8,7 +8,6 @@ import ResponsiveTabs, { Tab } from "../../Common/ResponsiveTabs.tsx";
import Bot from "../../Icons/Bot.tsx";
import Color from "../../Icons/Color.tsx";
import CubeSync from "../../Icons/CubeSync.tsx";
import CubeTree from "../../Icons/CubeTree.tsx";
import Currency from "../../Icons/Currency.tsx";
import FilmstripImage from "../../Icons/FilmstripImage.tsx";
import Globe from "../../Icons/Globe.tsx";
@ -22,7 +21,6 @@ import Appearance from "./Appearance/Appearance.tsx";
import Captcha from "./Captcha/Captcha.tsx";
import Email from "./Email/Email.tsx";
import Events from "./Event/Events.tsx";
import Filesystem from "./Filesystem/Filesystem.tsx";
import Media from "./Media/Media.tsx";
import Queue from "./Queue/Queue.tsx";
import ServerSetting from "./Server/ServerSetting.tsx";
@ -104,11 +102,6 @@ const Settings = () => {
value: SettingsPageTab.Captcha,
icon: <Bot />,
},
{
label: t("nav.fileSystem"),
value: SettingsPageTab.FileSystem,
icon: <CubeTree />,
},
{
label: t("nav.mediaProcessing"),
value: SettingsPageTab.MediaProcessing,
@ -230,43 +223,6 @@ const Settings = () => {
<Captcha />
</SettingsWrapper>
)}
{tab === SettingsPageTab.FileSystem && (
<SettingsWrapper
settings={[
"maxEditSize",
"cron_trash_bin_collect",
"cron_entity_collect",
"public_resource_maxage",
"use_cursor_pagination",
"max_page_size",
"max_recursive_searched_folder",
"max_batched_file",
"map_provider",
"map_google_tile_type",
"mime_mapping",
"explorer_icons",
"file_viewers",
"explorer_category_image_query",
"explorer_category_video_query",
"explorer_category_audio_query",
"explorer_category_document_query",
"emojis",
"archive_timeout",
"upload_session_timeout",
"slave_api_timeout",
"folder_props_timeout",
"chunk_retries",
"use_temp_chunk_buffer",
"max_parallel_transfer",
"cron_oauth_cred_refresh",
"viewer_session_timeout",
"entity_url_default_ttl",
"entity_url_cache_margin",
]}
>
<Filesystem />
</SettingsWrapper>
)}
{tab === SettingsPageTab.MediaProcessing && (
<SettingsWrapper
settings={[

View File

@ -0,0 +1,9 @@
import { SvgIcon, SvgIconProps } from "@mui/material";
export default function AppGeneric(props: SvgIconProps) {
return (
<SvgIcon {...props}>
<path d="M3 6.25A3.25 3.25 0 0 1 6.25 3h11.5A3.25 3.25 0 0 1 21 6.25v11.5A3.25 3.25 0 0 1 17.75 21H6.25A3.25 3.25 0 0 1 3 17.75zM6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v.25h15v-.25a1.75 1.75 0 0 0-1.75-1.75zM4.5 17.75c0 .966.784 1.75 1.75 1.75h11.5a1.75 1.75 0 0 0 1.75-1.75V8h-15zM6.85 9.5h3.3c.47 0 .85.38.85.85v6.8c0 .47-.38.85-.85.85h-3.3a.85.85 0 0 1-.85-.85v-6.8c0-.47.38-.85.85-.85m.65 7h2V11h-2zm4.5-6.25a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75m.75 2.25a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5z" />
</SvgIcon>
);
}

View File

@ -0,0 +1,9 @@
import { SvgIcon, SvgIconProps } from "@mui/material";
export default function Icons(props: SvgIconProps) {
return (
<SvgIcon {...props}>
<path d="M13 3.5a3.5 3.5 0 0 0-2.669 5.764a.75.75 0 0 1-.571 1.236H3.75a.25.25 0 0 0-.25.25v1c0 2.9 2.35 5.25 5.25 5.25h3.218c.15.11.319.196.504.255a9.129 9.129 0 0 0-.727 1.245H8.75A6.75 6.75 0 0 1 2 11.75v-1C2 9.784 2.784 9 3.75 9h4.666a5 5 0 1 1 9.168-4h1.666c.966 0 1.75.784 1.75 1.75a2.75 2.75 0 0 1-2.75 2.75h-.92c-.135.235-.29.458-.461.667c.157.137.305.284.442.44c-.176.019-.356.04-.54.065a7.01 7.01 0 0 0-1.323.306a3.047 3.047 0 0 0-.1-.058a.75.75 0 0 1-.11-1.23A3.502 3.502 0 0 0 16.355 8h1.895c.69 0 1.25-.56 1.25-1.25a.25.25 0 0 0-.25-.25h-2.785A3.5 3.5 0 0 0 13 3.5m3.901 8.163c2.801-.37 4.539-.06 5.563.227a.75.75 0 0 1 .212 1.348c-.057.038-.15.13-.26.34a4.94 4.94 0 0 0-.32.825c-.088.284-.17.59-.261.925l-.057.212c-.112.41-.234.85-.382 1.286c-.292.862-.704 1.782-1.397 2.488c-.72.733-1.695 1.19-3 1.19c-1.328 0-2.256-.539-2.85-1.159c-.403.74-.613 1.437-.645 1.951a.75.75 0 0 1-1.497-.091c.07-1.14.665-2.574 1.662-3.844c1.009-1.284 2.48-2.467 4.381-3.04a.75.75 0 0 1 .433 1.436c-1.448.437-2.61 1.312-3.452 2.308c.31.442.922.939 1.969.939c.911 0 1.498-.302 1.928-.74c.457-.466.78-1.13 1.047-1.92c.132-.389.245-.791.355-1.197l.055-.201c.09-.335.181-.674.278-.987c.09-.291.192-.583.314-.853c-.893-.13-2.15-.184-3.879.044c-1.998.265-3.124 1.723-3.375 2.635a.75.75 0 0 1-1.446-.398c.396-1.442 1.978-3.373 4.624-3.724" />
</SvgIcon>
);
}