feat: profile enhanced mode
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
import dayjs from "dayjs";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useSWRConfig } from "swr";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
alpha,
|
||||
@@ -12,7 +10,7 @@ import {
|
||||
Menu,
|
||||
} from "@mui/material";
|
||||
import { CmdType } from "../../services/types";
|
||||
import { deleteProfile, viewProfile } from "../../services/cmds";
|
||||
import { viewProfile } from "../../services/cmds";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import ProfileEdit from "./profile-edit";
|
||||
import Notice from "../base/base-notice";
|
||||
@@ -37,6 +35,7 @@ interface Props {
|
||||
onDisable: () => void;
|
||||
onMoveTop: () => void;
|
||||
onMoveEnd: () => void;
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
// profile enhanced item
|
||||
@@ -48,10 +47,10 @@ const ProfileMore = (props: Props) => {
|
||||
onDisable,
|
||||
onMoveTop,
|
||||
onMoveEnd,
|
||||
onDelete,
|
||||
} = props;
|
||||
|
||||
const { type } = itemData;
|
||||
const { mutate } = useSWRConfig();
|
||||
const [anchorEl, setAnchorEl] = useState<any>(null);
|
||||
const [position, setPosition] = useState({ left: 0, top: 0 });
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
@@ -70,30 +69,25 @@ const ProfileMore = (props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = useLockFn(async () => {
|
||||
const closeWrapper = (fn: () => void) => () => {
|
||||
setAnchorEl(null);
|
||||
try {
|
||||
await deleteProfile(itemData.uid);
|
||||
mutate("getProfiles");
|
||||
} catch (err: any) {
|
||||
Notice.error(err?.message || err.toString());
|
||||
}
|
||||
});
|
||||
return fn();
|
||||
};
|
||||
|
||||
const enableMenu = [
|
||||
{ label: "Disable", handler: onDisable },
|
||||
{ label: "Disable", handler: closeWrapper(onDisable) },
|
||||
{ label: "Edit", handler: onEdit },
|
||||
{ label: "View File", handler: onView },
|
||||
{ label: "To Top", handler: onMoveTop },
|
||||
{ label: "To End", handler: onMoveEnd },
|
||||
{ label: "Delete", handler: onDelete },
|
||||
{ label: "To Top", handler: closeWrapper(onMoveTop) },
|
||||
{ label: "To End", handler: closeWrapper(onMoveEnd) },
|
||||
{ label: "Delete", handler: closeWrapper(onDelete) },
|
||||
];
|
||||
|
||||
const disableMenu = [
|
||||
{ label: "Enable", handler: onEnable },
|
||||
{ label: "Enable", handler: closeWrapper(onEnable) },
|
||||
{ label: "Edit", handler: onEdit },
|
||||
{ label: "View File", handler: onView },
|
||||
{ label: "Delete", handler: onDelete },
|
||||
{ label: "Delete", handler: closeWrapper(onDelete) },
|
||||
];
|
||||
|
||||
const boxStyle = {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import useSWR, { useSWRConfig } from "swr";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useLockFn } from "ahooks";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Box, Button, Grid, TextField } from "@mui/material";
|
||||
import {
|
||||
getProfiles,
|
||||
selectProfile,
|
||||
patchProfile,
|
||||
deleteProfile,
|
||||
selectProfile,
|
||||
importProfile,
|
||||
changeProfileChain,
|
||||
} from "../services/cmds";
|
||||
import { getProxies, updateProxy } from "../services/api";
|
||||
import Notice from "../components/base/base-notice";
|
||||
@@ -25,13 +27,20 @@ const ProfilePage = () => {
|
||||
const { data: profiles = {} } = useSWR("getProfiles", getProfiles);
|
||||
|
||||
const { regularItems, enhanceItems } = useMemo(() => {
|
||||
const { items = [] } = profiles;
|
||||
const regularItems = items.filter((i) =>
|
||||
["local", "remote"].includes(i.type!)
|
||||
);
|
||||
const enhanceItems = items.filter((i) =>
|
||||
["merge", "script"].includes(i.type!)
|
||||
);
|
||||
const items = profiles.items || [];
|
||||
const chain = profiles.chain || [];
|
||||
|
||||
const type1 = ["local", "remote"];
|
||||
const type2 = ["merge", "script"];
|
||||
|
||||
const regularItems = items.filter((i) => type1.includes(i.type!));
|
||||
const restItems = items.filter((i) => type2.includes(i.type!));
|
||||
|
||||
const restMap = Object.fromEntries(restItems.map((i) => [i.uid, i]));
|
||||
|
||||
const enhanceItems = chain
|
||||
.map((i) => restMap[i]!)
|
||||
.concat(restItems.filter((i) => !chain.includes(i.uid)));
|
||||
|
||||
return { regularItems, enhanceItems };
|
||||
}, [profiles]);
|
||||
@@ -113,10 +122,51 @@ const ProfilePage = () => {
|
||||
}
|
||||
});
|
||||
|
||||
const onEnhanceEnable = useLockFn(async (uid: string) => {});
|
||||
const onEnhanceDisable = useLockFn(async (uid: string) => {});
|
||||
const onMoveTop = useLockFn(async (uid: string) => {});
|
||||
const onMoveEnd = useLockFn(async (uid: string) => {});
|
||||
/** enhanced profile mode */
|
||||
|
||||
const chain = profiles.chain || [];
|
||||
|
||||
const onEnhanceEnable = useLockFn(async (uid: string) => {
|
||||
if (chain.includes(uid)) return;
|
||||
|
||||
const newChain = [...chain, uid];
|
||||
await changeProfileChain(newChain);
|
||||
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||
});
|
||||
|
||||
const onEnhanceDisable = useLockFn(async (uid: string) => {
|
||||
if (!chain.includes(uid)) return;
|
||||
|
||||
const newChain = chain.filter((i) => i !== uid);
|
||||
await changeProfileChain(newChain);
|
||||
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||
});
|
||||
|
||||
const onEnhanceDelete = useLockFn(async (uid: string) => {
|
||||
try {
|
||||
await onEnhanceDisable(uid);
|
||||
await deleteProfile(uid);
|
||||
mutate("getProfiles");
|
||||
} catch (err: any) {
|
||||
Notice.error(err?.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
const onMoveTop = useLockFn(async (uid: string) => {
|
||||
if (!chain.includes(uid)) return;
|
||||
|
||||
const newChain = [uid].concat(chain.filter((i) => i !== uid));
|
||||
await changeProfileChain(newChain);
|
||||
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||
});
|
||||
|
||||
const onMoveEnd = useLockFn(async (uid: string) => {
|
||||
if (!chain.includes(uid)) return;
|
||||
|
||||
const newChain = chain.filter((i) => i !== uid).concat([uid]);
|
||||
await changeProfileChain(newChain);
|
||||
mutate("getProfiles", { ...profiles, chain: newChain }, true);
|
||||
});
|
||||
|
||||
return (
|
||||
<BasePage title="Profiles">
|
||||
@@ -164,6 +214,7 @@ const ProfilePage = () => {
|
||||
itemData={item}
|
||||
onEnable={() => onEnhanceEnable(item.uid)}
|
||||
onDisable={() => onEnhanceDisable(item.uid)}
|
||||
onDelete={() => onEnhanceDelete(item.uid)}
|
||||
onMoveTop={() => onMoveTop(item.uid)}
|
||||
onMoveEnd={() => onMoveEnd(item.uid)}
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,10 @@ export async function syncProfiles() {
|
||||
return invoke<void>("sync_profiles");
|
||||
}
|
||||
|
||||
export async function enhanceProfiles() {
|
||||
return invoke<void>("enhance_profiles");
|
||||
}
|
||||
|
||||
export async function createProfile(item: Partial<CmdType.ProfileItem>) {
|
||||
return invoke<void>("create_profile", { item });
|
||||
}
|
||||
@@ -40,8 +44,8 @@ export async function selectProfile(index: string) {
|
||||
return invoke<void>("select_profile", { index });
|
||||
}
|
||||
|
||||
export async function restartSidecar() {
|
||||
return invoke<void>("restart_sidecar");
|
||||
export async function changeProfileChain(chain?: string[]) {
|
||||
return invoke<void>("change_profile_chain", { chain });
|
||||
}
|
||||
|
||||
export async function getClashInfo() {
|
||||
@@ -64,6 +68,10 @@ export async function getSystemProxy() {
|
||||
return invoke<any>("get_sys_proxy");
|
||||
}
|
||||
|
||||
export async function restartSidecar() {
|
||||
return invoke<void>("restart_sidecar");
|
||||
}
|
||||
|
||||
export async function killSidecars() {
|
||||
return invoke<any>("kill_sidecars");
|
||||
}
|
||||
|
||||
@@ -1,22 +1,98 @@
|
||||
import { emit, listen } from "@tauri-apps/api/event";
|
||||
import { CmdType } from "./types";
|
||||
|
||||
function toMerge(
|
||||
merge: CmdType.ProfileMerge,
|
||||
data: CmdType.ProfileData
|
||||
): CmdType.ProfileData {
|
||||
if (!merge) return data;
|
||||
|
||||
const newData = { ...data };
|
||||
|
||||
// rules
|
||||
if (Array.isArray(merge["prepend-rules"])) {
|
||||
if (!newData.rules) newData.rules = [];
|
||||
newData.rules.unshift(...merge["prepend-rules"]);
|
||||
}
|
||||
if (Array.isArray(merge["append-rules"])) {
|
||||
if (!newData.rules) newData.rules = [];
|
||||
newData.rules.push(...merge["append-rules"]);
|
||||
}
|
||||
|
||||
// proxies
|
||||
if (Array.isArray(merge["prepend-proxies"])) {
|
||||
if (!newData.proxies) newData.proxies = [];
|
||||
newData.proxies.unshift(...merge["prepend-proxies"]);
|
||||
}
|
||||
if (Array.isArray(merge["append-proxies"])) {
|
||||
if (!newData.proxies) newData.proxies = [];
|
||||
newData.proxies.push(...merge["append-proxies"]);
|
||||
}
|
||||
|
||||
// proxy-groups
|
||||
if (Array.isArray(merge["prepend-proxy-groups"])) {
|
||||
if (!newData["proxy-groups"]) newData["proxy-groups"] = [];
|
||||
newData["proxy-groups"].unshift(...merge["prepend-proxy-groups"]);
|
||||
}
|
||||
if (Array.isArray(merge["append-proxy-groups"])) {
|
||||
if (!newData["proxy-groups"]) newData["proxy-groups"] = [];
|
||||
newData["proxy-groups"].push(...merge["append-proxy-groups"]);
|
||||
}
|
||||
|
||||
return newData;
|
||||
}
|
||||
|
||||
function toScript(
|
||||
script: string,
|
||||
data: CmdType.ProfileData
|
||||
): Promise<CmdType.ProfileData> {
|
||||
if (!script) {
|
||||
throw new Error("miss the main function");
|
||||
}
|
||||
|
||||
const paramsName = `__verge${Math.floor(Math.random() * 1000)}`;
|
||||
const code = `'use strict';${script};return main(${paramsName});`;
|
||||
const func = new Function(paramsName, code);
|
||||
return func(data); // support async main function
|
||||
}
|
||||
|
||||
export default function setup() {
|
||||
listen("script-handler", (event) => {
|
||||
listen("script-handler", async (event) => {
|
||||
const payload = event.payload as CmdType.EnhancedPayload;
|
||||
console.log(payload);
|
||||
|
||||
// setTimeout(() => {
|
||||
// try {
|
||||
// const fn = eval(payload.script + "\n\nmixin");
|
||||
// console.log(fn);
|
||||
let pdata = payload.current || {};
|
||||
|
||||
// const result = fn(payload.params || {});
|
||||
// console.log("result", result);
|
||||
// emit(payload.callback, JSON.stringify(result)).catch(console.error);
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
// }
|
||||
// }, 3000);
|
||||
for (const each of payload.chain) {
|
||||
try {
|
||||
// process script
|
||||
if (each.item.type === "script") {
|
||||
pdata = await toScript(each.script!, pdata);
|
||||
}
|
||||
|
||||
// process merge
|
||||
else if (each.item.type === "merge") {
|
||||
pdata = toMerge(each.merge!, pdata);
|
||||
}
|
||||
|
||||
// invalid type
|
||||
else {
|
||||
throw new Error(`invalid enhanced profile type "${each.item.type}"`);
|
||||
}
|
||||
|
||||
console.log("step", pdata);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
const result: CmdType.EnhancedResult = {
|
||||
data: pdata,
|
||||
status: "success",
|
||||
};
|
||||
|
||||
emit(payload.callback, JSON.stringify(result)).catch(console.error);
|
||||
});
|
||||
|
||||
// enhanceProfiles();
|
||||
}
|
||||
|
||||
@@ -124,14 +124,32 @@ export namespace CmdType {
|
||||
system_proxy_bypass?: string;
|
||||
}
|
||||
|
||||
export type ProfileMerge = Record<string, any>;
|
||||
|
||||
// partial of the clash config
|
||||
export type ProfileData = Partial<{
|
||||
rules: any[];
|
||||
proxies: any[];
|
||||
"proxy-groups": any[];
|
||||
"proxy-providers": any[];
|
||||
"rule-providers": any[];
|
||||
}>;
|
||||
|
||||
export interface ChainItem {
|
||||
item: ProfileItem;
|
||||
merge?: object;
|
||||
merge?: ProfileMerge;
|
||||
script?: string;
|
||||
}
|
||||
|
||||
export interface EnhancedPayload {
|
||||
chain: ChainItem[];
|
||||
current: object;
|
||||
current: ProfileData;
|
||||
callback: string;
|
||||
}
|
||||
|
||||
export interface EnhancedResult {
|
||||
data: ProfileData;
|
||||
status: string;
|
||||
error?: string;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user