Skip to content

Commit

Permalink
refactor: Refactor update logic
Browse files Browse the repository at this point in the history
  • Loading branch information
yanguoyu committed Jul 31, 2023
1 parent b81b08f commit f9f75ac
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 119 deletions.
46 changes: 46 additions & 0 deletions packages/neuron-ui/src/components/GeneralSetting/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react'
import { cancelCheckUpdates, cancelDownloadUpdate, checkForUpdates } from 'services/remote'

export const useUpdateDownloadStatus = ({
setShowCheckDialog,
downloadProgress,
}: {
setShowCheckDialog: Dispatch<SetStateAction<boolean>>
downloadProgress: number
}) => {
const [showUpdateDownloadStatus, setShowUpdateDownloadStatus] = useState(false)
const openShowUpdateDownloadStatus = useCallback(() => {
setShowUpdateDownloadStatus(true)
}, [])
const onCheckUpdate = useCallback(() => {
setShowCheckDialog(true)
checkForUpdates()
}, [setShowCheckDialog])
const hasStartDownload = useMemo(() => downloadProgress >= 0, [downloadProgress])
return {
showUpdateDownloadStatus,
openShowUpdateDownloadStatus,
onCheckUpdate,
onCancel: useCallback(() => {
if (hasStartDownload) {
cancelDownloadUpdate()
}
setShowUpdateDownloadStatus(false)
}, [hasStartDownload]),
}
}

export const useCheckUpdate = () => {
const [showCheckDialog, setShowCheckDialog] = useState(false)
const onCancelCheckUpdates = useCallback(() => {
if (showCheckDialog) {
cancelCheckUpdates()
}
setShowCheckDialog(false)
}, [showCheckDialog])
return {
showCheckDialog,
setShowCheckDialog,
onCancelCheckUpdates,
}
}
105 changes: 29 additions & 76 deletions packages/neuron-ui/src/components/GeneralSetting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,15 @@ import LanguageDialog from 'components/LanguageDialog'
import AlertDialog from 'widgets/AlertDialog'
import { ReactComponent as VersionLogo } from 'widgets/Icons/VersionLogo.svg'
import { ReactComponent as ArrowNext } from 'widgets/Icons/ArrowNext.svg'
import {
checkForUpdates,
cancelCheckUpdates,
downloadUpdate,
cancelDownloadUpdate,
installUpdate,
getVersion,
} from 'services/remote'
import { cancelCheckUpdates, downloadUpdate, installUpdate, getVersion } from 'services/remote'
import { uniformTimeFormatter, bytesFormatter, clsx } from 'utils'
import { LanguageSelect } from 'widgets/Icons/icon'
import styles from './generalSetting.module.scss'
import { useCheckUpdate, useUpdateDownloadStatus } from './hooks'

interface UpdateDownloadStatusProps {
show: boolean
onCancel: (status?: string) => void
onCancel: () => void
progress: number
newVersion: string
releaseDate: string
Expand Down Expand Up @@ -69,7 +63,7 @@ const UpdateDownloadStatus = ({
onConfirm={handleConfirm}
disabled={!available}
confirmText={t('updates.install-update')}
onCancel={() => onCancel('checked')}
onCancel={onCancel}
title={t('updates.update-available')}
confirmProps={{
'data-method': 'download',
Expand Down Expand Up @@ -135,76 +129,45 @@ const GeneralSetting = ({ updater }: GeneralSettingProps) => {
const [showLangDialog, setShowLangDialog] = useState(false)
const [searchParams] = useSearchParams()
const [errorMsg, setErrorMsg] = useState('')
const [dialogType, setDialogType] = useState<'' | 'checking' | 'checked' | 'updating' | 'updated'>('')
const [isFetchUpdateByClick, setIsFetchUpdateByClick] = useState<boolean>(false)
const { version: newVersion } = updater
const { showCheckDialog, setShowCheckDialog, onCancelCheckUpdates } = useCheckUpdate()
const { version: newVersion, checking, downloadProgress } = updater
const { showUpdateDownloadStatus, openShowUpdateDownloadStatus, onCheckUpdate, onCancel } = useUpdateDownloadStatus({
setShowCheckDialog,
downloadProgress,
})

const version = useMemo(() => {
useEffect(() => {
if (showCheckDialog && newVersion) {
setShowCheckDialog(false)
openShowUpdateDownloadStatus()
}
}, [showCheckDialog, newVersion, openShowUpdateDownloadStatus, setShowCheckDialog])

const currentVersion = useMemo(() => {
return getVersion()
}, [])

useEffect(() => {
const checkUpdate = searchParams.get('checkUpdate')
if (checkUpdate === '1') {
checkForUpdates()
setIsFetchUpdateByClick(true)
onCheckUpdate()
}
return () => setIsFetchUpdateByClick(false)
}, [searchParams, checkForUpdates])
}, [searchParams, onCheckUpdate])

useEffect(() => {
if (updater.errorMsg) {
setErrorMsg(updater.errorMsg)
cancelCheckUpdates()
return
}
if (updater.isUpdated) {
setDialogType('updated')
return
}
if (updater.checking) {
setDialogType('checking')
return
}
if (newVersion) {
setDialogType('checked')
return
}
if (updater.downloadProgress > 0) {
setDialogType('updating')
return
}
setDialogType('')
}, [updater, setDialogType, setErrorMsg])

const handleUpdate = useCallback(
(e: React.SyntheticEvent) => {
const {
dataset: { method },
} = e.target as HTMLElement

setIsFetchUpdateByClick(true)
if (newVersion) {
setDialogType('checked')
} else if (method === 'cancelCheck') {
if (dialogType === 'checking') {
cancelCheckUpdates()
}
setDialogType('')
} else if (method === 'check') {
checkForUpdates()
}
},
[dialogType, setDialogType, cancelCheckUpdates, checkForUpdates]
)
}, [updater.errorMsg, setErrorMsg])

return (
<div className={styles.container}>
<div className={clsx(styles.content, `${newVersion ? styles.showVersion : ''}`)} data-new-version-tip="New">
<p>
{t('settings.general.version')} v{newVersion || version}
{t('settings.general.version')} v{newVersion || currentVersion}
</p>
<button type="button" onClick={handleUpdate} data-method="check">
<button type="button" onClick={newVersion ? openShowUpdateDownloadStatus : onCheckUpdate} data-method="check">
{t(newVersion ? 'updates.install-update' : 'updates.check-updates')} <ArrowNext />
</button>
</div>
Expand All @@ -228,37 +191,27 @@ const GeneralSetting = ({ updater }: GeneralSettingProps) => {
message={errorMsg}
type="failed"
onCancel={() => {
setIsFetchUpdateByClick(false)
setErrorMsg('')
}}
/>

<Dialog
show={['checking', 'updated'].includes(dialogType) && isFetchUpdateByClick}
show={showCheckDialog}
showCancel={false}
showHeader={false}
confirmText={t(dialogType === 'checking' ? 'common.cancel' : 'common.ok')}
onConfirm={handleUpdate}
confirmText={t(checking ? 'common.cancel' : 'common.ok')}
onConfirm={onCancelCheckUpdates}
className={styles.confirmDialog}
confirmProps={{
'data-method': 'cancelCheck',
}}
>
<div className={styles.wrap}>
<VersionLogo />
<p>{t(dialogType === 'checking' ? 'updates.checking-updates' : 'updates.update-not-available')}</p>
<p>{t(checking || newVersion ? 'updates.checking-updates' : 'updates.update-not-available')}</p>
</div>
</Dialog>

<UpdateDownloadStatus
show={dialogType === 'updating' || (dialogType === 'checked' && isFetchUpdateByClick)}
onCancel={status => {
if (status !== 'checked') {
cancelDownloadUpdate()
}
setIsFetchUpdateByClick(false)
setDialogType('')
}}
show={showUpdateDownloadStatus}
onCancel={onCancel}
progress={updater.downloadProgress}
progressInfo={updater.progressInfo}
newVersion={updater.version}
Expand Down
21 changes: 8 additions & 13 deletions packages/neuron-ui/src/containers/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ const MenuButton = ({
)
}

const ONE_DAY_MILLISECONDS = 24 * 3600 * 1000

const Navbar = () => {
const { pathname } = useLocation()
const dispatch = useDispatch()
Expand All @@ -94,21 +96,14 @@ const Navbar = () => {
}, [dispatch])

useEffect(() => {
const now = new Date()
const nextTriggerTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59)

checkForUpdates()
if (nextTriggerTime) {
const timeDiff = nextTriggerTime.getTime() - now.getTime()

setTimeout(() => {
checkForUpdates()
setInterval(() => {
checkForUpdates()
}, 24 * 60 * 60 * 1000)
}, timeDiff)
const interval = setInterval(() => {
checkForUpdates()
}, ONE_DAY_MILLISECONDS)
return () => {
clearInterval(interval)
}
}, [checkForUpdates])
}, [])

useEffect(() => {
if (pathname.includes(RoutePath.Settings)) {
Expand Down
61 changes: 33 additions & 28 deletions packages/neuron-wallet/src/controllers/update.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { autoUpdater, UpdateInfo, CancellationToken } from 'electron-updater'
import AppUpdaterSubject from '../models/subjects/app-updater'
import AppUpdaterSubject, { AppUpdater } from '../models/subjects/app-updater'

export default class UpdateController {
static isChecking = false // One instance is already running and checking

static downCancellationToken = new CancellationToken()

static lastNotifyInfo: AppUpdater

constructor(check: boolean = true) {
autoUpdater.autoDownload = false

Expand Down Expand Up @@ -34,14 +36,14 @@ export default class UpdateController {
}

public downloadUpdate() {
this.notify(0)
this.notify({ ...UpdateController.lastNotifyInfo, downloadProgress: 0 })
UpdateController.downCancellationToken = new CancellationToken()
autoUpdater.downloadUpdate(UpdateController.downCancellationToken)
}

public cancelDownloadUpdate() {
UpdateController.downCancellationToken.cancel()
this.notify()
this.notify({ ...UpdateController.lastNotifyInfo, downloadProgress: -1 })
autoUpdater.removeAllListeners()
}

Expand All @@ -50,54 +52,57 @@ export default class UpdateController {

autoUpdater.on('error', error => {
UpdateController.isChecking = false
this.notify(-1, null, false, '', '', '', error == null ? 'unknown' : (error.stack || error).toString())
this.notify({
version: '',
releaseDate: '',
releaseNotes: '',
errorMsg: error == null ? 'unknown' : (error.stack || error).toString(),
})
})

autoUpdater.on('update-available', (info: UpdateInfo) => {
if (UpdateController.isChecking) {
UpdateController.isChecking = false
this.notify(-1, null, false, info.version, info.releaseDate, info.releaseNotes as string)
this.notify({
version: info.version,
releaseDate: info.releaseDate,
releaseNotes: info.releaseNotes as string,
})
}
})

autoUpdater.on('update-not-available', () => {
if (UpdateController.isChecking) {
UpdateController.isChecking = false
this.notify(-1, null, true)
this.notify({ isUpdated: true })
}
})

autoUpdater.on('download-progress', progress => {
const progressPercent = progress.percent / 100
autoUpdater.on('download-progress', progressInfo => {
const progressPercent = progressInfo.percent / 100
if (progressPercent !== 1) {
this.notify(progressPercent, progress)
this.notify({ ...UpdateController.lastNotifyInfo, downloadProgress: progressPercent, progressInfo })
}
})

autoUpdater.on('update-downloaded', () => {
UpdateController.isChecking = false
this.notify(1)
this.notify({ ...UpdateController.lastNotifyInfo, downloadProgress: 1 })
})
}

private notify(
downloadProgress: number = -1,
progressInfo = null,
isUpdated = false,
version = '',
releaseDate = '',
releaseNotes = '',
errorMsg = ''
) {
AppUpdaterSubject.next({
downloadProgress,
progressInfo,
isUpdated,
version,
releaseDate,
releaseNotes,
errorMsg,
private notify(appUpdater?: Partial<Omit<AppUpdater, 'checking'>>) {
UpdateController.lastNotifyInfo = {
downloadProgress: -1,
progressInfo: null,
isUpdated: false,
version: '',
releaseDate: '',
releaseNotes: '',
errorMsg: '',
...appUpdater,
checking: UpdateController.isChecking,
})
}
AppUpdaterSubject.next(UpdateController.lastNotifyInfo)
}
}
6 changes: 4 additions & 2 deletions packages/neuron-wallet/src/models/subjects/app-updater.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Subject } from 'rxjs'

const AppUpdaterSubject = new Subject<{
export interface AppUpdater {
checking: boolean
isUpdated: boolean
downloadProgress: number // -1: not started, 1: finished, 0~1: downloading
Expand All @@ -13,6 +13,8 @@ const AppUpdaterSubject = new Subject<{
releaseDate: string
releaseNotes: string
errorMsg: string
}>()
}

const AppUpdaterSubject = new Subject<AppUpdater>()

export default AppUpdaterSubject

0 comments on commit f9f75ac

Please sign in to comment.