From 4e6fc19ea8bb27c273f1c1a947ed6961c3cc8176 Mon Sep 17 00:00:00 2001 From: binarybaron <86064887+binarybaron@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:15:54 +0200 Subject: [PATCH] feat: Display update dialog in React, store update infos in Redux --- src/main/dev-app-update.yml | 5 ++ src/main/main.ts | 2 + src/main/updater.ts | 32 ++++----- src/renderer/components/App.tsx | 2 + .../modal/updater/UpdaterDialog.tsx | 69 +++++++++++++++++++ src/store/combinedReducer.ts | 2 + src/store/features/updateSlice.ts | 27 ++++++++ 7 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 src/main/dev-app-update.yml create mode 100644 src/renderer/components/modal/updater/UpdaterDialog.tsx create mode 100644 src/store/features/updateSlice.ts diff --git a/src/main/dev-app-update.yml b/src/main/dev-app-update.yml new file mode 100644 index 00000000..9e50b859 --- /dev/null +++ b/src/main/dev-app-update.yml @@ -0,0 +1,5 @@ +owner: UnstoppableSwap +repo: unstoppableswap-gui +provider: github +releaseType: prerelease +updaterCacheDirName: unstoppableswap-gui-updater diff --git a/src/main/main.ts b/src/main/main.ts index 3f0bc3bd..46217c38 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -33,6 +33,7 @@ import { suspendCurrentSwap, withdrawAllBitcoin, } from './cli/rpc'; +import initAutoUpdater from './updater'; let mainWindow: BrowserWindow | null = null; @@ -140,6 +141,7 @@ if (gotTheLock) { await spawnTor(); } + initAutoUpdater(); return 0; }) .catch((e) => diff --git a/src/main/updater.ts b/src/main/updater.ts index 05139e54..89751a90 100644 --- a/src/main/updater.ts +++ b/src/main/updater.ts @@ -1,20 +1,13 @@ -import { autoUpdater } from 'electron-updater'; -import { BrowserWindow, dialog } from 'electron'; +import { UpdateInfo, autoUpdater } from 'electron-updater'; import logger from '../utils/logger'; +import { isDevelopment } from 'store/config'; +import { updateReceived } from 'store/features/updateSlice'; +import { store } from './store/mainStore'; -export default async function initAutoUpdater(mainWindow: BrowserWindow) { - autoUpdater.on('update-downloaded', (info: any) => { - logger.info({ info }, 'Update downloaded'); - }); - autoUpdater.on('update-available', (info: any) => { - if (mainWindow === null) { - return; - } - dialog.showMessageBoxSync(mainWindow, { - title: 'Update available', - message: `Version ${info.version} is available. We recommend you update now by downloading the latest release from https://unstoppableswap.net/`, - type: 'info', - }); +export default async function initAutoUpdater() { + autoUpdater.on('update-available', (info: UpdateInfo) => { + store.dispatch(updateReceived(info)); + logger.info({ info }, 'Update available'); }); autoUpdater.on('update-not-available', (info: any) => { logger.info({ info }, 'Update not available'); @@ -29,7 +22,12 @@ export default async function initAutoUpdater(mainWindow: BrowserWindow) { autoUpdater.autoDownload = false; autoUpdater.allowPrerelease = false; - logger.info('Starting auto updater'); + // This is for development purposes only. It will force the auto updater to use the dev-app-update.yml file for updates. + if(isDevelopment) { + autoUpdater.forceDevUpdateConfig = true; + autoUpdater.allowDowngrade = true; + } - await autoUpdater.checkForUpdatesAndNotify(); + logger.info('Starting auto updater'); + await autoUpdater.checkForUpdates(); } diff --git a/src/renderer/components/App.tsx b/src/renderer/components/App.tsx index 1063537e..9de85ba2 100644 --- a/src/renderer/components/App.tsx +++ b/src/renderer/components/App.tsx @@ -8,6 +8,7 @@ import SwapPage from './pages/swap/SwapPage'; import WalletPage from './pages/wallet/WalletPage'; import HelpPage from './pages/help/HelpPage'; import GlobalSnackbarProvider from './snackbar/GlobalSnackbarProvider'; +import UpdaterDialog from './modal/updater/UpdaterDialog'; const useStyles = makeStyles((theme) => ({ innerContent: { @@ -57,6 +58,7 @@ export default function App() { + diff --git a/src/renderer/components/modal/updater/UpdaterDialog.tsx b/src/renderer/components/modal/updater/UpdaterDialog.tsx new file mode 100644 index 00000000..6157fba6 --- /dev/null +++ b/src/renderer/components/modal/updater/UpdaterDialog.tsx @@ -0,0 +1,69 @@ +import SystemUpdateIcon from '@material-ui/icons/SystemUpdate'; +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + Button, +} from '@material-ui/core'; +import { UpdateInfo } from 'electron-updater'; +import { useAppDispatch, useAppSelector } from 'store/hooks'; +import { updateShownToUser } from 'store/features/updateSlice'; + +export default function UpdaterDialog() { + const dispatch = useAppDispatch(); + const updateNotification: UpdateInfo | null = useAppSelector( + (state) => state.update.updateNotification + ); + + if (updateNotification == null) return null; + + function hideNotification() { + dispatch(updateShownToUser()); + } + + function openDownloadUrl() { + const downloadUrl = `https://github.com/UnstoppableSwap/unstoppableswap-gui/releases/tag/v${updateNotification!.version}`; + window.open(downloadUrl, '_blank'); + hideNotification(); + } + + return ( + hideNotification()} + > + Update Available + + + A new version (v{updateNotification.version}) of the software was + released on{' '} + {new Date(updateNotification.releaseDate).toLocaleDateString()}. + Please download and install the update manually to benefit from new + features and enhancements. Staying updated ensures you have the latest + improvements and security fixes. + + + + + + + + ); +} diff --git a/src/store/combinedReducer.ts b/src/store/combinedReducer.ts index c2c18b82..d3b8ca3d 100644 --- a/src/store/combinedReducer.ts +++ b/src/store/combinedReducer.ts @@ -4,6 +4,7 @@ import torSlice from './features/torSlice'; import rpcSlice from './features/rpcSlice'; import alertsSlice from './features/alertsSlice'; import ratesSlice from './features/ratesSlice'; +import updateSlice from './features/updateSlice'; export const reducers = { swap: swapReducer, @@ -12,4 +13,5 @@ export const reducers = { rpc: rpcSlice, alerts: alertsSlice, rates: ratesSlice, + update: updateSlice, }; diff --git a/src/store/features/updateSlice.ts b/src/store/features/updateSlice.ts new file mode 100644 index 00000000..3f3b1e0d --- /dev/null +++ b/src/store/features/updateSlice.ts @@ -0,0 +1,27 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { UpdateInfo } from 'electron-updater'; + +export interface UpdateState { + updateNotification: UpdateInfo | null; +} + +const initialState: UpdateState = { + updateNotification: null, +}; + +const updateSlice = createSlice({ + name: 'update', + initialState, + reducers: { + updateReceived: (state, action: PayloadAction) => { + state.updateNotification = action.payload; + }, + updateShownToUser: (state) => { + state.updateNotification = null; + }, + }, +}); + +export const { updateReceived, updateShownToUser } = updateSlice.actions; + +export default updateSlice.reducer;