refactor: cleanup
This commit is contained in:
parent
1f8b17810a
commit
07f3854019
|
@ -1,7 +1,7 @@
|
||||||
import { app, BrowserWindow, shell, ipcMain } from 'electron'
|
import { app, BrowserWindow, shell, ipcMain } from 'electron'
|
||||||
import { release } from 'node:os'
|
import { release } from 'node:os'
|
||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import { update } from '../preload/update'
|
import { update } from './update'
|
||||||
|
|
||||||
// The built directory structure
|
// The built directory structure
|
||||||
//
|
//
|
||||||
|
@ -73,6 +73,8 @@ async function createWindow() {
|
||||||
if (url.startsWith('https:')) shell.openExternal(url)
|
if (url.startsWith('https:')) shell.openExternal(url)
|
||||||
return { action: 'deny' }
|
return { action: 'deny' }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Apply electron-updater
|
||||||
update(win)
|
update(win)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,6 +18,9 @@
|
||||||
"pree2e": "vite build --mode=test",
|
"pree2e": "vite build --mode=test",
|
||||||
"e2e": "playwright test"
|
"e2e": "playwright test"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"electron-updater": "^5.3.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.31.0",
|
"@playwright/test": "^1.31.0",
|
||||||
"@types/react": "^18.0.28",
|
"@types/react": "^18.0.28",
|
||||||
|
@ -36,8 +39,5 @@
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.0.0"
|
"node": "^14.18.0 || >=16.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"electron-updater": "^5.3.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import nodeLogo from './assets/node.svg'
|
import nodeLogo from './assets/node.svg'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import './App.scss'
|
|
||||||
import Update from '@/components/update'
|
import Update from '@/components/update'
|
||||||
|
import './App.scss'
|
||||||
|
|
||||||
console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
|
console.log('[App.tsx]', `Hello world from Electron ${process.versions.electron}!`)
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [count, setCount] = useState(0)
|
const [count, setCount] = useState(0)
|
||||||
return (
|
return (
|
||||||
|
@ -30,6 +29,7 @@ function App() {
|
||||||
<div className='flex-center'>
|
<div className='flex-center'>
|
||||||
Place static files into the<code>/public</code> folder <img style={{ width: '5em' }} src={nodeLogo} alt='Node logo' />
|
Place static files into the<code>/public</code> folder <img style={{ width: '5em' }} src={nodeLogo} alt='Node logo' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Update />
|
<Update />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,62 +1,67 @@
|
||||||
import { createPortal } from 'react-dom';
|
import React, { ReactNode } from 'react'
|
||||||
import { ModalChildType, ModalPropsType } from './type';
|
import { createPortal } from 'react-dom'
|
||||||
import modalScss from './modal.module.scss'
|
import styles from './modal.module.scss'
|
||||||
const ModalTemplate = (child: ModalChildType) => {
|
|
||||||
return (
|
|
||||||
<div className={modalScss.modal}>
|
|
||||||
<div className='modal-bg' onClick={child.onCanCel} />
|
|
||||||
<div className='modal-outboard'>
|
|
||||||
<div className='modal-panel'>
|
|
||||||
{child.isHeaderShow ? (
|
|
||||||
<div className='modal-header'>
|
|
||||||
<div className='modal-header-text'>{child.titleText}</div>
|
|
||||||
<svg
|
|
||||||
onClick={child.onCanCel}
|
|
||||||
className='icon'
|
|
||||||
viewBox='0 0 1026 1024'
|
|
||||||
version='1.1'
|
|
||||||
xmlns='http://www.w3.org/2000/svg'
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d='M585.781589 510.748226l423.38657-423.38657A51.071963
|
|
||||||
51.071963 0 0 0 937.156692 14.839469L513.770122 438.736759
|
|
||||||
90.383552 14.839469A51.071963 51.071963 0 0 0 17.861365 87.361656L441.758655
|
|
||||||
510.748226l-423.89729 423.38657A51.071963 51.071963 0 1 0 89.872832 1006.146263l423.89729-423.38657
|
|
||||||
423.38657 423.38657a51.071963 51.071963 0 0 0 72.011467-72.011467z'
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<div className='modal-body'>{child.body}</div>
|
const ModalTemplate: React.FC<React.PropsWithChildren<{
|
||||||
{child.isFooterShow ? (
|
title?: ReactNode
|
||||||
|
footer?: ReactNode
|
||||||
|
cancelText?: string
|
||||||
|
okText?: string
|
||||||
|
onCancel?: () => void
|
||||||
|
onOk?: () => void
|
||||||
|
width?: number
|
||||||
|
}>> = props => {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
footer,
|
||||||
|
cancelText = 'Cancel',
|
||||||
|
okText = 'OK',
|
||||||
|
onCancel,
|
||||||
|
onOk,
|
||||||
|
width = 530,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.modal}>
|
||||||
|
<div className='modal-mask' />
|
||||||
|
<div className='modal-warp'>
|
||||||
|
<div className='modal-content' style={{ width }}>
|
||||||
|
<div className='modal-header'>
|
||||||
|
<div className='modal-header-text'>{title}</div>
|
||||||
|
<span
|
||||||
|
className='modal-close'
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M557.312 513.248l265.28-263.904c12.544-12.48 12.608-32.704 0.128-45.248-12.512-12.576-32.704-12.608-45.248-0.128l-265.344 263.936-263.04-263.84C236.64 191.584 216.384 191.52 203.84 204 191.328 216.48 191.296 236.736 203.776 249.28l262.976 263.776L201.6 776.8c-12.544 12.48-12.608 32.704-0.128 45.248 6.24 6.272 14.464 9.44 22.688 9.44 8.16 0 16.32-3.104 22.56-9.312l265.216-263.808 265.44 266.24c6.24 6.272 14.432 9.408 22.656 9.408 8.192 0 16.352-3.136 22.592-9.344 12.512-12.48 12.544-32.704 0.064-45.248L557.312 513.248z" p-id="2764" fill="currentColor">
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className='modal-body'>{children}</div>
|
||||||
|
{typeof footer !== 'undefined' ? (
|
||||||
<div className='modal-footer'>
|
<div className='modal-footer'>
|
||||||
{(child.isSubmitShow ?? true) ?<button onClick={child.onSubmit}>{child.submitText ?? '确认'}</button> : null}
|
<button onClick={onCancel}>{cancelText}</button>
|
||||||
{(child.isCanCelShow ?? true) ? <button onClick={child.onCanCel}>{child.canCelText ?? '取消'}</button> : null}
|
<button onClick={onOk}>{okText}</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : footer}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const Modal = (props: ModalPropsType) => {
|
const Modal = (props: Parameters<typeof ModalTemplate>[0] & { open: boolean }) => {
|
||||||
return createPortal(
|
const { open, ...omit } = props
|
||||||
props.isOpenModal?
|
|
||||||
ModalTemplate({
|
return createPortal(
|
||||||
titleText: props.titleText,
|
open ? ModalTemplate(omit) : null,
|
||||||
isHeaderShow: props.isHeaderShow ?? true,
|
document.body,
|
||||||
isFooterShow: props.isFooterShow ?? true,
|
)
|
||||||
isCanCelShow: props.isCanCelShow ?? true,
|
}
|
||||||
isSubmitShow: props.isSubmitShow ?? true,
|
|
||||||
body: props.children,
|
export default Modal
|
||||||
submitText: props.submitText,
|
|
||||||
canCelText: props.canCelText,
|
|
||||||
onCanCel: props.onCanCel,
|
|
||||||
onSubmit: props.onSubmit,
|
|
||||||
}): <div></div>,
|
|
||||||
document.body,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Modal;
|
|
||||||
|
|
|
@ -1,63 +1,89 @@
|
||||||
.modal{
|
.modal {
|
||||||
:global{
|
--primary-color: rgb(224, 30, 90);
|
||||||
.modal-bg {
|
|
||||||
width: 100vw;
|
:global {
|
||||||
height: 100vh;
|
.modal-mask {
|
||||||
position: fixed;
|
width: 100vw;
|
||||||
left: 0;
|
height: 100vh;
|
||||||
top: 0;
|
position: fixed;
|
||||||
z-index: 9999;
|
left: 0;
|
||||||
background: rgba(0, 0, 0, 0.3);
|
top: 0;
|
||||||
}
|
z-index: 9;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
.modal-outboard {
|
}
|
||||||
position: absolute;
|
|
||||||
top: 20vh;
|
.modal-warp {
|
||||||
left: 30vw;
|
position: fixed;
|
||||||
z-index: 10000;
|
top: 50%;
|
||||||
}
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
.modal-panel {
|
z-index: 19;
|
||||||
border: 1px solid #000000;
|
}
|
||||||
border-radius: 5px;
|
|
||||||
|
.modal-content {
|
||||||
.modal-header {
|
box-shadow: 0 0 10px -4px rgb(130, 86, 208);
|
||||||
$titleheight: 38px;
|
overflow: hidden;
|
||||||
width: 530px;
|
border-radius: 4px;
|
||||||
height: $titleheight;
|
|
||||||
line-height: $titleheight;
|
.modal-header {
|
||||||
background-color: rgb(99, 153, 255);
|
display: flex;
|
||||||
display: flex;
|
line-height: 38px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
.modal-header-text {
|
|
||||||
text-align: center;
|
.modal-header-text {
|
||||||
width: 480px;
|
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;
|
.modal-body {
|
||||||
display: flex;
|
padding: 10px;
|
||||||
justify-content: end;
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
button {
|
}
|
||||||
margin: 10px;
|
|
||||||
|
.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;
|
.icon {
|
||||||
width: 20px;
|
padding: 0 15px;
|
||||||
fill: currentColor;
|
width: 20px;
|
||||||
|
fill: currentColor;
|
||||||
&:hover {
|
|
||||||
color: rgba(0, 0, 0, 0.4);
|
&:hover {
|
||||||
}
|
color: rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,22 +1,22 @@
|
||||||
import { RsProgressType } from './type'
|
import React from 'react'
|
||||||
import progressScss from './progress.module.scss'
|
import styles from './progress.module.scss'
|
||||||
|
|
||||||
const Progress = (props: RsProgressType) => {
|
const Progress: React.FC<React.PropsWithChildren<{
|
||||||
|
percent?: number
|
||||||
|
}>> = props => {
|
||||||
|
const { percent = 0 } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={progressScss.progress}>
|
<div className={styles.progress}>
|
||||||
<div className='progress-pr' style={{ width: props.rateWidth ?? 250 }}>
|
<div className='progress-pr'>
|
||||||
<div
|
<div
|
||||||
className='progress-rate'
|
className='progress-rate'
|
||||||
style={{
|
style={{ width: `${percent / 100}%` }}
|
||||||
width: (props.percent ?? 0) * ((props.rateWidth ?? 250) / 100),
|
/>
|
||||||
backgroundColor: props.rateColor ?? 'blue',
|
</div>
|
||||||
}}
|
<span className='progress-num'>{percent}%</span>
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span className='progress-num'>{props.percent > 100 ? 100 :(props.percent.toString().substring(0,4) ?? 0) }%</span>
|
)
|
||||||
</div>
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Progress;
|
export default Progress
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
.progress-pr {
|
.progress-pr {
|
||||||
border: 1px solid #000000;
|
border: 1px solid #000;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,11 @@
|
||||||
.progress-rate {
|
.progress-rate {
|
||||||
height: 6px;
|
height: 6px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
background-image: linear-gradient(to right, rgb(130, 86, 208) 0%, var(--primary-color) 100%)
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-num {
|
.progress-num {
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
export interface RsProgressType {
|
|
||||||
rateColor?: string
|
|
||||||
rateWidth?: number
|
|
||||||
percent: number
|
|
||||||
}
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
interface VersionInfo {
|
||||||
|
update: boolean
|
||||||
|
version: string
|
||||||
|
newVersion?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorType {
|
||||||
|
message: string
|
||||||
|
error: Error
|
||||||
|
}
|
|
@ -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 Modal from '@/components/update/Modal'
|
||||||
import Progress from '@/components/update/Progress'
|
import Progress from '@/components/update/Progress'
|
||||||
import { ipcRenderer } from 'electron'
|
import styles from './update.module.scss'
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import updateScss from './update.module.scss'
|
|
||||||
import { checkUpdateType, isUpdateAvailable, ModalBtnText, VersionInfo } from './type'
|
|
||||||
|
|
||||||
|
|
||||||
let onModalSubmit = () => { }
|
|
||||||
let onModalCanCel = () => { }
|
|
||||||
|
|
||||||
const Update = () => {
|
const Update = () => {
|
||||||
const [checkBtnText, setCheckBtnText] = useState('check update')
|
const [checking, setChecking] = useState(false)
|
||||||
const [checkType, setCheckType] = useState(false)
|
const [updateAvailable, setUpdateAvailable] = useState(false)
|
||||||
const [checkLoading, setCheckLoading] = useState(false)
|
const [versionInfo, setVersionInfo] = useState<VersionInfo>()
|
||||||
const [isOpenModal, setIsOpenModal] = useState<boolean>(false)
|
const [updateError, setUpdateError] = useState<ErrorType>()
|
||||||
const [percentNum, setPercentNum] = useState<number>(0)
|
const [progressInfo, setProgressInfo] = useState<ProgressInfo>()
|
||||||
const [isNeedUpdate, setIsNeedUpdate] = useState<boolean>(false)
|
const [modalOpen, setModalOpen] = useState<boolean>(false)
|
||||||
const [updateError, setUpdateError] = useState<boolean>(false)
|
const [modalBtn, setModalBtn] = useState<{
|
||||||
const [versionInfo, setVersionInfo] = useState<VersionInfo>({
|
cancelText?: string
|
||||||
oldVersion: '',
|
okText?: string
|
||||||
newVersion: ''
|
onCancel?: () => void
|
||||||
})
|
onOk?: () => void
|
||||||
const [modalBtnText, setModalBtnText] = useState<ModalBtnText>({
|
}>({
|
||||||
canCelText: '',
|
onCancel: () => setModalOpen(false),
|
||||||
submitText: ''
|
onOk: () => ipcRenderer.invoke('start-download'),
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
const checkUpdate = async () => {
|
||||||
onModalCanCel = () => setIsOpenModal(false)
|
setChecking(true)
|
||||||
}, [])
|
/**
|
||||||
|
* @type {import('electron-updater').UpdateCheckResult | null | { message: string, error: Error }}
|
||||||
|
*/
|
||||||
|
const result = await ipcRenderer.invoke('check-update')
|
||||||
|
setChecking(false)
|
||||||
|
|
||||||
// Check for updates
|
if (result?.error) {
|
||||||
const checkUpdate = () => {
|
console.error(result.error)
|
||||||
setCheckLoading(true)
|
setUpdateAvailable(false)
|
||||||
setCheckBtnText('checking Update ...')
|
} else {
|
||||||
ipcRenderer.send('check-update')
|
setUpdateAvailable(true)
|
||||||
|
setModalOpen(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen to get the check result
|
const onUpdateCanAvailable = useCallback((_event: Electron.IpcRendererEvent, arg1: VersionInfo) => {
|
||||||
ipcRenderer.on('check-update-type', (_event, ...args: checkUpdateType[]) => {
|
setVersionInfo(arg1)
|
||||||
setCheckLoading(false)
|
// Can be update
|
||||||
setCheckBtnText('check update')
|
if (arg1.update) {
|
||||||
setCheckType(args[0].checkUpdate)
|
setModalBtn(state => ({
|
||||||
setIsOpenModal(true)
|
...state,
|
||||||
})
|
cancelText: 'Cancel',
|
||||||
|
okText: 'Update',
|
||||||
// Get version information and whether to update
|
onCancel: () => setModalOpen(false),
|
||||||
ipcRenderer.on('is-update-available', (_event, ...args: isUpdateAvailable[]) => {
|
onOk: () => ipcRenderer.invoke('start-download'),
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
})
|
}, [])
|
||||||
|
|
||||||
// Throw the update failure message when the update fails
|
const onUpdateError = useCallback((_event: Electron.IpcRendererEvent, arg1: ErrorType) => {
|
||||||
ipcRenderer.on('update-error', (_event, ...args: { updateError: boolean }[]) => {
|
console.error(arg1.error)
|
||||||
setUpdateError(args[0].updateError)
|
setUpdateError(arg1)
|
||||||
setCheckType(false)
|
}, [])
|
||||||
})
|
|
||||||
|
|
||||||
// Get update progress
|
const onDownloadProgress = useCallback((_event: Electron.IpcRendererEvent, arg1: ProgressInfo) => {
|
||||||
ipcRenderer.on('update-progress', (_event, ...args: { progressInfo: number }[]) => {
|
console.log(arg1)
|
||||||
setPercentNum(args[0].progressInfo)
|
setProgressInfo(arg1)
|
||||||
})
|
}, [])
|
||||||
|
|
||||||
// is update been completed
|
const onUpdateDownloaded = useCallback((_event: Electron.IpcRendererEvent, ...args: any[]) => {
|
||||||
ipcRenderer.on('update-downed', (_event, ...args) => {
|
setModalBtn(state => ({
|
||||||
setPercentNum(100)
|
...state,
|
||||||
setModalBtnText({
|
cancelText: 'Later',
|
||||||
canCelText: 'install later',
|
okText: 'Install now',
|
||||||
submitText: 'install now'
|
onOk: () => ipcRenderer.invoke('quit-and-install'),
|
||||||
})
|
}))
|
||||||
onModalSubmit = () => ipcRenderer.send('quit-and-install')
|
}, [])
|
||||||
onModalCanCel = () => {
|
|
||||||
ipcRenderer.send('will-quit')
|
useEffect(() => {
|
||||||
setIsOpenModal(false)
|
// 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal isOpenModal={isOpenModal} onCanCel={onModalCanCel} onSubmit={onModalSubmit}
|
<Modal
|
||||||
canCelText={modalBtnText.canCelText} submitText={modalBtnText.submitText}
|
open={modalOpen}
|
||||||
isFooterShow={checkType && isNeedUpdate}>
|
cancelText={modalBtn?.cancelText}
|
||||||
<div className={updateScss.modalslot}>
|
okText={modalBtn?.okText}
|
||||||
{updateError ?
|
onCancel={modalBtn?.onCancel}
|
||||||
<div className='update-error'>Error downloading the latest version, please contact the developer</div> :
|
onOk={modalBtn?.onOk}
|
||||||
checkType ? (
|
footer={!versionInfo?.update ? /* hide footer */null : undefined}
|
||||||
isNeedUpdate ? (
|
>
|
||||||
<div>
|
<div className={styles.modalslot}>
|
||||||
<div>
|
{updateError ? (
|
||||||
<span> oldVersion : v.{versionInfo.oldVersion} </span>
|
<div className='update-error'>
|
||||||
<span> newVersion : v.{versionInfo.newVersion} </span>
|
<p>Error downloading the latest version.</p>
|
||||||
|
<p>{updateError.message}</p>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{!updateAvailable ? (
|
||||||
|
<div className='update-not-available'>
|
||||||
|
<span>Check update is Error, Please check your Network!</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{versionInfo
|
||||||
|
? (
|
||||||
|
<div>
|
||||||
|
<div>The last version is: v{versionInfo.newVersion}</div>
|
||||||
|
<div>v{versionInfo.version} -> v{versionInfo.newVersion}</div>
|
||||||
|
<div className='update-progress'>
|
||||||
|
<div className='progress-title'>Update progress:</div>
|
||||||
|
<div className='progress-bar'>
|
||||||
|
<Progress percent={progressInfo?.percent} ></Progress>
|
||||||
</div>
|
</div>
|
||||||
<div className='update-progress'>
|
</div>
|
||||||
<span className='progress-title'> update progress : </span>
|
</div>
|
||||||
<Progress percent={percentNum} ></Progress>
|
)
|
||||||
</div>
|
: <span>Checking...</span>}
|
||||||
</div>)
|
|
||||||
: <span>This is last version : v.{versionInfo.oldVersion} !</span>
|
|
||||||
) : <span>Check update is Error,Please check your network!</span>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
<button disabled={checkLoading} onClick={checkUpdate}>
|
<button disabled={checking} onClick={checkUpdate}>
|
||||||
{checkBtnText}
|
{checking ? 'Checking...' : 'Check update'}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Update
|
export default Update
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
|
|
||||||
export interface checkUpdateType {
|
|
||||||
checkUpdate: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VersionInfo {
|
|
||||||
oldVersion: string
|
|
||||||
newVersion: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface isUpdateAvailable extends VersionInfo {
|
|
||||||
isUpdate: boolean
|
|
||||||
|
|
||||||
}
|
|
||||||
export interface ModalBtnText {
|
|
||||||
canCelText: string
|
|
||||||
submitText: string
|
|
||||||
}
|
|
|
@ -1,19 +1,20 @@
|
||||||
.modalslot{
|
.modalslot {
|
||||||
display: flex;
|
// display: flex;
|
||||||
align-items: center;
|
// align-items: center;
|
||||||
justify-content: center;
|
// justify-content: center;
|
||||||
height: 100px;
|
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
.progress-title {
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.update-progress {
|
.update-progress {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-title {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.a{
|
|
||||||
color: red;
|
|
||||||
}
|
|
Loading…
Reference in New Issue