feat: enhance profile status

This commit is contained in:
GyDi 2022-03-06 17:02:29 +08:00
parent dad94edb20
commit d63d49f246
No known key found for this signature in database
GPG Key ID: 1C95E0D3467B3084
4 changed files with 137 additions and 60 deletions

View File

@ -127,7 +127,7 @@ const ProfileItem = (props: Props) => {
const urlModeMenu = [ const urlModeMenu = [
{ label: "Select", handler: onForceSelect }, { label: "Select", handler: onForceSelect },
{ label: "Edit", handler: onEdit }, { label: "Edit", handler: onEdit },
{ label: "View", handler: onView }, { label: "File", handler: onView },
{ label: "Update", handler: onUpdateWrapper(false) }, { label: "Update", handler: onUpdateWrapper(false) },
{ label: "Update(Proxy)", handler: onUpdateWrapper(true) }, { label: "Update(Proxy)", handler: onUpdateWrapper(true) },
{ label: "Delete", handler: onDelete }, { label: "Delete", handler: onDelete },
@ -135,7 +135,7 @@ const ProfileItem = (props: Props) => {
const fileModeMenu = [ const fileModeMenu = [
{ label: "Select", handler: onForceSelect }, { label: "Select", handler: onForceSelect },
{ label: "Edit", handler: onEdit }, { label: "Edit", handler: onEdit },
{ label: "View", handler: onView }, { label: "File", handler: onView },
{ label: "Delete", handler: onDelete }, { label: "Delete", handler: onDelete },
]; ];

View File

@ -1,5 +1,5 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useState } from "react"; import { useEffect, useState } from "react";
import { import {
alpha, alpha,
Box, Box,
@ -14,6 +14,7 @@ import { viewProfile } from "../../services/cmds";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
import ProfileEdit from "./profile-edit"; import ProfileEdit from "./profile-edit";
import Notice from "../base/base-notice"; import Notice from "../base/base-notice";
import enhance from "../../services/enhance";
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
@ -52,10 +53,17 @@ const ProfileMore = (props: Props) => {
onEnhance, onEnhance,
} = props; } = props;
const { type } = itemData; const { uid, type } = itemData;
const [anchorEl, setAnchorEl] = useState<any>(null); const [anchorEl, setAnchorEl] = useState<any>(null);
const [position, setPosition] = useState({ left: 0, top: 0 }); const [position, setPosition] = useState({ left: 0, top: 0 });
const [editOpen, setEditOpen] = useState(false); const [editOpen, setEditOpen] = useState(false);
const [status, setStatus] = useState(enhance.status(uid));
// unlisten when unmount
useEffect(() => enhance.listen(uid, setStatus), [uid]);
// error during enhanced mode
const hasError = status?.status === "error";
const onEdit = () => { const onEdit = () => {
setAnchorEl(null); setAnchorEl(null);
@ -80,16 +88,16 @@ const ProfileMore = (props: Props) => {
{ label: "Disable", handler: closeWrapper(onDisable) }, { label: "Disable", handler: closeWrapper(onDisable) },
{ label: "Refresh", handler: closeWrapper(onEnhance) }, { label: "Refresh", handler: closeWrapper(onEnhance) },
{ label: "Edit", handler: onEdit }, { label: "Edit", handler: onEdit },
{ label: "View", handler: onView }, { label: "File", handler: onView },
{ label: "To Top", handler: closeWrapper(onMoveTop) }, { label: "To Top", show: !hasError, handler: closeWrapper(onMoveTop) },
{ label: "To End", handler: closeWrapper(onMoveEnd) }, { label: "To End", show: !hasError, handler: closeWrapper(onMoveEnd) },
{ label: "Delete", handler: closeWrapper(onDelete) }, { label: "Delete", handler: closeWrapper(onDelete) },
]; ];
const disableMenu = [ const disableMenu = [
{ label: "Enable", handler: closeWrapper(onEnable) }, { label: "Enable", handler: closeWrapper(onEnable) },
{ label: "Edit", handler: onEdit }, { label: "Edit", handler: onEdit },
{ label: "View", handler: onView }, { label: "File", handler: onView },
{ label: "Delete", handler: closeWrapper(onDelete) }, { label: "Delete", handler: closeWrapper(onDelete) },
]; ];
@ -105,15 +113,20 @@ const ProfileMore = (props: Props) => {
<> <>
<Wrapper <Wrapper
sx={({ palette }) => { sx={({ palette }) => {
const { mode, primary, text, grey } = palette; // todo
// 区分 selected 和 error 和 mode 下各种颜色的排列组合
const { mode, primary, text, grey, error } = palette;
const key = `${mode}-${selected}`; const key = `${mode}-${selected}`;
const bgkey = hasError ? `${mode}-err` : key;
const bgcolor = { const bgcolor = {
"light-true": alpha(primary.main, 0.15), "light-true": alpha(primary.main, 0.15),
"light-false": palette.background.paper, "light-false": palette.background.paper,
"dark-true": alpha(primary.main, 0.35), "dark-true": alpha(primary.main, 0.35),
"dark-false": alpha(grey[700], 0.35), "dark-false": alpha(grey[700], 0.35),
}[key]!; "light-err": alpha(error.main, 0.12),
"dark-err": alpha(error.main, 0.3),
}[bgkey]!;
const color = { const color = {
"light-true": text.secondary, "light-true": text.secondary,
@ -160,13 +173,24 @@ const ProfileMore = (props: Props) => {
</Box> </Box>
<Box sx={boxStyle}> <Box sx={boxStyle}>
<Typography {hasError ? (
noWrap <Typography
title={itemData.desc} noWrap
sx={{ width: "calc(100% - 75px)" }} color="error"
> sx={{ width: "calc(100% - 75px)" }}
{itemData.desc} title={status.message}
</Typography> >
{status.message}
</Typography>
) : (
<Typography
noWrap
title={itemData.desc}
sx={{ width: "calc(100% - 75px)" }}
>
{itemData.desc}
</Typography>
)}
<Typography <Typography
component="span" component="span"
@ -189,15 +213,17 @@ const ProfileMore = (props: Props) => {
e.preventDefault(); e.preventDefault();
}} }}
> >
{(selected ? enableMenu : disableMenu).map((item) => ( {(selected ? enableMenu : disableMenu)
<MenuItem .filter((item: any) => item.show !== false)
key={item.label} .map((item) => (
onClick={item.handler} <MenuItem
sx={{ minWidth: 133 }} key={item.label}
> onClick={item.handler}
{item.label} sx={{ minWidth: 133 }}
</MenuItem> >
))} {item.label}
</MenuItem>
))}
</Menu> </Menu>
{editOpen && ( {editOpen && (

View File

@ -6,9 +6,9 @@ import ReactDOM from "react-dom";
import { RecoilRoot } from "recoil"; import { RecoilRoot } from "recoil";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import Layout from "./pages/_layout"; import Layout from "./pages/_layout";
import setup from "./services/enhance"; import enhance from "./services/enhance";
setup(); enhance.setup();
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>

View File

@ -1,6 +1,9 @@
import { emit, listen } from "@tauri-apps/api/event"; import { emit, listen } from "@tauri-apps/api/event";
import { CmdType } from "./types"; import { CmdType } from "./types";
/**
* process the merge mode
*/
function toMerge( function toMerge(
merge: CmdType.ProfileMerge, merge: CmdType.ProfileMerge,
data: CmdType.ProfileData data: CmdType.ProfileData
@ -42,6 +45,9 @@ function toMerge(
return newData; return newData;
} }
/**
* process the script mode
*/
function toScript( function toScript(
script: string, script: string,
data: CmdType.ProfileData data: CmdType.ProfileData
@ -53,44 +59,89 @@ function toScript(
const paramsName = `__verge${Math.floor(Math.random() * 1000)}`; const paramsName = `__verge${Math.floor(Math.random() * 1000)}`;
const code = `'use strict';${script};return main(${paramsName});`; const code = `'use strict';${script};return main(${paramsName});`;
const func = new Function(paramsName, code); const func = new Function(paramsName, code);
return func(data); // support async main function return func(data);
} }
export default function setup() { export type EStatus = { status: "ok" | "error"; message?: string };
listen("script-handler", async (event) => { export type EListener = (status: EStatus) => void;
const payload = event.payload as CmdType.EnhancedPayload; export type EUnlistener = () => void;
console.log(payload);
let pdata = payload.current || {}; /**
* The service helps to
* implement enhanced profiles
*/
class Enhance {
private isSetup = false;
private listenMap: Map<string, EListener>;
private resultMap: Map<string, EStatus>;
for (const each of payload.chain) { constructor() {
try { this.listenMap = new Map();
// process script this.resultMap = new Map();
if (each.item.type === "script") { }
pdata = await toScript(each.script!, pdata);
// setup some listener
// for the enhanced running status
listen(uid: string, cb: EListener): EUnlistener {
this.listenMap.set(uid, cb);
return () => this.listenMap.delete(uid);
}
// get the running status
status(uid: string): EStatus | undefined {
return this.resultMap.get(uid);
}
// setup the handler
setup() {
if (this.isSetup) return;
this.isSetup = true;
listen("script-handler", async (event) => {
const payload = event.payload as CmdType.EnhancedPayload;
let pdata = payload.current || {};
for (const each of payload.chain) {
const { uid, type = "" } = each.item;
try {
// process script
if (type === "script") {
// support async main function
pdata = await toScript(each.script!, { ...pdata });
}
// process merge
else if (type === "merge") {
pdata = toMerge(each.merge!, { ...pdata });
}
// invalid type
else {
throw new Error(`invalid enhanced profile type "${type}"`);
}
this.exec(uid, { status: "ok" });
} catch (err: any) {
this.exec(uid, {
status: "error",
message: err.message || err.toString(),
});
console.error(err);
} }
// 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 = { const result = { data: pdata, status: "ok" };
data: pdata, emit(payload.callback, JSON.stringify(result)).catch(console.error);
status: "success", });
}; }
emit(payload.callback, JSON.stringify(result)).catch(console.error); // exec the listener
}); private exec(uid: string, status: EStatus) {
this.resultMap.set(uid, status);
this.listenMap.get(uid)?.(status);
}
} }
export default new Enhance();