From 66340a27fa66cb2b42e008e5b4836c96ef1e05dd Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Mon, 10 Jan 2022 02:05:35 +0800
Subject: [PATCH] feat: support update checker

---
 src/assets/styles/layout.scss    |  8 ++++
 src/components/setting-verge.tsx |  5 +--
 src/components/update-dialog.tsx | 66 ++++++++++++++++++++++++++++++++
 src/pages/_layout.tsx            | 28 ++++++++++++--
 4 files changed, 101 insertions(+), 6 deletions(-)
 create mode 100644 src/components/update-dialog.tsx

diff --git a/src/assets/styles/layout.scss b/src/assets/styles/layout.scss
index be322bf..509aaba 100644
--- a/src/assets/styles/layout.scss
+++ b/src/assets/styles/layout.scss
@@ -17,6 +17,7 @@
     overflow: hidden;
 
     .the-logo {
+      position: relative;
       flex: 0 1 180px;
       width: 100%;
       max-width: 180px;
@@ -25,6 +26,13 @@
       padding: 0 8px;
       text-align: center;
       box-sizing: border-box;
+
+      .the-newbtn {
+        position: absolute;
+        right: 20px;
+        bottom: 12px;
+        transform: scale(0.8);
+      }
     }
 
     .the-menu {
diff --git a/src/components/setting-verge.tsx b/src/components/setting-verge.tsx
index bc578c7..ea926cd 100644
--- a/src/components/setting-verge.tsx
+++ b/src/components/setting-verge.tsx
@@ -7,15 +7,15 @@ import {
   Typography,
 } from "@mui/material";
 import {
+  setSysProxy,
   getVergeConfig,
   patchVergeConfig,
-  setSysProxy,
 } from "../services/cmds";
 import { CmdType } from "../services/types";
+import { version } from "../../package.json";
 import GuardState from "./guard-state";
 import SettingItem from "./setting-item";
 import PaletteSwitch from "./palette-switch";
-import { version } from "../../package.json";
 
 interface Props {
   onError?: (err: Error) => void;
@@ -32,7 +32,6 @@ const SettingVerge = ({ onError }: Props) => {
   } = vergeConfig ?? {};
 
   const onSwitchFormat = (_e: any, value: boolean) => value;
-
   const onChangeData = (patch: Partial<CmdType.VergeConfig>) => {
     mutate("getVergeConfig", { ...vergeConfig, ...patch }, false);
   };
diff --git a/src/components/update-dialog.tsx b/src/components/update-dialog.tsx
new file mode 100644
index 0000000..1881148
--- /dev/null
+++ b/src/components/update-dialog.tsx
@@ -0,0 +1,66 @@
+import useSWR from "swr";
+import { useState } from "react";
+import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
+import { relaunch } from "@tauri-apps/api/process";
+import {
+  Button,
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogContentText,
+  DialogTitle,
+} from "@mui/material";
+
+interface Props {
+  open: boolean;
+  onClose: () => void;
+}
+
+let uploadingState = false;
+
+const UpdateDialog = (props: Props) => {
+  const { open, onClose } = props;
+  const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, {
+    errorRetryCount: 2,
+    revalidateIfStale: false,
+    focusThrottleInterval: 36e5, // 1 hour
+  });
+  const [uploading, setUploading] = useState(uploadingState);
+
+  const onUpdate = async () => {
+    try {
+      setUploading(true);
+      uploadingState = true;
+      await installUpdate();
+      await relaunch();
+    } catch (error) {
+      console.log(error);
+      window.alert("Failed to upload, please try again.");
+    } finally {
+      setUploading(true);
+      uploadingState = true;
+    }
+  };
+
+  return (
+    <Dialog open={open} onClose={onClose}>
+      <DialogTitle>New Version v{updateInfo?.manifest?.version}</DialogTitle>
+      <DialogContent sx={{ minWidth: 360, maxWidth: 400, maxHeight: "50vh" }}>
+        <DialogContentText>{updateInfo?.manifest?.body}</DialogContentText>
+      </DialogContent>
+      <DialogActions>
+        <Button onClick={onClose}>Cancel</Button>
+        <Button
+          variant="contained"
+          autoFocus
+          onClick={onUpdate}
+          disabled={uploading}
+        >
+          Update
+        </Button>
+      </DialogActions>
+    </Dialog>
+  );
+};
+
+export default UpdateDialog;
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
index adf7159..f5ea019 100644
--- a/src/pages/_layout.tsx
+++ b/src/pages/_layout.tsx
@@ -1,8 +1,9 @@
-import { useEffect, useMemo } from "react";
+import { useEffect, useMemo, useState } from "react";
 import useSWR, { SWRConfig } from "swr";
 import { Route, Routes } from "react-router-dom";
 import { useRecoilState } from "recoil";
 import {
+  Button,
   createTheme,
   IconButton,
   List,
@@ -10,6 +11,7 @@ import {
   ThemeProvider,
 } from "@mui/material";
 import { HorizontalRuleRounded, CloseRounded } from "@mui/icons-material";
+import { checkUpdate } from "@tauri-apps/api/updater";
 import { atomPaletteMode } from "../states/setting";
 import { getVergeConfig, windowDrag, windowHide } from "../services/cmds";
 import LogoSvg from "../assets/image/logo.svg";
@@ -20,6 +22,7 @@ import SettingPage from "./setting";
 import ConnectionsPage from "./connections";
 import LayoutItem from "../components/layout-item";
 import Traffic from "../components/traffic";
+import UpdateDialog from "../components/update-dialog";
 
 const routers = [
   {
@@ -52,6 +55,12 @@ const routers = [
 const Layout = () => {
   const [mode, setMode] = useRecoilState(atomPaletteMode);
   const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
+  const { data: updateInfo } = useSWR("checkUpdate", checkUpdate, {
+    errorRetryCount: 2,
+    revalidateIfStale: false,
+    focusThrottleInterval: 36e5, // 1 hour
+  });
+  const [dialogOpen, setDialogOpen] = useState(false);
 
   useEffect(() => {
     setMode(vergeConfig?.theme_mode ?? "light");
@@ -92,6 +101,18 @@ const Layout = () => {
                   e.preventDefault();
                 }}
               />
+
+              {updateInfo?.shouldUpdate && (
+                <Button
+                  color="error"
+                  variant="contained"
+                  size="small"
+                  className="the-newbtn"
+                  onClick={() => setDialogOpen(true)}
+                >
+                  New
+                </Button>
+              )}
             </div>
 
             <List className="the-menu">
@@ -126,13 +147,14 @@ const Layout = () => {
 
             <div className="the-content">
               <Routes>
-                {routers.map(({ link, ele: Ele }) => (
-                  <Route path={link} element={<Ele />} />
+                {routers.map(({ label, link, ele: Ele }) => (
+                  <Route key={label} path={link} element={<Ele />} />
                 ))}
               </Routes>
             </div>
           </div>
         </Paper>
+        <UpdateDialog open={dialogOpen} onClose={() => setDialogOpen(false)} />
       </ThemeProvider>
     </SWRConfig>
   );
-- 
GitLab