feat: reactive after save when profile content changes

This commit is contained in:
dongchengjie 2024-06-29 09:21:50 +08:00
parent 6ee5e560cc
commit 80d3c9e96f
12 changed files with 157 additions and 71 deletions

View File

@ -27,10 +27,10 @@ export const ConfirmViewer = (props: Props) => {
return ( return (
<Dialog open={open} onClose={onClose} maxWidth="xs" fullWidth> <Dialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
<DialogTitle>{t(title)}</DialogTitle> <DialogTitle>{title}</DialogTitle>
<DialogContent sx={{ pb: 1, userSelect: "text" }}> <DialogContent sx={{ pb: 1, userSelect: "text" }}>
{t(message)} {message}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>

View File

@ -32,7 +32,7 @@ interface Props {
language: "yaml" | "javascript" | "css"; language: "yaml" | "javascript" | "css";
schema?: "clash" | "merge"; schema?: "clash" | "merge";
onClose: () => void; onClose: () => void;
onChange?: (content?: string) => void; onChange?: (prev?: string, curr?: string) => void;
} }
// yaml worker // yaml worker
@ -90,6 +90,7 @@ export const EditorViewer = (props: Props) => {
const editorRef = useRef<any>(); const editorRef = useRef<any>();
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null); const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
const themeMode = useThemeMode(); const themeMode = useThemeMode();
const prevData = useRef<string>();
useEffect(() => { useEffect(() => {
if (!open) return; if (!open) return;
@ -136,6 +137,8 @@ export const EditorViewer = (props: Props) => {
fontLigatures: true, // 连字符 fontLigatures: true, // 连字符
smoothScrolling: true, // 平滑滚动 smoothScrolling: true, // 平滑滚动
}); });
prevData.current = data;
}); });
return () => { return () => {
@ -147,15 +150,15 @@ export const EditorViewer = (props: Props) => {
}, [open]); }, [open]);
const onSave = useLockFn(async () => { const onSave = useLockFn(async () => {
const value = instanceRef.current?.getValue(); const currData = instanceRef.current?.getValue();
if (value == null) return; if (currData == null) return;
try { try {
if (mode === "profile") { if (mode === "profile") {
await saveProfileFile(property, value); await saveProfileFile(property, currData);
} }
onChange?.(value); onChange?.(prevData.current, currData);
onClose(); onClose();
} catch (err: any) { } catch (err: any) {
Notice.error(err.message || err.toString()); Notice.error(err.message || err.toString());

View File

@ -17,7 +17,7 @@ import {
} from "@mui/material"; } from "@mui/material";
import { RefreshRounded, DragIndicator } from "@mui/icons-material"; import { RefreshRounded, DragIndicator } from "@mui/icons-material";
import { useLoadingCache, useSetLoadingCache } from "@/services/states"; import { useLoadingCache, useSetLoadingCache } from "@/services/states";
import { updateProfile, deleteProfile, viewProfile } from "@/services/cmds"; import { updateProfile, viewProfile } from "@/services/cmds";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { EditorViewer } from "@/components/profile/editor-viewer"; import { EditorViewer } from "@/components/profile/editor-viewer";
import { ProfileBox } from "./profile-box"; import { ProfileBox } from "./profile-box";
@ -36,10 +36,20 @@ interface Props {
itemData: IProfileItem; itemData: IProfileItem;
onSelect: (force: boolean) => void; onSelect: (force: boolean) => void;
onEdit: () => void; onEdit: () => void;
onChange?: (prev?: string, curr?: string) => void;
onDelete: () => void;
} }
export const ProfileItem = (props: Props) => { export const ProfileItem = (props: Props) => {
const { selected, activating, itemData, onSelect, onEdit } = props; const {
selected,
activating,
itemData,
onSelect,
onEdit,
onChange,
onDelete,
} = props;
const { attributes, listeners, setNodeRef, transform, transition } = const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: props.id }); useSortable({ id: props.id });
@ -53,6 +63,7 @@ export const ProfileItem = (props: Props) => {
// local file mode // local file mode
// remote file mode // remote file mode
// remote file mode
const hasUrl = !!itemData.url; const hasUrl = !!itemData.url;
const hasExtra = !!extra; // only subscription url has extra info const hasExtra = !!extra; // only subscription url has extra info
const hasHome = !!itemData.home; // only subscription url has home page const hasHome = !!itemData.home; // only subscription url has home page
@ -162,16 +173,6 @@ export const ProfileItem = (props: Props) => {
} }
}); });
const onDelete = useLockFn(async () => {
setAnchorEl(null);
try {
await deleteProfile(itemData.uid);
mutate("getProfiles");
} catch (err: any) {
Notice.error(err?.message || err.toString());
}
});
const urlModeMenu = ( const urlModeMenu = (
hasHome ? [{ label: "Home", handler: onOpenHome }] : [] hasHome ? [{ label: "Home", handler: onOpenHome }] : []
).concat([ ).concat([
@ -242,7 +243,7 @@ export const ProfileItem = (props: Props) => {
backdropFilter: "blur(2px)", backdropFilter: "blur(2px)",
}} }}
> >
<CircularProgress size={20} /> <CircularProgress color="inherit" size={20} />
</Box> </Box>
)} )}
<Box position="relative"> <Box position="relative">
@ -312,7 +313,7 @@ export const ProfileItem = (props: Props) => {
</Typography> </Typography>
) : ( ) : (
hasUrl && ( hasUrl && (
<Typography noWrap title={`From ${from}`}> <Typography noWrap title={`${t("From")} ${from}`}>
{from} {from}
</Typography> </Typography>
) )
@ -323,7 +324,7 @@ export const ProfileItem = (props: Props) => {
flex="1 0 auto" flex="1 0 auto"
fontSize={14} fontSize={14}
textAlign="right" textAlign="right"
title={`Updated Time: ${parseExpire(updated)}`} title={`${t("Update Time")}: ${parseExpire(updated)}`}
> >
{updated > 0 ? dayjs(updated * 1000).fromNow() : ""} {updated > 0 ? dayjs(updated * 1000).fromNow() : ""}
</Typography> </Typography>
@ -334,17 +335,21 @@ export const ProfileItem = (props: Props) => {
{/* the third line show extra info or last updated time */} {/* the third line show extra info or last updated time */}
{hasExtra ? ( {hasExtra ? (
<Box sx={{ ...boxStyle, fontSize: 14 }}> <Box sx={{ ...boxStyle, fontSize: 14 }}>
<span title="Used / Total"> <span title={t("Used / Total")}>
{parseTraffic(upload + download)} / {parseTraffic(total)} {parseTraffic(upload + download)} / {parseTraffic(total)}
</span> </span>
<span title="Expire Time">{expire}</span> <span title={t("Expire Time")}>{expire}</span>
</Box> </Box>
) : ( ) : (
<Box sx={{ ...boxStyle, fontSize: 12, justifyContent: "flex-end" }}> <Box sx={{ ...boxStyle, fontSize: 12, justifyContent: "flex-end" }}>
<span title="Updated Time">{parseExpire(updated)}</span> <span title={t("Update Time")}>{parseExpire(updated)}</span>
</Box> </Box>
)} )}
<LinearProgress variant="determinate" value={progress} /> <LinearProgress
variant="determinate"
value={progress}
style={{ opacity: progress > 0 ? 1 : 0 }}
/>
</ProfileBox> </ProfileBox>
<Menu <Menu
@ -390,11 +395,12 @@ export const ProfileItem = (props: Props) => {
open={fileOpen} open={fileOpen}
language="yaml" language="yaml"
schema="clash" schema="clash"
onChange={onChange}
onClose={() => setFileOpen(false)} onClose={() => setFileOpen(false)}
/> />
<ConfirmViewer <ConfirmViewer
title="Confirm deletion" title={t("Confirm deletion")}
message="This operation is not reversible" message={t("This operation is not reversible")}
open={confirmOpen} open={confirmOpen}
onClose={() => setConfirmOpen(false)} onClose={() => setConfirmOpen(false)}
onConfirm={() => { onConfirm={() => {

View File

@ -9,6 +9,7 @@ import {
MenuItem, MenuItem,
Menu, Menu,
IconButton, IconButton,
CircularProgress,
} from "@mui/material"; } from "@mui/material";
import { FeaturedPlayListRounded } from "@mui/icons-material"; import { FeaturedPlayListRounded } from "@mui/icons-material";
import { viewProfile } from "@/services/cmds"; import { viewProfile } from "@/services/cmds";
@ -20,6 +21,7 @@ import { ConfirmViewer } from "./confirm-viewer";
interface Props { interface Props {
selected: boolean; selected: boolean;
activating: boolean;
itemData: IProfileItem; itemData: IProfileItem;
enableNum: number; enableNum: number;
logInfo?: [string, string][]; logInfo?: [string, string][];
@ -27,14 +29,16 @@ interface Props {
onDisable: () => void; onDisable: () => void;
onMoveTop: () => void; onMoveTop: () => void;
onMoveEnd: () => void; onMoveEnd: () => void;
onDelete: () => void;
onEdit: () => void; onEdit: () => void;
onChange?: (prev?: string, curr?: string) => void;
onDelete: () => void;
} }
// profile enhanced item // profile enhanced item
export const ProfileMore = (props: Props) => { export const ProfileMore = (props: Props) => {
const { const {
selected, selected,
activating,
itemData, itemData,
enableNum, enableNum,
logInfo = [], logInfo = [],
@ -44,6 +48,7 @@ export const ProfileMore = (props: Props) => {
onMoveEnd, onMoveEnd,
onDelete, onDelete,
onEdit, onEdit,
onChange,
} = props; } = props;
const { uid, type } = itemData; const { uid, type } = itemData;
@ -132,6 +137,24 @@ export const ProfileMore = (props: Props) => {
event.preventDefault(); event.preventDefault();
}} }}
> >
{activating && (
<Box
sx={{
position: "absolute",
display: "flex",
justifyContent: "center",
alignItems: "center",
top: 10,
left: 10,
right: 10,
bottom: 2,
zIndex: 10,
backdropFilter: "blur(2px)",
}}
>
<CircularProgress color="inherit" size={20} />
</Box>
)}
<Box <Box
display="flex" display="flex"
justifyContent="space-between" justifyContent="space-between"
@ -237,11 +260,12 @@ export const ProfileMore = (props: Props) => {
open={fileOpen} open={fileOpen}
language={type === "merge" ? "yaml" : "javascript"} language={type === "merge" ? "yaml" : "javascript"}
schema={type === "merge" ? "merge" : undefined} schema={type === "merge" ? "merge" : undefined}
onChange={onChange}
onClose={() => setFileOpen(false)} onClose={() => setFileOpen(false)}
/> />
<ConfirmViewer <ConfirmViewer
title="Confirm deletion" title={t("Confirm deletion")}
message="This operation is not reversible" message={t("This operation is not reversible")}
open={confirmOpen} open={confirmOpen}
onClose={() => setConfirmOpen(false)} onClose={() => setConfirmOpen(false)}
onConfirm={() => { onConfirm={() => {

View File

@ -249,10 +249,10 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
property={value.pac_content ?? ""} property={value.pac_content ?? ""}
open={editorOpen} open={editorOpen}
language="javascript" language="javascript"
onChange={(content) => { onChange={(_prev, curr) => {
let pac = DEFAULT_PAC; let pac = DEFAULT_PAC;
if (content && content.trim().length > 0) { if (curr && curr.trim().length > 0) {
pac = content; pac = curr;
} }
setValue((v) => ({ ...v, pac_content: pac })); setValue((v) => ({ ...v, pac_content: pac }));
}} }}

View File

@ -129,8 +129,8 @@ export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
property={theme.css_injection ?? ""} property={theme.css_injection ?? ""}
open={editorOpen} open={editorOpen}
language="css" language="css"
onChange={(content) => { onChange={(_prev, curr) => {
theme.css_injection = content; theme.css_injection = curr;
handleChange("css_injection"); handleChange("css_injection");
}} }}
onClose={() => { onClose={() => {

View File

@ -49,6 +49,10 @@
"Paste": "Paste", "Paste": "Paste",
"Profile URL": "Profile URL", "Profile URL": "Profile URL",
"Import": "Import", "Import": "Import",
"From": "From",
"Update Time": "Update Time",
"Used / Total": "Used / Total",
"Expire Time": "Expire Time",
"Create Profile": "Create Profile", "Create Profile": "Create Profile",
"Edit Profile": "Edit Profile", "Edit Profile": "Edit Profile",
"Type": "Type", "Type": "Type",
@ -178,6 +182,9 @@
"Open UWP tool Info": "Since Windows 8, UWP apps (such as Microsoft Store) are restricted from directly accessing local host network services, and this tool can be used to bypass this restriction", "Open UWP tool Info": "Since Windows 8, UWP apps (such as Microsoft Store) are restricted from directly accessing local host network services, and this tool can be used to bypass this restriction",
"Update GeoData": "Update GeoData", "Update GeoData": "Update GeoData",
"TG Channel": "Telegram Channel",
"Manual": "Manual",
"Github Repo": "Github Repo",
"Verge Setting": "Verge Setting", "Verge Setting": "Verge Setting",
"Language": "Language", "Language": "Language",
"Theme Mode": "Theme Mode", "Theme Mode": "Theme Mode",
@ -246,9 +253,6 @@
"Open Dev Tools": "Open Dev Tools", "Open Dev Tools": "Open Dev Tools",
"Exit": "Exit", "Exit": "Exit",
"Verge Version": "Verge Version", "Verge Version": "Verge Version",
"TG Channel": "Telegram Channel",
"Doc": "Docs",
"Source Code": "Source Code",
"ReadOnly": "ReadOnly", "ReadOnly": "ReadOnly",
"ReadOnlyMessage": "Cannot edit in read-only editor", "ReadOnlyMessage": "Cannot edit in read-only editor",

View File

@ -49,6 +49,10 @@
"Paste": "چسباندن", "Paste": "چسباندن",
"Profile URL": "آدرس پروفایل", "Profile URL": "آدرس پروفایل",
"Import": "وارد کردن", "Import": "وارد کردن",
"From": "از",
"Update Time": "زمان به‌روزرسانی",
"Used / Total": "استفاده‌شده / کل",
"Expire Time": "زمان انقضا",
"Create Profile": "ایجاد پروفایل", "Create Profile": "ایجاد پروفایل",
"Edit Profile": "ویرایش پروفایل", "Edit Profile": "ویرایش پروفایل",
"Type": "نوع", "Type": "نوع",
@ -183,6 +187,9 @@
"Open UWP tool Info": "از ویندوز 8 به بعد، برنامه‌های UWP (مانند Microsoft Store) از دسترسی مستقیم به خدمات شبکه محلی محدود شده‌اند و این ابزار می‌تواند برای دور زدن این محدودیت استفاده شود", "Open UWP tool Info": "از ویندوز 8 به بعد، برنامه‌های UWP (مانند Microsoft Store) از دسترسی مستقیم به خدمات شبکه محلی محدود شده‌اند و این ابزار می‌تواند برای دور زدن این محدودیت استفاده شود",
"Update GeoData": "به‌روزرسانی GeoData", "Update GeoData": "به‌روزرسانی GeoData",
"TG Channel": "کانال تلگرام",
"Manual": "راهنما",
"Github Repo": "مخزن GitHub",
"Verge Setting": "تنظیمات Verge", "Verge Setting": "تنظیمات Verge",
"Language": "زبان", "Language": "زبان",
"Theme Mode": "حالت تم", "Theme Mode": "حالت تم",
@ -251,9 +258,6 @@
"Open Dev Tools": "باز کردن ابزارهای توسعه‌دهنده", "Open Dev Tools": "باز کردن ابزارهای توسعه‌دهنده",
"Exit": "خروج", "Exit": "خروج",
"Verge Version": "نسخه Verge", "Verge Version": "نسخه Verge",
"TG Channel": "کانال تلگرام",
"Doc": "سند",
"Source Code": "کد منبع",
"ReadOnly": "فقط خواندنی", "ReadOnly": "فقط خواندنی",
"ReadOnlyMessage": "نمی‌توان در ویرایشگر فقط خواندنی ویرایش کرد", "ReadOnlyMessage": "نمی‌توان در ویرایشگر فقط خواندنی ویرایش کرد",

View File

@ -49,6 +49,10 @@
"Paste": "Вставить", "Paste": "Вставить",
"Profile URL": "URL профиля", "Profile URL": "URL профиля",
"Import": "Импорт", "Import": "Импорт",
"From": "От",
"Update Time": "Время обновления",
"Used / Total": "Использовано / Всего",
"Expire Time": "Время окончания",
"Create Profile": "Создать профиль", "Create Profile": "Создать профиль",
"Edit Profile": "Изменить профиль", "Edit Profile": "Изменить профиль",
"Type": "Тип", "Type": "Тип",
@ -183,6 +187,9 @@
"Open UWP tool Info": "С Windows 8 приложения UWP (такие как Microsoft Store) ограничены в прямом доступе к сетевым службам локального хоста, и этот инструмент позволяет обойти это ограничение", "Open UWP tool Info": "С Windows 8 приложения UWP (такие как Microsoft Store) ограничены в прямом доступе к сетевым службам локального хоста, и этот инструмент позволяет обойти это ограничение",
"Update GeoData": "Обновление GeoData", "Update GeoData": "Обновление GeoData",
"TG Channel": "Канал Telegram",
"Manual": "Документация",
"Github Repo": "GitHub репозиторий",
"Verge Setting": "Настройки Verge", "Verge Setting": "Настройки Verge",
"Language": "Язык", "Language": "Язык",
"Theme Mode": "Режим темы", "Theme Mode": "Режим темы",
@ -251,9 +258,6 @@
"Open Dev Tools": "Открыть инструменты разработчика", "Open Dev Tools": "Открыть инструменты разработчика",
"Exit": "Выход", "Exit": "Выход",
"Verge Version": "Версия Verge", "Verge Version": "Версия Verge",
"TG Channel": "Канал Telegram",
"Doc": "документ",
"Source Code": "Исходный код",
"ReadOnly": "Только для чтения", "ReadOnly": "Только для чтения",
"ReadOnlyMessage": "Невозможно редактировать в режиме только для чтения", "ReadOnlyMessage": "Невозможно редактировать в режиме только для чтения",

View File

@ -49,6 +49,10 @@
"Paste": "粘贴", "Paste": "粘贴",
"Profile URL": "订阅文件链接", "Profile URL": "订阅文件链接",
"Import": "导入", "Import": "导入",
"From": "来自",
"Update Time": "更新时间",
"Used / Total": "已使用 / 总量",
"Expire Time": "到期时间",
"Create Profile": "新建配置", "Create Profile": "新建配置",
"Edit Profile": "编辑配置", "Edit Profile": "编辑配置",
"Type": "类型", "Type": "类型",
@ -154,6 +158,9 @@
"Silent Start": "静默启动", "Silent Start": "静默启动",
"Silent Start Info": "程序启动时以后台模式运行,不显示程序面板", "Silent Start Info": "程序启动时以后台模式运行,不显示程序面板",
"TG Channel": "Telegram 频道",
"Manual": "使用手册",
"Github Repo": "GitHub 项目地址",
"Clash Setting": "Clash 设置", "Clash Setting": "Clash 设置",
"Allow Lan": "局域网连接", "Allow Lan": "局域网连接",
"IPv6": "IPv6", "IPv6": "IPv6",
@ -176,7 +183,7 @@
"Upgrade": "升级内核", "Upgrade": "升级内核",
"Restart": "重启内核", "Restart": "重启内核",
"Release Version": "正式版", "Release Version": "正式版",
"Alpha Version": "测版", "Alpha Version": "版",
"Tun mode requires": "如需启用 Tun 模式需要授权", "Tun mode requires": "如需启用 Tun 模式需要授权",
"Grant": "授权", "Grant": "授权",
"Open UWP tool": "UWP 工具", "Open UWP tool": "UWP 工具",
@ -251,9 +258,6 @@
"Open Dev Tools": "打开开发者工具", "Open Dev Tools": "打开开发者工具",
"Exit": "退出", "Exit": "退出",
"Verge Version": "Verge 版本", "Verge Version": "Verge 版本",
"TG Channel": "Telegram 频道",
"Doc": "文档",
"Source Code": "源代码",
"ReadOnly": "只读", "ReadOnly": "只读",
"ReadOnlyMessage": "无法在只读模式下编辑", "ReadOnlyMessage": "无法在只读模式下编辑",

View File

@ -56,7 +56,7 @@ const ProfilePage = () => {
const [url, setUrl] = useState(""); const [url, setUrl] = useState("");
const [disabled, setDisabled] = useState(false); const [disabled, setDisabled] = useState(false);
const [activating, setActivating] = useState(""); const [activatings, setActivatings] = useState<string[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor), useSensor(PointerSensor),
@ -128,6 +128,10 @@ const ProfilePage = () => {
return { regularItems, enhanceItems }; return { regularItems, enhanceItems };
}, [profiles]); }, [profiles]);
const currentActivatings = () => {
return [...new Set([profiles.current ?? "", ...chain])].filter(Boolean);
};
const onImport = async () => { const onImport = async () => {
if (!url) return; if (!url) return;
setLoading(true); setLoading(true);
@ -138,13 +142,13 @@ const ProfilePage = () => {
setUrl(""); setUrl("");
setLoading(false); setLoading(false);
getProfiles().then((newProfiles) => { getProfiles().then(async (newProfiles) => {
mutate("getProfiles", newProfiles); mutate("getProfiles", newProfiles);
const remoteItem = newProfiles.items?.find((e) => e.type === "remote"); const remoteItem = newProfiles.items?.find((e) => e.type === "remote");
if (!newProfiles.current && remoteItem) { if (!newProfiles.current && remoteItem) {
const current = remoteItem.uid; const current = remoteItem.uid;
patchProfiles({ current }); await patchProfiles({ current });
mutateLogs(); mutateLogs();
setTimeout(() => activateSelected(), 2000); setTimeout(() => activateSelected(), 2000);
} }
@ -171,7 +175,9 @@ const ProfilePage = () => {
const onSelect = useLockFn(async (current: string, force: boolean) => { const onSelect = useLockFn(async (current: string, force: boolean) => {
if (!force && current === profiles.current) return; if (!force && current === profiles.current) return;
// 避免大多数情况下loading态闪烁 // 避免大多数情况下loading态闪烁
const reset = setTimeout(() => setActivating(current), 100); const reset = setTimeout(() => {
setActivatings([...currentActivatings(), current]);
}, 100);
try { try {
await patchProfiles({ current }); await patchProfiles({ current });
mutateLogs(); mutateLogs();
@ -182,42 +188,64 @@ const ProfilePage = () => {
Notice.error(err?.message || err.toString(), 4000); Notice.error(err?.message || err.toString(), 4000);
} finally { } finally {
clearTimeout(reset); clearTimeout(reset);
setActivating(""); setActivatings([]);
} }
}); });
const onEnhance = useLockFn(async () => { const onEnhance = useLockFn(async () => {
setActivatings(currentActivatings());
try { try {
await enhanceProfiles(); await enhanceProfiles();
mutateLogs(); mutateLogs();
Notice.success(t("Profile Reactivated"), 1000); Notice.success(t("Profile Reactivated"), 1000);
} catch (err: any) { } catch (err: any) {
Notice.error(err.message || err.toString(), 3000); Notice.error(err.message || err.toString(), 3000);
} finally {
setActivatings([]);
} }
}); });
const onEnable = useLockFn(async (uid: string) => { const onEnable = useLockFn(async (uid: string) => {
if (chain.includes(uid)) return; if (chain.includes(uid)) return;
const newChain = [...chain, uid]; try {
await patchProfiles({ chain: newChain }); setActivatings([...currentActivatings(), uid]);
mutateLogs(); const newChain = [...chain, uid];
await patchProfiles({ chain: newChain });
mutateLogs();
} catch (err: any) {
Notice.error(err.message || err.toString(), 3000);
} finally {
setActivatings([]);
}
}); });
const onDisable = useLockFn(async (uid: string) => { const onDisable = useLockFn(async (uid: string) => {
if (!chain.includes(uid)) return; if (!chain.includes(uid)) return;
const newChain = chain.filter((i) => i !== uid); try {
await patchProfiles({ chain: newChain }); setActivatings([...currentActivatings(), uid]);
mutateLogs(); const newChain = chain.filter((i) => i !== uid);
await patchProfiles({ chain: newChain });
mutateLogs();
} catch (err: any) {
Notice.error(err.message || err.toString(), 3000);
} finally {
setActivatings([]);
}
}); });
const onDelete = useLockFn(async (uid: string) => { const onDelete = useLockFn(async (uid: string) => {
const current = profiles.current === uid;
try { try {
await onDisable(uid); await onDisable(uid);
setActivatings([...(current ? currentActivatings() : []), uid]);
await deleteProfile(uid); await deleteProfile(uid);
mutateProfiles(); mutateProfiles();
mutateLogs(); mutateLogs();
current && (await onEnhance());
} catch (err: any) { } catch (err: any) {
Notice.error(err?.message || err.toString()); Notice.error(err?.message || err.toString());
} finally {
setActivatings([]);
} }
}); });
@ -396,10 +424,14 @@ const ProfilePage = () => {
<ProfileItem <ProfileItem
id={item.uid} id={item.uid}
selected={profiles.current === item.uid} selected={profiles.current === item.uid}
activating={activating === item.uid} activating={activatings.includes(item.uid)}
itemData={item} itemData={item}
onSelect={(f) => onSelect(item.uid, f)} onSelect={(f) => onSelect(item.uid, f)}
onEdit={() => viewerRef.current?.edit(item)} onEdit={() => viewerRef.current?.edit(item)}
onChange={async (prev, curr) => {
prev !== curr && (await onEnhance());
}}
onDelete={() => onDelete(item.uid)}
/> />
</Grid> </Grid>
))} ))}
@ -423,6 +455,7 @@ const ProfilePage = () => {
<Grid item xs={12} sm={6} md={4} lg={3} key={item.file}> <Grid item xs={12} sm={6} md={4} lg={3} key={item.file}>
<ProfileMore <ProfileMore
selected={!!chain.includes(item.uid)} selected={!!chain.includes(item.uid)}
activating={activatings.includes(item.uid)}
itemData={item} itemData={item}
enableNum={chain.length || 0} enableNum={chain.length || 0}
logInfo={chainLogs[item.uid]} logInfo={chainLogs[item.uid]}
@ -432,6 +465,9 @@ const ProfilePage = () => {
onMoveTop={() => onMoveTop(item.uid)} onMoveTop={() => onMoveTop(item.uid)}
onMoveEnd={() => onMoveEnd(item.uid)} onMoveEnd={() => onMoveEnd(item.uid)}
onEdit={() => viewerRef.current?.edit(item)} onEdit={() => viewerRef.current?.edit(item)}
onChange={async (prev, curr) => {
prev !== curr && (await onEnhance());
}}
/> />
</Grid> </Grid>
))} ))}

View File

@ -39,15 +39,7 @@ const SettingPage = () => {
<IconButton <IconButton
size="medium" size="medium"
color="inherit" color="inherit"
title={t("TG Channel")} title={t("Manual")}
onClick={toTelegramChannel}
>
<Telegram fontSize="inherit" />
</IconButton>
<IconButton
size="medium"
color="inherit"
title={t("Doc")}
onClick={toGithubDoc} onClick={toGithubDoc}
> >
<HelpOutlineSharp fontSize="inherit" /> <HelpOutlineSharp fontSize="inherit" />
@ -55,7 +47,16 @@ const SettingPage = () => {
<IconButton <IconButton
size="medium" size="medium"
color="inherit" color="inherit"
title={t("Source Code")} title={t("TG Channel")}
onClick={toTelegramChannel}
>
<Telegram fontSize="inherit" />
</IconButton>
<IconButton
size="medium"
color="inherit"
title={t("Github Repo")}
onClick={toGithubRepo} onClick={toGithubRepo}
> >
<GitHub fontSize="inherit" /> <GitHub fontSize="inherit" />