feat: adjust profiles page ui

This commit is contained in:
GyDi 2022-11-20 22:37:34 +08:00
parent 8bb4803ff9
commit 7338838b0e
No known key found for this signature in database
GPG Key ID: 9C3AD40F1F99880A
10 changed files with 120 additions and 140 deletions

View File

@ -26,7 +26,7 @@ interface Props {
onChange?: () => void; onChange?: () => void;
} }
export const FileEditor = (props: Props) => { export const EditorViewer = (props: Props) => {
const { uid, open, mode, onClose, onChange } = props; const { uid, open, mode, onClose, onChange } = props;
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -1,44 +1,29 @@
import useSWR from "swr"; import useSWR from "swr";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useTranslation } from "react-i18next"; import { Grid } from "@mui/material";
import { Box, Grid, IconButton, Stack } from "@mui/material";
import { RestartAltRounded } from "@mui/icons-material";
import { import {
getProfiles, getProfiles,
deleteProfile, deleteProfile,
enhanceProfiles,
patchProfilesConfig, patchProfilesConfig,
getRuntimeLogs, getRuntimeLogs,
} from "@/services/cmds"; } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import ProfileMore from "./profile-more"; import { ProfileMore } from "./profile-more";
interface Props { interface Props {
items: IProfileItem[]; items: IProfileItem[];
chain: string[]; chain: string[];
} }
const EnhancedMode = (props: Props) => { export const EnhancedMode = (props: Props) => {
const { items, chain } = props; const { items, chain } = props;
const { t } = useTranslation();
const { mutate: mutateProfiles } = useSWR("getProfiles", getProfiles); const { mutate: mutateProfiles } = useSWR("getProfiles", getProfiles);
const { data: chainLogs = {}, mutate: mutateLogs } = useSWR( const { data: chainLogs = {}, mutate: mutateLogs } = useSWR(
"getRuntimeLogs", "getRuntimeLogs",
getRuntimeLogs getRuntimeLogs
); );
// handler
const onEnhance = useLockFn(async () => {
try {
await enhanceProfiles();
mutateLogs();
// Notice.success("Refresh clash config", 1000);
} catch (err: any) {
Notice.error(err.message || err.toString());
}
});
const onEnhanceEnable = useLockFn(async (uid: string) => { const onEnhanceEnable = useLockFn(async (uid: string) => {
if (chain.includes(uid)) return; if (chain.includes(uid)) return;
@ -87,43 +72,22 @@ const EnhancedMode = (props: Props) => {
}); });
return ( return (
<Box sx={{ mt: 2 }}> <Grid container spacing={{ xs: 2, lg: 3 }}>
<Stack {items.map((item) => (
spacing={1} <Grid item xs={12} sm={6} md={4} lg={3} key={item.file}>
direction="row" <ProfileMore
alignItems="center" selected={!!chain.includes(item.uid)}
justifyContent="flex-end" itemData={item}
sx={{ mb: 0.5 }} enableNum={chain.length}
> logInfo={chainLogs[item.uid]}
<IconButton onEnable={() => onEnhanceEnable(item.uid)}
size="small" onDisable={() => onEnhanceDisable(item.uid)}
color="inherit" onDelete={() => onEnhanceDelete(item.uid)}
title={t("Refresh profiles")} onMoveTop={() => onMoveTop(item.uid)}
onClick={onEnhance} onMoveEnd={() => onMoveEnd(item.uid)}
> />
<RestartAltRounded /> </Grid>
</IconButton> ))}
</Stack> </Grid>
<Grid container spacing={2}>
{items.map((item) => (
<Grid item xs={12} sm={6} key={item.file}>
<ProfileMore
selected={!!chain.includes(item.uid)}
itemData={item}
enableNum={chain.length}
logInfo={chainLogs[item.uid]}
onEnable={() => onEnhanceEnable(item.uid)}
onDisable={() => onEnhanceDisable(item.uid)}
onDelete={() => onEnhanceDelete(item.uid)}
onMoveTop={() => onMoveTop(item.uid)}
onMoveEnd={() => onMoveEnd(item.uid)}
/>
</Grid>
))}
</Grid>
</Box>
); );
}; };
export default EnhancedMode;

View File

@ -7,7 +7,7 @@ interface Props {
onChange: (value: string) => void; onChange: (value: string) => void;
} }
const FileInput = (props: Props) => { export const FileInput = (props: Props) => {
const { onChange } = props; const { onChange } = props;
const { t } = useTranslation(); const { t } = useTranslation();
@ -59,5 +59,3 @@ const FileInput = (props: Props) => {
</Box> </Box>
); );
}; };
export default FileInput;

View File

@ -27,7 +27,7 @@ interface Props {
// edit the profile item // edit the profile item
// remote / local file / merge / script // remote / local file / merge / script
const InfoEditor = (props: Props) => { export const InfoViewer = (props: Props) => {
const { open, itemData, onClose } = props; const { open, itemData, onClose } = props;
const { t } = useTranslation(); const { t } = useTranslation();
@ -209,5 +209,3 @@ const InfoEditor = (props: Props) => {
</Dialog> </Dialog>
); );
}; };
export default InfoEditor;

View File

@ -18,7 +18,7 @@ interface Props {
onClose: () => void; onClose: () => void;
} }
const LogViewer = (props: Props) => { export const LogViewer = (props: Props) => {
const { open, logInfo, onClose } = props; const { open, logInfo, onClose } = props;
const { t } = useTranslation(); const { t } = useTranslation();
@ -67,5 +67,3 @@ const LogViewer = (props: Props) => {
</Dialog> </Dialog>
); );
}; };
export default LogViewer;

View File

@ -1,43 +1,43 @@
import { alpha, Box, styled } from "@mui/material"; import { alpha, Box, styled } from "@mui/material";
const ProfileBox = styled(Box)(({ theme, "aria-selected": selected }) => { export const ProfileBox = styled(Box)(
const { mode, primary, text, grey, background } = theme.palette; ({ theme, "aria-selected": selected }) => {
const key = `${mode}-${!!selected}`; const { mode, primary, text, grey, background } = theme.palette;
const key = `${mode}-${!!selected}`;
const backgroundColor = { const backgroundColor = {
"light-true": alpha(primary.main, 0.2), "light-true": alpha(primary.main, 0.2),
"light-false": alpha(background.paper, 0.75), "light-false": alpha(background.paper, 0.75),
"dark-true": alpha(primary.main, 0.45), "dark-true": alpha(primary.main, 0.45),
"dark-false": alpha(grey[700], 0.45), "dark-false": alpha(grey[700], 0.45),
}[key]!; }[key]!;
const color = { const color = {
"light-true": text.secondary, "light-true": text.secondary,
"light-false": text.secondary, "light-false": text.secondary,
"dark-true": alpha(text.secondary, 0.85), "dark-true": alpha(text.secondary, 0.85),
"dark-false": alpha(text.secondary, 0.65), "dark-false": alpha(text.secondary, 0.65),
}[key]!; }[key]!;
const h2color = { const h2color = {
"light-true": primary.main, "light-true": primary.main,
"light-false": text.primary, "light-false": text.primary,
"dark-true": primary.light, "dark-true": primary.light,
"dark-false": text.primary, "dark-false": text.primary,
}[key]!; }[key]!;
return { return {
width: "100%", width: "100%",
display: "block", display: "block",
cursor: "pointer", cursor: "pointer",
textAlign: "left", textAlign: "left",
borderRadius: theme.shape.borderRadius, borderRadius: theme.shape.borderRadius,
boxShadow: theme.shadows[2], boxShadow: theme.shadows[2],
padding: "8px 16px", padding: "8px 16px",
boxSizing: "border-box", boxSizing: "border-box",
backgroundColor, backgroundColor,
color, color,
"& h2": { color: h2color }, "& h2": { color: h2color },
}; };
}); }
);
export default ProfileBox;

View File

@ -17,10 +17,10 @@ import { RefreshRounded } from "@mui/icons-material";
import { atomLoadingCache } from "@/services/states"; import { atomLoadingCache } from "@/services/states";
import { updateProfile, deleteProfile, viewProfile } from "@/services/cmds"; import { updateProfile, deleteProfile, viewProfile } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { InfoViewer } from "./info-viewer";
import { EditorViewer } from "./editor-viewer";
import { ProfileBox } from "./profile-box";
import parseTraffic from "@/utils/parse-traffic"; import parseTraffic from "@/utils/parse-traffic";
import ProfileBox from "./profile-box";
import InfoEditor from "./info-editor";
import { FileEditor } from "./file-editor";
const round = keyframes` const round = keyframes`
from { transform: rotate(0deg); } from { transform: rotate(0deg); }
@ -33,7 +33,7 @@ interface Props {
onSelect: (force: boolean) => void; onSelect: (force: boolean) => void;
} }
const ProfileItem = (props: Props) => { export const ProfileItem = (props: Props) => {
const { selected, itemData, onSelect } = props; const { selected, itemData, onSelect } = props;
const { t } = useTranslation(); const { t } = useTranslation();
@ -298,13 +298,13 @@ const ProfileItem = (props: Props) => {
))} ))}
</Menu> </Menu>
<InfoEditor <InfoViewer
open={editOpen} open={editOpen}
itemData={itemData} itemData={itemData}
onClose={() => setEditOpen(false)} onClose={() => setEditOpen(false)}
/> />
<FileEditor <EditorViewer
uid={uid} uid={uid}
open={fileOpen} open={fileOpen}
mode="yaml" mode="yaml"
@ -325,5 +325,3 @@ function parseExpire(expire?: number) {
if (!expire) return "-"; if (!expire) return "-";
return dayjs(expire * 1000).format("YYYY-MM-DD"); return dayjs(expire * 1000).format("YYYY-MM-DD");
} }
export default ProfileItem;

View File

@ -14,10 +14,10 @@ import {
import { FeaturedPlayListRounded } from "@mui/icons-material"; import { FeaturedPlayListRounded } from "@mui/icons-material";
import { viewProfile } from "@/services/cmds"; import { viewProfile } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import InfoEditor from "./info-editor"; import { InfoViewer } from "./info-viewer";
import { FileEditor } from "./file-editor"; import { EditorViewer } from "./editor-viewer";
import ProfileBox from "./profile-box"; import { ProfileBox } from "./profile-box";
import LogViewer from "./log-viewer"; import { LogViewer } from "./log-viewer";
interface Props { interface Props {
selected: boolean; selected: boolean;
@ -32,7 +32,7 @@ interface Props {
} }
// profile enhanced item // profile enhanced item
const ProfileMore = (props: Props) => { export const ProfileMore = (props: Props) => {
const { const {
selected, selected,
itemData, itemData,
@ -219,13 +219,13 @@ const ProfileMore = (props: Props) => {
))} ))}
</Menu> </Menu>
<InfoEditor <InfoViewer
open={editOpen} open={editOpen}
itemData={itemData} itemData={itemData}
onClose={() => setEditOpen(false)} onClose={() => setEditOpen(false)}
/> />
<FileEditor <EditorViewer
uid={uid} uid={uid}
open={fileOpen} open={fileOpen}
mode={type === "merge" ? "yaml" : "javascript"} mode={type === "merge" ? "yaml" : "javascript"}
@ -247,5 +247,3 @@ function parseExpire(expire?: number) {
if (!expire) return "-"; if (!expire) return "-";
return dayjs(expire * 1000).format("YYYY-MM-DD"); return dayjs(expire * 1000).format("YYYY-MM-DD");
} }
export default ProfileMore;

View File

@ -21,7 +21,7 @@ import {
import { Settings } from "@mui/icons-material"; import { Settings } from "@mui/icons-material";
import { createProfile } from "@/services/cmds"; import { createProfile } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import FileInput from "./file-input"; import { FileInput } from "./file-input";
interface Props { interface Props {
open: boolean; open: boolean;
@ -30,7 +30,7 @@ interface Props {
// create a new profile // create a new profile
// remote / local file / merge / script // remote / local file / merge / script
const ProfileNew = (props: Props) => { export const ProfileNew = (props: Props) => {
const { open, onClose } = props; const { open, onClose } = props;
const { t } = useTranslation(); const { t } = useTranslation();
@ -210,5 +210,3 @@ const ProfileNew = (props: Props) => {
</Dialog> </Dialog>
); );
}; };
export default ProfileNew;

View File

@ -2,20 +2,22 @@ import useSWR, { mutate } from "swr";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useSetRecoilState } from "recoil"; import { useSetRecoilState } from "recoil";
import { Button, Grid, Stack, TextField } from "@mui/material"; import { Box, Button, Grid, IconButton, Stack, TextField } from "@mui/material";
import { CachedRounded } from "@mui/icons-material";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
getProfiles, getProfiles,
patchProfile, patchProfile,
patchProfilesConfig, patchProfilesConfig,
importProfile, importProfile,
enhanceProfiles,
} from "@/services/cmds"; } from "@/services/cmds";
import { getProxies, updateProxy } from "@/services/api"; import { getProxies, updateProxy } from "@/services/api";
import { atomCurrentProfile } from "@/services/states"; import { atomCurrentProfile } from "@/services/states";
import { BasePage, Notice } from "@/components/base"; import { BasePage, Notice } from "@/components/base";
import ProfileNew from "@/components/profile/profile-new"; import { ProfileNew } from "@/components/profile/profile-new";
import ProfileItem from "@/components/profile/profile-item"; import { ProfileItem } from "@/components/profile/profile-item";
import EnhancedMode from "@/components/profile/enhanced"; import { EnhancedMode } from "@/components/profile/enhanced";
const ProfilePage = () => { const ProfilePage = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -138,8 +140,32 @@ const ProfilePage = () => {
} }
}); });
const onEnhance = useLockFn(async () => {
try {
await enhanceProfiles();
mutate("getRuntimeLogs");
// Notice.success("Refresh clash config", 1000);
} catch (err: any) {
Notice.error(err.message || err.toString(), 3000);
}
});
return ( return (
<BasePage title={t("Profiles")}> <BasePage
title={t("Profiles")}
header={
<Box sx={{ mt: 1, display: "flex", alignItems: "center" }}>
<IconButton
size="small"
color="inherit"
title={t("Refresh profiles")}
onClick={onEnhance}
>
<CachedRounded />
</IconButton>
</Box>
}
>
<Stack direction="row" spacing={1} sx={{ mb: 2 }}> <Stack direction="row" spacing={1} sx={{ mb: 2 }}>
<TextField <TextField
hiddenLabel hiddenLabel
@ -170,17 +196,19 @@ const ProfilePage = () => {
</Button> </Button>
</Stack> </Stack>
<Grid container spacing={2}> <Box sx={{ mb: 4.5 }}>
{regularItems.map((item) => ( <Grid container spacing={{ xs: 2, lg: 3 }}>
<Grid item xs={12} sm={6} key={item.file}> {regularItems.map((item) => (
<ProfileItem <Grid item xs={12} sm={6} md={4} lg={3} key={item.file}>
selected={profiles.current === item.uid} <ProfileItem
itemData={item} selected={profiles.current === item.uid}
onSelect={(f) => onSelect(item.uid, f)} itemData={item}
/> onSelect={(f) => onSelect(item.uid, f)}
</Grid> />
))} </Grid>
</Grid> ))}
</Grid>
</Box>
{enhanceItems.length > 0 && ( {enhanceItems.length > 0 && (
<EnhancedMode items={enhanceItems} chain={profiles.chain || []} /> <EnhancedMode items={enhanceItems} chain={profiles.chain || []} />