From 07f3854019afcb82ea0722f1b99161d71001f108 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=8D=89=E9=9E=8B=E6=B2=A1=E5=8F=B7?= <308487730@qq.com>
Date: Sun, 5 Mar 2023 10:39:45 +0800
Subject: [PATCH] refactor: cleanup
---
electron/main/index.ts | 4 +-
electron/main/update.ts | 70 ++++++
electron/preload/update.ts | 95 --------
package.json | 6 +-
src/App.tsx | 4 +-
src/components/update/Modal/index.tsx | 117 +++++-----
src/components/update/Modal/modal.module.scss | 138 +++++++-----
src/components/update/Modal/type.d.ts | 20 --
src/components/update/Progress/index.tsx | 34 +--
.../update/Progress/progress.module.scss | 5 +-
src/components/update/Progress/type.d.ts | 5 -
src/components/update/electron-updater.d.ts | 10 +
src/components/update/index.tsx | 208 +++++++++---------
src/components/update/type.d.ts | 18 --
src/components/update/update.module.scss | 25 ++-
15 files changed, 374 insertions(+), 385 deletions(-)
create mode 100644 electron/main/update.ts
delete mode 100644 electron/preload/update.ts
delete mode 100644 src/components/update/Modal/type.d.ts
delete mode 100644 src/components/update/Progress/type.d.ts
create mode 100644 src/components/update/electron-updater.d.ts
delete mode 100644 src/components/update/type.d.ts
diff --git a/electron/main/index.ts b/electron/main/index.ts
index a4540b7..0deb058 100644
--- a/electron/main/index.ts
+++ b/electron/main/index.ts
@@ -1,7 +1,7 @@
import { app, BrowserWindow, shell, ipcMain } from 'electron'
import { release } from 'node:os'
import { join } from 'node:path'
-import { update } from '../preload/update'
+import { update } from './update'
// The built directory structure
//
@@ -73,6 +73,8 @@ async function createWindow() {
if (url.startsWith('https:')) shell.openExternal(url)
return { action: 'deny' }
})
+
+ // Apply electron-updater
update(win)
}
diff --git a/electron/main/update.ts b/electron/main/update.ts
new file mode 100644
index 0000000..d080ff1
--- /dev/null
+++ b/electron/main/update.ts
@@ -0,0 +1,70 @@
+import { app, ipcMain } from 'electron'
+import {
+ type ProgressInfo,
+ type UpdateDownloadedEvent,
+ autoUpdater
+} from 'electron-updater'
+
+export function update(win: Electron.BrowserWindow) {
+
+ // When set to false, the update download will be triggered through the API
+ autoUpdater.autoDownload = false
+
+ autoUpdater.disableWebInstaller = false
+
+ autoUpdater.allowDowngrade = false
+
+ // start check
+ autoUpdater.on('checking-for-update', function () { })
+ // update available
+ autoUpdater.on('update-available', (arg) => {
+ win.webContents.send('update-can-available', { update: true, version: app.getVersion(), newVersion: arg?.version })
+ })
+ // update not available
+ autoUpdater.on('update-not-available', (arg) => {
+ win.webContents.send('update-can-available', { update: false, version: app.getVersion(), newVersion: arg?.version })
+ })
+
+ // Checking for updates
+ ipcMain.handle('check-update', async () => {
+ try {
+ return await autoUpdater.checkForUpdatesAndNotify()
+ } catch (error) {
+ return { message: 'Network error', error }
+ }
+ })
+
+ // Start downloading and feedback on progress
+ ipcMain.handle('start-download', (event) => {
+ startDownload(
+ (error, progressInfo) => {
+ if (error) {
+ // feedback download error message
+ event.sender.send('update-error', { message: error.message, error })
+ } else {
+ // feedback update progress message
+ event.sender.send('download-progress', progressInfo)
+ }
+ },
+ () => {
+ // feedback update downloaded message
+ event.sender.send('update-downloaded')
+ }
+ )
+ })
+
+ // Install now
+ ipcMain.handle('quit-and-install', () => {
+ autoUpdater.quitAndInstall(false, true)
+ })
+}
+
+function startDownload(
+ callback: (error: Error | null, info: ProgressInfo) => void,
+ complete: (evnet: UpdateDownloadedEvent) => void,
+) {
+ autoUpdater.on('download-progress', info => callback(null, info))
+ autoUpdater.on('error', error => callback(error, null))
+ autoUpdater.on('update-downloaded', complete)
+ autoUpdater.downloadUpdate()
+}
diff --git a/electron/preload/update.ts b/electron/preload/update.ts
deleted file mode 100644
index 76b0a05..0000000
--- a/electron/preload/update.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { autoUpdater } from "electron-updater"
-import { app, ipcMain } from "electron";
-export const update = (win: Electron.CrossProcessExports.BrowserWindow) => {
-
- // When set to false, the update download will be triggered through the API
- autoUpdater.autoDownload = false;
-
- autoUpdater.disableWebInstaller = false
-
- autoUpdater.allowDowngrade = false;
-
- // Save the version status of whether the update needs to be installed,
- // Because the user needs to update immediately and later after the update is downloaded
- let NEED_INSTALL = false;
-
- // Check whether update is used
- ipcMain.on('check-update',()=>{
- autoUpdater.checkForUpdatesAndNotify()
- .then((res) => {
- win.webContents.send('check-update-type',{ checkUpdate: true})
- }).catch(err => {
- // network error
- win.webContents.send('check-update-type', { checkUpdate: false})
- });
- })
-
- // start check
- autoUpdater.on('checking-for-update', function () {
- console.log('checking-for-update')
- })
- // update available
- autoUpdater.on('update-available', (arg) => {
- console.log('update-available')
- win.webContents.send('is-update-available', { isUpdate: true, oldVersion: app.getVersion(), newVersion: arg?.version })
- })
- // update not available
- autoUpdater.on('update-not-available', (arg) => {
- console.log('update-not-available')
- win.webContents.send('is-update-available', { isUpdate: false, oldVersion: app.getVersion(), newVersion: arg?.version })
- })
-
- const startDownload = (callback: any, successCallback: any) => {
- // Monitor the download progress and push it to the update window
- autoUpdater.on('download-progress', (data) => {
- console.log("progress", data)
- win.webContents.send('download-progress-data', data)
- callback && callback instanceof Function && callback(null, data);
- });
- // Listen for download errors and push to the update window
- autoUpdater.on('error', (err) => {
- callback && callback instanceof Function && callback(err);
- });
- // Listen to the download completion and push it to the update window
- autoUpdater.on('update-downloaded', () => {
- NEED_INSTALL = true;
- successCallback && successCallback instanceof Function && successCallback();
- });
-
- autoUpdater.downloadUpdate();
- };
-
- // Listen to the process message sent by the application layer and start downloading updates
- ipcMain.on('start-download', (event) => {
- console.log("start")
- startDownload(
- (err: any, progressInfo: { percent: any; }) => {
- if (err) {
- // callback download error message
- event.sender.send('update-error', { updateError:true});
- } else {
- // callback update progress message
- event.sender.send('update-progress', { progressInfo: progressInfo.percent });
- }
- },
- () => {
- // callback update downed message
- event.sender.send('update-downed');
- }
- );
- });
-
- // install now
- ipcMain.on('quit-and-install', () => {
- autoUpdater.quitAndInstall(false, true);
- })
-
- // install later
- app.on('will-quit', () => {
- console.log("NEED_INSTALL=true")
- if (NEED_INSTALL) {
- autoUpdater.quitAndInstall(true, false);
- }
- });
-
-}
\ No newline at end of file
diff --git a/package.json b/package.json
index a373bcc..7d217c8 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,9 @@
"pree2e": "vite build --mode=test",
"e2e": "playwright test"
},
+ "dependencies": {
+ "electron-updater": "^5.3.0"
+ },
"devDependencies": {
"@playwright/test": "^1.31.0",
"@types/react": "^18.0.28",
@@ -36,8 +39,5 @@
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
- },
- "dependencies": {
- "electron-updater": "^5.3.0"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index c4c091d..6566091 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,11 +1,10 @@
import nodeLogo from './assets/node.svg'
import { useState } from 'react'
-import './App.scss'
import Update from '@/components/update'
+import './App.scss'
console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
-
function App() {
const [count, setCount] = useState(0)
return (
@@ -30,6 +29,7 @@ function App() {
Place static files into the
/public
folder
+
)
diff --git a/src/components/update/Modal/index.tsx b/src/components/update/Modal/index.tsx
index 7ecce0b..c8d6922 100644
--- a/src/components/update/Modal/index.tsx
+++ b/src/components/update/Modal/index.tsx
@@ -1,62 +1,67 @@
-import { createPortal } from 'react-dom';
-import { ModalChildType, ModalPropsType } from './type';
-import modalScss from './modal.module.scss'
-const ModalTemplate = (child: ModalChildType) => {
- return (
-
-
-
-
- {child.isHeaderShow ? (
-
-
{child.titleText}
-
-
- ) : null}
+import React, { ReactNode } from 'react'
+import { createPortal } from 'react-dom'
+import styles from './modal.module.scss'
-
{child.body}
- {child.isFooterShow ? (
+const ModalTemplate: React.FC
void
+ onOk?: () => void
+ width?: number
+}>> = props => {
+ const {
+ title,
+ children,
+ footer,
+ cancelText = 'Cancel',
+ okText = 'OK',
+ onCancel,
+ onOk,
+ width = 530,
+ } = props
+
+ return (
+
+
+
+
+
+
{children}
+ {typeof footer !== 'undefined' ? (
- {(child.isSubmitShow ?? true) ? : null}
- {(child.isCanCelShow ?? true) ? : null}
+
+
- ) : null}
-
+ ) : footer}
+
- );
-};
+ )
+}
-const Modal = (props: ModalPropsType) => {
- return createPortal(
- props.isOpenModal?
- ModalTemplate({
- titleText: props.titleText,
- isHeaderShow: props.isHeaderShow ?? true,
- isFooterShow: props.isFooterShow ?? true,
- isCanCelShow: props.isCanCelShow ?? true,
- isSubmitShow: props.isSubmitShow ?? true,
- body: props.children,
- submitText: props.submitText,
- canCelText: props.canCelText,
- onCanCel: props.onCanCel,
- onSubmit: props.onSubmit,
- }):
,
- document.body,
- );
-};
-export default Modal;
+const Modal = (props: Parameters
[0] & { open: boolean }) => {
+ const { open, ...omit } = props
+
+ return createPortal(
+ open ? ModalTemplate(omit) : null,
+ document.body,
+ )
+}
+
+export default Modal
diff --git a/src/components/update/Modal/modal.module.scss b/src/components/update/Modal/modal.module.scss
index a9536de..7218815 100644
--- a/src/components/update/Modal/modal.module.scss
+++ b/src/components/update/Modal/modal.module.scss
@@ -1,63 +1,89 @@
-.modal{
- :global{
- .modal-bg {
- width: 100vw;
- height: 100vh;
- position: fixed;
- left: 0;
- top: 0;
- z-index: 9999;
- background: rgba(0, 0, 0, 0.3);
- }
-
- .modal-outboard {
- position: absolute;
- top: 20vh;
- left: 30vw;
- z-index: 10000;
- }
-
- .modal-panel {
- border: 1px solid #000000;
- border-radius: 5px;
-
- .modal-header {
- $titleheight: 38px;
- width: 530px;
- height: $titleheight;
- line-height: $titleheight;
- background-color: rgb(99, 153, 255);
- display: flex;
-
- .modal-header-text {
- text-align: center;
- width: 480px;
- }
+.modal {
+ --primary-color: rgb(224, 30, 90);
+
+ :global {
+ .modal-mask {
+ width: 100vw;
+ height: 100vh;
+ position: fixed;
+ left: 0;
+ top: 0;
+ z-index: 9;
+ background: rgba(0, 0, 0, 0.45);
+ }
+
+ .modal-warp {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 19;
+ }
+
+ .modal-content {
+ box-shadow: 0 0 10px -4px rgb(130, 86, 208);
+ overflow: hidden;
+ border-radius: 4px;
+
+ .modal-header {
+ display: flex;
+ line-height: 38px;
+ background-color: var(--primary-color);
+
+ .modal-header-text {
+ font-weight: bold;
+ width: 0;
+ flex-grow: 1;
}
-
- .modal-body {
- background-color: #ffffff;
+ }
+
+ .modal-close {
+ width: 30px;
+ height: 30px;
+ margin: 4px;
+ line-height: 34px;
+ text-align: center;
+ cursor: pointer;
+
+ svg {
+ width: 17px;
+ height: 17px;
}
-
- .modal-footer {
- background-color: #ffffff;
- display: flex;
- justify-content: end;
-
- button {
- margin: 10px;
+ }
+
+ .modal-body {
+ padding: 10px;
+ background-color: #fff;
+ color: #333;
+ }
+
+ .modal-footer {
+ padding: 10px;
+ background-color: #fff;
+ display: flex;
+ justify-content: end;
+
+ button {
+ padding: 7px 11px;
+ background-color: var(--primary-color);
+ font-size: 14px;
+ margin-left: 10px;
+
+ &:first-child {
+ margin-left: 0;
}
}
}
-
- .icon {
- padding: 0 15px;
- width: 20px;
- fill: currentColor;
-
- &:hover {
- color: rgba(0, 0, 0, 0.4);
- }
+ }
+
+ .icon {
+ padding: 0 15px;
+ width: 20px;
+ fill: currentColor;
+
+ &:hover {
+ color: rgba(0, 0, 0, 0.4);
}
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/components/update/Modal/type.d.ts b/src/components/update/Modal/type.d.ts
deleted file mode 100644
index 2ba43c3..0000000
--- a/src/components/update/Modal/type.d.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { ReactNode } from 'react'
-interface childrens {
- titleText?: string
- isHeaderShow?: boolean
- isFooterShow?: boolean
- isCanCelShow?: boolean
- isSubmitShow?: boolean
- canCelText?: string
- submitText?: string
- onSubmit?: () => void
- onCanCel?: () => void
-}
-export interface ModalChildType extends childrens {
- body: ReactNode | null
-}
-
-export interface ModalPropsType extends childrens {
- isOpenModal: boolean
- children: ReactNode | null
-}
diff --git a/src/components/update/Progress/index.tsx b/src/components/update/Progress/index.tsx
index bb82162..06d5ed0 100644
--- a/src/components/update/Progress/index.tsx
+++ b/src/components/update/Progress/index.tsx
@@ -1,22 +1,22 @@
-import { RsProgressType } from './type'
-import progressScss from './progress.module.scss'
+import React from 'react'
+import styles from './progress.module.scss'
-const Progress = (props: RsProgressType) => {
+const Progress: React.FC> = props => {
+ const { percent = 0 } = props
return (
-
-
-
+
-
{props.percent > 100 ? 100 :(props.percent.toString().substring(0,4) ?? 0) }%
-
- );
-};
+ )
+}
-export default Progress;
+export default Progress
diff --git a/src/components/update/Progress/progress.module.scss b/src/components/update/Progress/progress.module.scss
index ff84486..1a66779 100644
--- a/src/components/update/Progress/progress.module.scss
+++ b/src/components/update/Progress/progress.module.scss
@@ -4,7 +4,7 @@
:global {
.progress-pr {
- border: 1px solid #000000;
+ border: 1px solid #000;
border-radius: 3px;
height: 6px;
}
@@ -12,10 +12,11 @@
.progress-rate {
height: 6px;
border-radius: 3px;
+ background-image: linear-gradient(to right, rgb(130, 86, 208) 0%, var(--primary-color) 100%)
}
.progress-num {
margin: 0 10px;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/components/update/Progress/type.d.ts b/src/components/update/Progress/type.d.ts
deleted file mode 100644
index 24a0970..0000000
--- a/src/components/update/Progress/type.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface RsProgressType {
- rateColor?: string
- rateWidth?: number
- percent: number
-}
diff --git a/src/components/update/electron-updater.d.ts b/src/components/update/electron-updater.d.ts
new file mode 100644
index 0000000..5b26aba
--- /dev/null
+++ b/src/components/update/electron-updater.d.ts
@@ -0,0 +1,10 @@
+interface VersionInfo {
+ update: boolean
+ version: string
+ newVersion?: string
+}
+
+interface ErrorType {
+ message: string
+ error: Error
+}
diff --git a/src/components/update/index.tsx b/src/components/update/index.tsx
index 3eb4fd0..796ccaf 100644
--- a/src/components/update/index.tsx
+++ b/src/components/update/index.tsx
@@ -1,123 +1,135 @@
+import { ipcRenderer } from 'electron'
+import type { ProgressInfo } from 'electron-updater'
+import { useCallback, useEffect, useState } from 'react'
import Modal from '@/components/update/Modal'
import Progress from '@/components/update/Progress'
-import { ipcRenderer } from 'electron'
-import { useEffect, useState } from 'react'
-import updateScss from './update.module.scss'
-import { checkUpdateType, isUpdateAvailable, ModalBtnText, VersionInfo } from './type'
-
-
-let onModalSubmit = () => { }
-let onModalCanCel = () => { }
+import styles from './update.module.scss'
const Update = () => {
- const [checkBtnText, setCheckBtnText] = useState('check update')
- const [checkType, setCheckType] = useState(false)
- const [checkLoading, setCheckLoading] = useState(false)
- const [isOpenModal, setIsOpenModal] = useState
(false)
- const [percentNum, setPercentNum] = useState(0)
- const [isNeedUpdate, setIsNeedUpdate] = useState(false)
- const [updateError, setUpdateError] = useState(false)
- const [versionInfo, setVersionInfo] = useState({
- oldVersion: '',
- newVersion: ''
- })
- const [modalBtnText, setModalBtnText] = useState({
- canCelText: '',
- submitText: ''
+ const [checking, setChecking] = useState(false)
+ const [updateAvailable, setUpdateAvailable] = useState(false)
+ const [versionInfo, setVersionInfo] = useState()
+ const [updateError, setUpdateError] = useState()
+ const [progressInfo, setProgressInfo] = useState()
+ const [modalOpen, setModalOpen] = useState(false)
+ const [modalBtn, setModalBtn] = useState<{
+ cancelText?: string
+ okText?: string
+ onCancel?: () => void
+ onOk?: () => void
+ }>({
+ onCancel: () => setModalOpen(false),
+ onOk: () => ipcRenderer.invoke('start-download'),
})
- useEffect(() => {
- onModalCanCel = () => setIsOpenModal(false)
- }, [])
+ const checkUpdate = async () => {
+ setChecking(true)
+ /**
+ * @type {import('electron-updater').UpdateCheckResult | null | { message: string, error: Error }}
+ */
+ const result = await ipcRenderer.invoke('check-update')
+ setChecking(false)
- // Check for updates
- const checkUpdate = () => {
- setCheckLoading(true)
- setCheckBtnText('checking Update ...')
- ipcRenderer.send('check-update')
+ if (result?.error) {
+ console.error(result.error)
+ setUpdateAvailable(false)
+ } else {
+ setUpdateAvailable(true)
+ setModalOpen(true)
+ }
}
- // Listen to get the check result
- ipcRenderer.on('check-update-type', (_event, ...args: checkUpdateType[]) => {
- setCheckLoading(false)
- setCheckBtnText('check update')
- setCheckType(args[0].checkUpdate)
- setIsOpenModal(true)
- })
-
- // Get version information and whether to update
- ipcRenderer.on('is-update-available', (_event, ...args: isUpdateAvailable[]) => {
- setVersionInfo({
- oldVersion: args[0].oldVersion,
- newVersion: args[0].newVersion,
- })
- setIsNeedUpdate(args[0].isUpdate)
- // Update required
- if (args[0].isUpdate) {
- setModalBtnText({
- canCelText: 'cancel',
- submitText: 'update'
- })
- onModalSubmit = () => ipcRenderer.send('start-download')
- onModalCanCel = () => setIsOpenModal(false)
+ const onUpdateCanAvailable = useCallback((_event: Electron.IpcRendererEvent, arg1: VersionInfo) => {
+ setVersionInfo(arg1)
+ // Can be update
+ if (arg1.update) {
+ setModalBtn(state => ({
+ ...state,
+ cancelText: 'Cancel',
+ okText: 'Update',
+ onCancel: () => setModalOpen(false),
+ onOk: () => ipcRenderer.invoke('start-download'),
+ }))
}
- })
+ }, [])
- // Throw the update failure message when the update fails
- ipcRenderer.on('update-error', (_event, ...args: { updateError: boolean }[]) => {
- setUpdateError(args[0].updateError)
- setCheckType(false)
- })
+ const onUpdateError = useCallback((_event: Electron.IpcRendererEvent, arg1: ErrorType) => {
+ console.error(arg1.error)
+ setUpdateError(arg1)
+ }, [])
- // Get update progress
- ipcRenderer.on('update-progress', (_event, ...args: { progressInfo: number }[]) => {
- setPercentNum(args[0].progressInfo)
- })
+ const onDownloadProgress = useCallback((_event: Electron.IpcRendererEvent, arg1: ProgressInfo) => {
+ console.log(arg1)
+ setProgressInfo(arg1)
+ }, [])
- // is update been completed
- ipcRenderer.on('update-downed', (_event, ...args) => {
- setPercentNum(100)
- setModalBtnText({
- canCelText: 'install later',
- submitText: 'install now'
- })
- onModalSubmit = () => ipcRenderer.send('quit-and-install')
- onModalCanCel = () => {
- ipcRenderer.send('will-quit')
- setIsOpenModal(false)
+ const onUpdateDownloaded = useCallback((_event: Electron.IpcRendererEvent, ...args: any[]) => {
+ setModalBtn(state => ({
+ ...state,
+ cancelText: 'Later',
+ okText: 'Install now',
+ onOk: () => ipcRenderer.invoke('quit-and-install'),
+ }))
+ }, [])
+
+ useEffect(() => {
+ // Get version information and whether to update
+ ipcRenderer.on('update-can-available', onUpdateCanAvailable)
+ ipcRenderer.on('update-error', onUpdateError)
+ ipcRenderer.on('download-progress', onDownloadProgress)
+ ipcRenderer.on('update-downloaded', onUpdateDownloaded)
+
+ return () => {
+ ipcRenderer.off('update-can-available', onUpdateCanAvailable)
+ ipcRenderer.off('update-error', onUpdateError)
+ ipcRenderer.off('download-progress', onDownloadProgress)
+ ipcRenderer.off('update-downloaded', onUpdateDownloaded)
}
- })
+ }, [])
return (
<>
-
-
- {updateError ?
-
Error downloading the latest version, please contact the developer
:
- checkType ? (
- isNeedUpdate ? (
-
-
-
oldVersion : v.{versionInfo.oldVersion}
-
newVersion : v.{versionInfo.newVersion}
+
+
+ {updateError ? (
+
+
Error downloading the latest version.
+
{updateError.message}
+
+ ) : null}
+ {!updateAvailable ? (
+
+ Check update is Error, Please check your Network!
+
+ ) : null}
+ {versionInfo
+ ? (
+
+
The last version is: v{versionInfo.newVersion}
+
v{versionInfo.version} -> v{versionInfo.newVersion}
+
)
- :
This is last version : v.{versionInfo.oldVersion} !
- ) :
Check update is Error,Please check your network!
- }
+
+
+ )
+ : Checking...}
-