From d63d49f246494cf707e6f09102604e5c51d937fe Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Sun, 6 Mar 2022 17:02:29 +0800
Subject: [PATCH] feat: enhance profile status

---
 src/components/profile/profile-item.tsx |   4 +-
 src/components/profile/profile-more.tsx |  74 +++++++++++-----
 src/main.tsx                            |   4 +-
 src/services/enhance.ts                 | 113 +++++++++++++++++-------
 4 files changed, 136 insertions(+), 59 deletions(-)

diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx
index af4dc53..ebcf4ec 100644
--- a/src/components/profile/profile-item.tsx
+++ b/src/components/profile/profile-item.tsx
@@ -127,7 +127,7 @@ const ProfileItem = (props: Props) => {
   const urlModeMenu = [
     { label: "Select", handler: onForceSelect },
     { label: "Edit", handler: onEdit },
-    { label: "View", handler: onView },
+    { label: "File", handler: onView },
     { label: "Update", handler: onUpdateWrapper(false) },
     { label: "Update(Proxy)", handler: onUpdateWrapper(true) },
     { label: "Delete", handler: onDelete },
@@ -135,7 +135,7 @@ const ProfileItem = (props: Props) => {
   const fileModeMenu = [
     { label: "Select", handler: onForceSelect },
     { label: "Edit", handler: onEdit },
-    { label: "View", handler: onView },
+    { label: "File", handler: onView },
     { label: "Delete", handler: onDelete },
   ];
 
diff --git a/src/components/profile/profile-more.tsx b/src/components/profile/profile-more.tsx
index 03d6263..3c0b910 100644
--- a/src/components/profile/profile-more.tsx
+++ b/src/components/profile/profile-more.tsx
@@ -1,5 +1,5 @@
 import dayjs from "dayjs";
-import { useState } from "react";
+import { useEffect, useState } from "react";
 import {
   alpha,
   Box,
@@ -14,6 +14,7 @@ import { viewProfile } from "../../services/cmds";
 import relativeTime from "dayjs/plugin/relativeTime";
 import ProfileEdit from "./profile-edit";
 import Notice from "../base/base-notice";
+import enhance from "../../services/enhance";
 
 dayjs.extend(relativeTime);
 
@@ -52,10 +53,17 @@ const ProfileMore = (props: Props) => {
     onEnhance,
   } = props;
 
-  const { type } = itemData;
+  const { uid, type } = itemData;
   const [anchorEl, setAnchorEl] = useState<any>(null);
   const [position, setPosition] = useState({ left: 0, top: 0 });
   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 = () => {
     setAnchorEl(null);
@@ -80,16 +88,16 @@ const ProfileMore = (props: Props) => {
     { label: "Disable", handler: closeWrapper(onDisable) },
     { label: "Refresh", handler: closeWrapper(onEnhance) },
     { label: "Edit", handler: onEdit },
-    { label: "View", handler: onView },
-    { label: "To Top", handler: closeWrapper(onMoveTop) },
-    { label: "To End", handler: closeWrapper(onMoveEnd) },
+    { label: "File", handler: onView },
+    { label: "To Top", show: !hasError, handler: closeWrapper(onMoveTop) },
+    { label: "To End", show: !hasError, handler: closeWrapper(onMoveEnd) },
     { label: "Delete", handler: closeWrapper(onDelete) },
   ];
 
   const disableMenu = [
     { label: "Enable", handler: closeWrapper(onEnable) },
     { label: "Edit", handler: onEdit },
-    { label: "View", handler: onView },
+    { label: "File", handler: onView },
     { label: "Delete", handler: closeWrapper(onDelete) },
   ];
 
@@ -105,15 +113,20 @@ const ProfileMore = (props: Props) => {
     <>
       <Wrapper
         sx={({ palette }) => {
-          const { mode, primary, text, grey } = palette;
+          // todo
+          // 区分 selected 和 error 和 mode 下各种颜色的排列组合
+          const { mode, primary, text, grey, error } = palette;
           const key = `${mode}-${selected}`;
+          const bgkey = hasError ? `${mode}-err` : key;
 
           const bgcolor = {
             "light-true": alpha(primary.main, 0.15),
             "light-false": palette.background.paper,
             "dark-true": alpha(primary.main, 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 = {
             "light-true": text.secondary,
@@ -160,13 +173,24 @@ const ProfileMore = (props: Props) => {
         </Box>
 
         <Box sx={boxStyle}>
-          <Typography
-            noWrap
-            title={itemData.desc}
-            sx={{ width: "calc(100% - 75px)" }}
-          >
-            {itemData.desc}
-          </Typography>
+          {hasError ? (
+            <Typography
+              noWrap
+              color="error"
+              sx={{ width: "calc(100% - 75px)" }}
+              title={status.message}
+            >
+              {status.message}
+            </Typography>
+          ) : (
+            <Typography
+              noWrap
+              title={itemData.desc}
+              sx={{ width: "calc(100% - 75px)" }}
+            >
+              {itemData.desc}
+            </Typography>
+          )}
 
           <Typography
             component="span"
@@ -189,15 +213,17 @@ const ProfileMore = (props: Props) => {
           e.preventDefault();
         }}
       >
-        {(selected ? enableMenu : disableMenu).map((item) => (
-          <MenuItem
-            key={item.label}
-            onClick={item.handler}
-            sx={{ minWidth: 133 }}
-          >
-            {item.label}
-          </MenuItem>
-        ))}
+        {(selected ? enableMenu : disableMenu)
+          .filter((item: any) => item.show !== false)
+          .map((item) => (
+            <MenuItem
+              key={item.label}
+              onClick={item.handler}
+              sx={{ minWidth: 133 }}
+            >
+              {item.label}
+            </MenuItem>
+          ))}
       </Menu>
 
       {editOpen && (
diff --git a/src/main.tsx b/src/main.tsx
index 2c0d0c4..7845c70 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -6,9 +6,9 @@ import ReactDOM from "react-dom";
 import { RecoilRoot } from "recoil";
 import { BrowserRouter } from "react-router-dom";
 import Layout from "./pages/_layout";
-import setup from "./services/enhance";
+import enhance from "./services/enhance";
 
-setup();
+enhance.setup();
 
 ReactDOM.render(
   <React.StrictMode>
diff --git a/src/services/enhance.ts b/src/services/enhance.ts
index ac4b946..d25c6c8 100644
--- a/src/services/enhance.ts
+++ b/src/services/enhance.ts
@@ -1,6 +1,9 @@
 import { emit, listen } from "@tauri-apps/api/event";
 import { CmdType } from "./types";
 
+/**
+ * process the merge mode
+ */
 function toMerge(
   merge: CmdType.ProfileMerge,
   data: CmdType.ProfileData
@@ -42,6 +45,9 @@ function toMerge(
   return newData;
 }
 
+/**
+ * process the script mode
+ */
 function toScript(
   script: string,
   data: CmdType.ProfileData
@@ -53,44 +59,89 @@ function toScript(
   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
+  return func(data);
 }
 
-export default function setup() {
-  listen("script-handler", async (event) => {
-    const payload = event.payload as CmdType.EnhancedPayload;
-    console.log(payload);
-
-    let pdata = payload.current || {};
+export type EStatus = { status: "ok" | "error"; message?: string };
+export type EListener = (status: EStatus) => void;
+export type EUnlistener = () => void;
+
+/**
+ * The service helps to
+ * implement enhanced profiles
+ */
+class Enhance {
+  private isSetup = false;
+  private listenMap: Map<string, EListener>;
+  private resultMap: Map<string, EStatus>;
+
+  constructor() {
+    this.listenMap = new Map();
+    this.resultMap = new Map();
+  }
 
-    for (const each of payload.chain) {
-      try {
-        // process script
-        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);
+  }
 
-        // process merge
-        else if (each.item.type === "merge") {
-          pdata = toMerge(each.merge!, pdata);
-        }
+  // get the running status
+  status(uid: string): EStatus | undefined {
+    return this.resultMap.get(uid);
+  }
 
-        // invalid type
-        else {
-          throw new Error(`invalid enhanced profile type "${each.item.type}"`);
+  // 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);
         }
-
-        console.log("step", pdata);
-      } catch (err) {
-        console.error(err);
       }
-    }
 
-    const result: CmdType.EnhancedResult = {
-      data: pdata,
-      status: "success",
-    };
+      const result = { data: pdata, status: "ok" };
+      emit(payload.callback, JSON.stringify(result)).catch(console.error);
+    });
+  }
 
-    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();
-- 
GitLab