Skip to content

Commit 1c1e1fe

Browse files
committed
feat: optional auto update
1 parent 9ad37e1 commit 1c1e1fe

File tree

4 files changed

+133
-12
lines changed

4 files changed

+133
-12
lines changed

package-lock.json

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@base-ui/react": "^1.1.0",
1919
"@phosphor-icons/react": "^2.1.10",
2020
"@radix-ui/react-alert-dialog": "^1.1.15",
21+
"@tanstack/react-query": "^5.90.21",
2122
"@tauri-apps/api": "^2.0.0",
2223
"@tauri-apps/plugin-dialog": "^2.0.0",
2324
"@tauri-apps/plugin-opener": "^2.5.2",
Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
1-
import { TranslationProvider } from '@/i18n'
2-
import { AppThemeProvider } from '../AppThemeProvider'
3-
import { AnchoredToastProvider, ToastProvider } from '../ui/toast'
1+
import { TranslationProvider } from "@/i18n";
2+
import { AppThemeProvider } from "../AppThemeProvider";
3+
import { AnchoredToastProvider, ToastProvider } from "../ui/toast";
4+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5+
6+
const queryClient = new QueryClient();
47

58
export function AppProviders({ children }: { children: React.ReactNode }) {
6-
return (
7-
<TranslationProvider>
8-
<ToastProvider position="bottom-center" limit={1}>
9-
<AnchoredToastProvider>
10-
<AppThemeProvider>{children}</AppThemeProvider>
11-
</AnchoredToastProvider>
12-
</ToastProvider>
13-
</TranslationProvider>
14-
)
9+
return (
10+
<TranslationProvider>
11+
<QueryClientProvider client={queryClient}>
12+
<ToastProvider position="bottom-center" limit={1}>
13+
<AnchoredToastProvider>
14+
<AppThemeProvider>{children}</AppThemeProvider>
15+
</AnchoredToastProvider>
16+
</ToastProvider>
17+
</QueryClientProvider>
18+
</TranslationProvider>
19+
);
1520
}

web-app/src/components/settings/auto-update/auto-update.tsx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,56 @@
1+
import { useState } from "react";
12
import { useTranslation } from "../../../i18n";
23
import { useAppSettingStore } from "../../../store/app-setting";
4+
import { Button } from "../../ui/button";
35
import {
46
FrameTitle,
57
FrameDescription,
68
Frame,
79
FramePanel,
10+
FrameFooter,
811
} from "../../ui/frame";
912
import { Switch } from "../../ui/switch";
13+
import { AlertDialog, AlertDialogContent } from "../../ui/alert-dialog";
14+
import { Gift, Loader2 } from "lucide-react";
15+
import { check } from "@tauri-apps/plugin-updater";
16+
import { relaunch } from "@tauri-apps/plugin-process";
17+
import { useMutation } from "@tanstack/react-query";
18+
import { toastManager } from "../../ui/toast";
1019

1120
export function AutoUpdate() {
1221
const { t } = useTranslation();
1322
const value = useAppSettingStore((r) => r.autoUpdate);
1423
const toggle = useAppSettingStore((r) => r.setAutoUpdate);
24+
const [isOpen, setIsOpen] = useState(false);
25+
const checkForUpdates = useMutation({
26+
mutationFn: async () => {
27+
const update = await check();
28+
return update;
29+
},
30+
onSuccess: (update) => {
31+
if (update) {
32+
setIsOpen(true);
33+
} else {
34+
toastManager.add({
35+
title: "No updates available",
36+
description: "You are already using the latest version",
37+
type: "info",
38+
});
39+
}
40+
},
41+
});
42+
const handleUpdate = useMutation({
43+
mutationFn: async () => {
44+
const update = await check();
45+
if (update) {
46+
await update.downloadAndInstall();
47+
await relaunch();
48+
}
49+
},
50+
onSuccess: () => {
51+
setIsOpen(false);
52+
},
53+
});
1554

1655
return (
1756
<Frame>
@@ -26,6 +65,55 @@ export function AutoUpdate() {
2665
</div>
2766
<Switch checked={value} onCheckedChange={toggle} />
2867
</FramePanel>
68+
{value === false && (
69+
<FrameFooter className="flex-row justify-end">
70+
<Button
71+
className="w-48"
72+
variant="secondary"
73+
onClick={() => checkForUpdates.mutate()}
74+
disabled={checkForUpdates.isPending}
75+
>
76+
{checkForUpdates.isPending ? (
77+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
78+
) : null}
79+
Check for updates
80+
</Button>
81+
</FrameFooter>
82+
)}
83+
<AlertDialog open={isOpen} onOpenChange={setIsOpen}>
84+
<AlertDialogContent
85+
backdropClassName="!bg-transparent !backdrop-blur-none"
86+
className="fixed bottom-1 left-2 translate-x-0 translate-y-0 w-md"
87+
>
88+
<div className="flex px-5 py-4 items-center gap-2">
89+
<Gift className="w-4 h-4 text-muted-foreground" />
90+
<p className="text-sm flex items-center text-muted-foreground">
91+
A new version is available -{" "}
92+
{checkForUpdates.data?.version}
93+
</p>
94+
<div className="flex gap-2 ml-auto">
95+
<Button
96+
variant="outline"
97+
size="sm"
98+
onClick={() => setIsOpen(false)}
99+
>
100+
Later
101+
</Button>
102+
<Button
103+
size="sm"
104+
disabled={handleUpdate.isPending}
105+
onClick={() => handleUpdate.mutate()}
106+
>
107+
{handleUpdate.isPending ? (
108+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
109+
) : (
110+
"Update now"
111+
)}
112+
</Button>
113+
</div>
114+
</div>
115+
</AlertDialogContent>
116+
</AlertDialog>
29117
</Frame>
30118
);
31119
}

0 commit comments

Comments
 (0)