mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-25 19:52:48 +00:00
refactor(dashboard): move all filesystem settings into one new page
This commit is contained in:
parent
ada49fd21d
commit
772333248e
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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={{
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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={[
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue