mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-04-28 03:36:16 -04:00
feat(gui): Display dialog when update is available (#132)
* feat(tauri): Use new tauri signing key * feat(gui): Display MUI dialog when update is available
This commit is contained in:
parent
a9b1d05af0
commit
c1afc7aa2a
@ -9,6 +9,7 @@ import HistoryPage from "./pages/history/HistoryPage";
|
|||||||
import SwapPage from "./pages/swap/SwapPage";
|
import SwapPage from "./pages/swap/SwapPage";
|
||||||
import WalletPage from "./pages/wallet/WalletPage";
|
import WalletPage from "./pages/wallet/WalletPage";
|
||||||
import GlobalSnackbarProvider from "./snackbar/GlobalSnackbarProvider";
|
import GlobalSnackbarProvider from "./snackbar/GlobalSnackbarProvider";
|
||||||
|
import UpdaterDialog from "./modal/updater/UpdaterDialog";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
innerContent: {
|
innerContent: {
|
||||||
@ -58,6 +59,7 @@ export default function App() {
|
|||||||
<Router>
|
<Router>
|
||||||
<Navigation />
|
<Navigation />
|
||||||
<InnerContent />
|
<InnerContent />
|
||||||
|
<UpdaterDialog/>
|
||||||
</Router>
|
</Router>
|
||||||
</GlobalSnackbarProvider>
|
</GlobalSnackbarProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
166
src-gui/src/renderer/components/modal/updater/UpdaterDialog.tsx
Normal file
166
src-gui/src/renderer/components/modal/updater/UpdaterDialog.tsx
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogActions,
|
||||||
|
Button,
|
||||||
|
LinearProgress,
|
||||||
|
Typography,
|
||||||
|
makeStyles,
|
||||||
|
LinearProgressProps,
|
||||||
|
Box,
|
||||||
|
} from '@material-ui/core';
|
||||||
|
import SystemUpdateIcon from '@material-ui/icons/SystemUpdate';
|
||||||
|
import { check, Update, DownloadEvent } from '@tauri-apps/plugin-updater';
|
||||||
|
import { useSnackbar } from 'notistack';
|
||||||
|
import { relaunch } from '@tauri-apps/plugin-process';
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
progress: {
|
||||||
|
marginTop: theme.spacing(2)
|
||||||
|
},
|
||||||
|
releaseNotes: {
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
marginBottom: theme.spacing(1)
|
||||||
|
},
|
||||||
|
noteContent: {
|
||||||
|
whiteSpace: 'pre-line'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface DownloadProgress {
|
||||||
|
contentLength: number | null;
|
||||||
|
downloadedBytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function LinearProgressWithLabel(props: LinearProgressProps & { label?: string }) {
|
||||||
|
return (
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
<Box width="100%" mr={1}>
|
||||||
|
<LinearProgress variant="determinate" {...props} />
|
||||||
|
</Box>
|
||||||
|
<Box minWidth={85}>
|
||||||
|
<Typography variant="body2" color="textSecondary">
|
||||||
|
{props.label || `${Math.round(props.value)}%`}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UpdaterDialog() {
|
||||||
|
const classes = useStyles();
|
||||||
|
const [availableUpdate, setAvailableUpdate] = useState<Update | null>(null);
|
||||||
|
const [downloadProgress, setDownloadProgress] = useState<DownloadProgress | null>(null);
|
||||||
|
const {enqueueSnackbar} = useSnackbar();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check for updates when component mounts
|
||||||
|
check().then((updateResponse) => {
|
||||||
|
setAvailableUpdate(updateResponse);
|
||||||
|
}).catch((err) => {
|
||||||
|
enqueueSnackbar(`Failed to check for updates: ${err}`, {
|
||||||
|
variant: 'error',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// If no update is available, don't render the dialog
|
||||||
|
if (!availableUpdate?.available) return null;
|
||||||
|
|
||||||
|
function hideNotification() {
|
||||||
|
setAvailableUpdate(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function handleInstall() {
|
||||||
|
try {
|
||||||
|
await availableUpdate.downloadAndInstall((event: DownloadEvent) => {
|
||||||
|
if (event.event === 'Started') {
|
||||||
|
setDownloadProgress({
|
||||||
|
contentLength: event.data.contentLength || null,
|
||||||
|
downloadedBytes: 0,
|
||||||
|
});
|
||||||
|
} else if (event.event === 'Progress') {
|
||||||
|
setDownloadProgress(prev => ({
|
||||||
|
...prev,
|
||||||
|
downloadedBytes: prev.downloadedBytes + event.data.chunkLength,
|
||||||
|
}));
|
||||||
|
} else if (event.event === 'Finished') {
|
||||||
|
// Relaunch the application for the new version to be used
|
||||||
|
relaunch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
enqueueSnackbar(`Failed to install update: ${err}`, {
|
||||||
|
variant: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDownloading = downloadProgress !== null;
|
||||||
|
|
||||||
|
const progress = isDownloading
|
||||||
|
? Math.round((downloadProgress.downloadedBytes / downloadProgress.contentLength) * 100)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
open={availableUpdate.available}
|
||||||
|
onClose={hideNotification}
|
||||||
|
>
|
||||||
|
<DialogTitle>Update Available</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
A new version (v{availableUpdate.version}) is available. Your current version is {availableUpdate.currentVersion}.
|
||||||
|
The update will be verified using PGP signature verification to ensure authenticity.
|
||||||
|
{availableUpdate.body && (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" className={classes.releaseNotes}>
|
||||||
|
Release Notes:
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" component="div" className={classes.noteContent}>
|
||||||
|
{availableUpdate.body}
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DialogContentText>
|
||||||
|
|
||||||
|
{isDownloading && (
|
||||||
|
<Box className={classes.progress}>
|
||||||
|
<LinearProgressWithLabel
|
||||||
|
value={progress}
|
||||||
|
label={`${(downloadProgress.downloadedBytes / 1024 / 1024).toFixed(1)} MB${
|
||||||
|
downloadProgress.contentLength
|
||||||
|
? ` / ${(downloadProgress.contentLength / 1024 / 1024).toFixed(1)} MB`
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
color="default"
|
||||||
|
onClick={hideNotification}
|
||||||
|
disabled={isDownloading}
|
||||||
|
>
|
||||||
|
Remind me later
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
endIcon={<SystemUpdateIcon />}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleInstall}
|
||||||
|
disabled={isDownloading}
|
||||||
|
>
|
||||||
|
{isDownloading ? 'DOWNLOADING...' : 'INSTALL UPDATE'}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
@ -18,8 +18,6 @@ import {
|
|||||||
import App from "./components/App";
|
import App from "./components/App";
|
||||||
import { initEventListeners } from "./rpc";
|
import { initEventListeners } from "./rpc";
|
||||||
import { persistor, store } from "./store/storeRenderer";
|
import { persistor, store } from "./store/storeRenderer";
|
||||||
import { Box } from "@material-ui/core";
|
|
||||||
import { checkForAppUpdates } from "./updater";
|
|
||||||
|
|
||||||
const container = document.getElementById("root");
|
const container = document.getElementById("root");
|
||||||
const root = createRoot(container!);
|
const root = createRoot(container!);
|
||||||
@ -77,4 +75,3 @@ async function fetchInitialData() {
|
|||||||
|
|
||||||
fetchInitialData();
|
fetchInitialData();
|
||||||
initEventListeners();
|
initEventListeners();
|
||||||
checkForAppUpdates();
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import { check } from "@tauri-apps/plugin-updater";
|
|
||||||
import { ask, message } from "@tauri-apps/plugin-dialog";
|
|
||||||
import { relaunch } from "@tauri-apps/plugin-process";
|
|
||||||
|
|
||||||
export async function checkForAppUpdates() {
|
|
||||||
const update = await check();
|
|
||||||
|
|
||||||
if (update?.available) {
|
|
||||||
const yes = await ask(
|
|
||||||
`
|
|
||||||
Update to ${update.version} is available!
|
|
||||||
Release notes: ${update.body}
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
title: "Update Now!",
|
|
||||||
kind: "info",
|
|
||||||
okLabel: "Update",
|
|
||||||
cancelLabel: "Cancel",
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (yes) {
|
|
||||||
await update.downloadAndInstall();
|
|
||||||
await relaunch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -41,7 +41,7 @@
|
|||||||
"endpoints": [
|
"endpoints": [
|
||||||
"https://cdn.crabnebula.app/update/unstoppableswap/unstoppableswap-gui-rs/{{target}}-{{arch}}/{{current_version}}"
|
"https://cdn.crabnebula.app/update/unstoppableswap/unstoppableswap-gui-rs/{{target}}-{{arch}}/{{current_version}}"
|
||||||
],
|
],
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyNTQ3NTQxQTQ2MkI4N0IKUldSN3VHS2tRWFZVMGpWYytkRFg4dFBzNEh5ZnlxaHBubGpRalVMMG5nVytiR3JPOUE3QjRxc00K"
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEE2MDgxRDEwMDZENkYxNUMKUldSYzhkWUdFQjBJcGwzN24yZlduTzNndFZnVW9Qa1k2WFVTMEMxcHBSc2dSVVlzbVNHdGNFQ0EK"
|
||||||
},
|
},
|
||||||
"cli": {
|
"cli": {
|
||||||
"description": "Start the GUI application",
|
"description": "Start the GUI application",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user