134 lines
4.3 KiB
TypeScript
134 lines
4.3 KiB
TypeScript
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 './update.css'
|
|
|
|
const Update = () => {
|
|
const [checking, setChecking] = useState(false)
|
|
const [updateAvailable, setUpdateAvailable] = useState(false)
|
|
const [versionInfo, setVersionInfo] = useState<VersionInfo>()
|
|
const [updateError, setUpdateError] = useState<ErrorType>()
|
|
const [progressInfo, setProgressInfo] = useState<Partial<ProgressInfo>>()
|
|
const [modalOpen, setModalOpen] = useState<boolean>(false)
|
|
const [modalBtn, setModalBtn] = useState<{
|
|
cancelText?: string
|
|
okText?: string
|
|
onCancel?: () => void
|
|
onOk?: () => void
|
|
}>({
|
|
onCancel: () => setModalOpen(false),
|
|
onOk: () => ipcRenderer.invoke('start-download'),
|
|
})
|
|
|
|
const checkUpdate = async () => {
|
|
setChecking(true)
|
|
/**
|
|
* @type {import('electron-updater').UpdateCheckResult | null | { message: string, error: Error }}
|
|
*/
|
|
const result = await ipcRenderer.invoke('check-update')
|
|
setProgressInfo({ percent: 0 })
|
|
setChecking(false)
|
|
setModalOpen(true)
|
|
if (result?.error) {
|
|
setUpdateAvailable(false)
|
|
setUpdateError(result?.error)
|
|
}
|
|
}
|
|
|
|
const onUpdateCanAvailable = useCallback((_event: Electron.IpcRendererEvent, arg1: VersionInfo) => {
|
|
setVersionInfo(arg1)
|
|
setUpdateError(undefined)
|
|
// Can be update
|
|
if (arg1.update) {
|
|
setModalBtn(state => ({
|
|
...state,
|
|
cancelText: 'Cancel',
|
|
okText: 'Update',
|
|
onOk: () => ipcRenderer.invoke('start-download'),
|
|
}))
|
|
setUpdateAvailable(true)
|
|
} else {
|
|
setUpdateAvailable(false)
|
|
}
|
|
}, [])
|
|
|
|
const onUpdateError = useCallback((_event: Electron.IpcRendererEvent, arg1: ErrorType) => {
|
|
setUpdateAvailable(false)
|
|
setUpdateError(arg1)
|
|
}, [])
|
|
|
|
const onDownloadProgress = useCallback((_event: Electron.IpcRendererEvent, arg1: ProgressInfo) => {
|
|
setProgressInfo(arg1)
|
|
}, [])
|
|
|
|
const onUpdateDownloaded = useCallback((_event: Electron.IpcRendererEvent, ...args: any[]) => {
|
|
setProgressInfo({ percent: 100 })
|
|
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 (
|
|
<>
|
|
<Modal
|
|
open={modalOpen}
|
|
cancelText={modalBtn?.cancelText}
|
|
okText={modalBtn?.okText}
|
|
onCancel={modalBtn?.onCancel}
|
|
onOk={modalBtn?.onOk}
|
|
footer={updateAvailable ? /* hide footer */null : undefined}
|
|
>
|
|
<div className='modal-slot'>
|
|
{updateError
|
|
? (
|
|
<div>
|
|
<p>Error downloading the latest version.</p>
|
|
<p>{updateError.message}</p>
|
|
</div>
|
|
) : updateAvailable
|
|
? (
|
|
<div>
|
|
<div>The last version is: v{versionInfo?.newVersion}</div>
|
|
<div className='new-version__target'>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>
|
|
)
|
|
: (
|
|
<div className='can-not-available'>{JSON.stringify(versionInfo ?? {}, null, 2)}</div>
|
|
)}
|
|
</div>
|
|
</Modal>
|
|
<button disabled={checking} onClick={checkUpdate}>
|
|
{checking ? 'Checking...' : 'Check update'}
|
|
</button>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default Update
|