From bd7eb52813c45c9116021d5a85cba5a576fa361d Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Fri, 13 Sep 2024 16:25:33 +0800 Subject: [PATCH] move updater to js --- apps/desktop/package.json | 1 + .../src-tauri/capabilities/default.json | 5 +- apps/desktop/src-tauri/src/lib.rs | 5 +- apps/desktop/src-tauri/src/updater.rs | 64 ---------- apps/desktop/src/routes/(main)/index.tsx | 30 ++++- apps/desktop/src/routes/(main)/update.tsx | 116 ++++++++++++++++++ pnpm-lock.yaml | 30 +++-- 7 files changed, 170 insertions(+), 81 deletions(-) delete mode 100644 apps/desktop/src-tauri/src/updater.rs create mode 100644 apps/desktop/src/routes/(main)/update.tsx diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 25b45f1f..462f85d3 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -36,6 +36,7 @@ "@tauri-apps/plugin-process": "2.0.0-rc.0", "@tauri-apps/plugin-shell": ">=2.0.0-rc.0", "@tauri-apps/plugin-store": "2.0.0-rc.0", + "@tauri-apps/plugin-updater": "2.0.0-rc.0", "@types/react-tooltip": "^4.2.4", "cva": "npm:class-variance-authority@^0.7.0", "effect": "^3.7.2", diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index bac3b6bb..981681fb 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -20,9 +20,10 @@ "core:webview:allow-create-webview-window", "shell:default", "fs:default", - "dialog:allow-save", + "dialog:default", "store:default", "process:default", - "oauth:allow-start" + "oauth:allow-start", + "updater:default" ] } diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 44fce599..70edd334 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -10,7 +10,6 @@ mod playback; mod project_recordings; mod recording; mod tray; -mod updater; mod upload; use auth::AuthStore; @@ -30,6 +29,7 @@ use serde_json::json; use specta::Type; use std::fs::File; use std::io::{BufReader, Write}; +use std::sync::atomic::AtomicBool; use std::{ collections::HashMap, marker::PhantomData, path::PathBuf, process::Command, sync::Arc, time::Duration, @@ -1137,8 +1137,6 @@ async fn list_audio_devices() -> Result, ()> { #[tauri::command(async)] #[specta::specta] fn open_main_window(app: AppHandle) { - tokio::spawn(updater::check_for_updates(app.clone())); - if let Some(window) = app.get_webview_window("main") { window.set_focus().ok(); return; @@ -1244,7 +1242,6 @@ struct RecordingMetaChanged { #[tauri::command(async)] #[specta::specta] fn get_recording_meta(app: AppHandle, id: String) -> RecordingMeta { - RecordingMeta::load_for_project(&recording_path(&app, &id)).unwrap() } diff --git a/apps/desktop/src-tauri/src/updater.rs b/apps/desktop/src-tauri/src/updater.rs deleted file mode 100644 index 30129b7f..00000000 --- a/apps/desktop/src-tauri/src/updater.rs +++ /dev/null @@ -1,64 +0,0 @@ - -use tauri::AppHandle; -use tauri_plugin_dialog::DialogExt; -use tauri_plugin_updater::UpdaterExt; - -#[tauri::command] -#[specta::specta] -pub async fn check_for_updates(app: AppHandle) -> Result<(), ()> { - let Some(update) = app - .updater() - .map_err(|_| {})? - .check() - .await - .map_err(|_| {})? - else { - return Ok(()); - }; - - let (tx, rx) = tokio::sync::oneshot::channel(); - - app.dialog() - .message(format!( - "Version {} of Cap is available, would you like to install it?", - update.version - )) - .title("Update Cap") - .ok_button_label("Update") - .cancel_button_label("Ignore") - .show(move |install| { - tx.send(install).ok(); - }); - - if !rx.await.unwrap() { - return Ok(()); - } - - update - .download_and_install( - |_, _| {}, - || { - }, - ) - .await - .unwrap(); - - let (tx, rx) = tokio::sync::oneshot::channel(); - app.dialog() - .message(format!( - "Cap v{} has been installed, restart Cap to finish updating.", - update.version - )) - .title("Update Cap") - .ok_button_label("Restart Now") - .cancel_button_label("Restart Later") - .show(|restart| { - tx.send(restart).ok(); - }); - - if rx.await.unwrap() { - app.restart(); - } - - Ok(()) -} diff --git a/apps/desktop/src/routes/(main)/index.tsx b/apps/desktop/src/routes/(main)/index.tsx index 8cccbb98..8dd04d1a 100644 --- a/apps/desktop/src/routes/(main)/index.tsx +++ b/apps/desktop/src/routes/(main)/index.tsx @@ -11,7 +11,7 @@ import { Select as KSelect } from "@kobalte/core/select"; import { SwitchTab, Button } from "@cap/ui-solid"; import { createMutation } from "@tanstack/solid-query"; import { createEventListener } from "@solid-primitives/event-listener"; -import { cache, createAsync, redirect } from "@solidjs/router"; +import { cache, createAsync, redirect, useNavigate } from "@solidjs/router"; import { createCameraForLabel, createCameras } from "../../utils/media"; import { @@ -83,6 +83,8 @@ export default function () { const audioDevicesData = createMemo(() => audioDevices.data ?? []); const camerasData = createMemo(() => cameras() ?? []); + createUpdateCheck(); + return (
@@ -370,3 +372,29 @@ export default function () {
); } + +import * as dialog from "@tauri-apps/plugin-dialog"; +import * as updater from "@tauri-apps/plugin-updater"; + +let hasChecked = false; +function createUpdateCheck() { + const navigate = useNavigate(); + + onMount(async () => { + if (hasChecked) return; + hasChecked = true; + + await new Promise((res) => setTimeout(res, 1000)); + + const update = await updater.check(); + if (!update) return; + + const shouldUpdate = await dialog.confirm( + `Version ${update.version} of Cap is available, would you like to install it?`, + { title: "Update Cap", okLabel: "Update", cancelLabel: "Ignore" } + ); + + if (!shouldUpdate) return; + navigate("/update"); + }); +} diff --git a/apps/desktop/src/routes/(main)/update.tsx b/apps/desktop/src/routes/(main)/update.tsx new file mode 100644 index 00000000..62a0b10d --- /dev/null +++ b/apps/desktop/src/routes/(main)/update.tsx @@ -0,0 +1,116 @@ +import { useNavigate } from "@solidjs/router"; +import { check } from "@tauri-apps/plugin-updater"; +import { + createResource, + createSignal, + Match, + onMount, + Show, + Switch, +} from "solid-js"; +import * as dialog from "@tauri-apps/plugin-dialog"; +import { relaunch } from "@tauri-apps/plugin-process"; +import { Button } from "@cap/ui-solid"; + +export default function () { + const navigate = useNavigate(); + + const [update] = createResource(async () => { + const update = await check(); + if (!update) return; + + return update; + }); + + return ( +
+ + {(update) => { + type UpdateStatus = + | { type: "downloading"; progress: number; contentLength?: number } + | { type: "done" }; + + const [updateStatus, updateStatusActions] = + createResource( + () => + new Promise((resolve) => { + update + .downloadAndInstall((e) => { + if (e.event === "Started") { + resolve({ + type: "downloading", + progress: 0, + contentLength: e.data.contentLength, + }); + } else if (e.event === "Progress") { + const status = updateStatus(); + if ( + !status || + status.type !== "downloading" || + status.contentLength === undefined + ) + return; + updateStatusActions.mutate({ + ...status, + progress: e.data.chunkLength + status.progress, + }); + } + }) + .then(async () => { + updateStatusActions.mutate({ type: "done" }); + }) + .catch(() => navigate("/")); + }) + ); + + return ( +
+ }> + + Update has been installed. Restart Cap to finish updating. +
+ + +
+
+ { + const s = updateStatus(); + if ( + s && + s.type === "downloading" && + s.contentLength !== undefined + ) + return s; + })()} + > + {(status) => ( + <> +

Installing Update

+ +
+
+
+ + )} + + +
+ ); + }} + +
+ ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b1febdf6..c858aa59 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,6 +113,9 @@ importers: '@tauri-apps/plugin-store': specifier: 2.0.0-rc.0 version: 2.0.0-rc.0 + '@tauri-apps/plugin-updater': + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 '@types/react-tooltip': specifier: ^4.2.4 version: 4.2.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -4864,11 +4867,11 @@ packages: react-dom: optional: true - '@storybook/builder-vite@8.3.0-beta.4': - resolution: {integrity: sha512-ngdtu1Dn9cq2GoK7RL0cTDxnRZ4altblAW7bX9U6WXHHAxP83mIDvbnm+7IDoYuSRwm9KgLf+k8YiarCZTBEHQ==} + '@storybook/builder-vite@8.4.0-alpha.0': + resolution: {integrity: sha512-gFeW+fuUN4s9rzGZAUZunU6n1+Ck7js29f6DRyUGSSwXxGwgwvqT8MdS5v1tqEdoKG+QwLNek1pTY6s6UugLdg==} peerDependencies: '@preact/preset-vite': '*' - storybook: ^8.3.0-beta.4 + storybook: ^8.4.0-alpha.0 typescript: '>= 4.3.x' vite: ^4.0.0 || ^5.0.0 vite-plugin-glimmerx: '*' @@ -4891,10 +4894,10 @@ packages: peerDependencies: storybook: ^8.2.9 - '@storybook/csf-plugin@8.3.0-beta.4': - resolution: {integrity: sha512-VgldiH9nC/P97KEnXyDkS63GxXlfaz7BWdnkqJk+mmnLCnxqJMckQ8fOdjuKOdmOSQMF8r1NjVpRHnMJeCpg3A==} + '@storybook/csf-plugin@8.4.0-alpha.0': + resolution: {integrity: sha512-BAhSUEBd+y314f2DEdSSPddpgzk9fTk2lXP205HGZESaP1bpBbMdIEqM6wC/BuX7Em/HZ8iZIhHqFKnX9XknIQ==} peerDependencies: - storybook: ^8.3.0-beta.4 + storybook: ^8.4.0-alpha.0 '@storybook/csf@0.1.11': resolution: {integrity: sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==} @@ -5139,6 +5142,9 @@ packages: '@tauri-apps/plugin-store@2.0.0-rc.0': resolution: {integrity: sha512-KqiEzq6EdRwxrl0/FwyNLwumDBM91xTchdu2a8vfkNub30GuP9z7RskP9ifVRI1gbxfa5TUDi0hKFk/SP7TANQ==} + '@tauri-apps/plugin-updater@2.0.0-rc.0': + resolution: {integrity: sha512-EKajf/sBpFif0cwXhTo3BmNvTZ2t2DDLRyhA8FFKugZNoOeqU97bHhPT5DIqMUPRE1tyDk9o7sXm8dKf7oz+EA==} + '@testing-library/dom@10.1.0': resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==} engines: {node: '>=18'} @@ -16806,9 +16812,9 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@storybook/builder-vite@8.3.0-beta.4(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2)(terser@5.31.6))': + '@storybook/builder-vite@8.4.0-alpha.0(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2)(terser@5.31.6))': dependencies: - '@storybook/csf-plugin': 8.3.0-beta.4(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@storybook/csf-plugin': 8.4.0-alpha.0(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10)) '@types/find-cache-dir': 3.2.1 browser-assert: 1.2.1 es-module-lexer: 1.5.4 @@ -16867,7 +16873,7 @@ snapshots: storybook: 8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10) unplugin: 1.12.1 - '@storybook/csf-plugin@8.3.0-beta.4(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + '@storybook/csf-plugin@8.4.0-alpha.0(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))': dependencies: storybook: 8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10) unplugin: 1.12.1 @@ -17094,6 +17100,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.0.0-rc.4 + '@tauri-apps/plugin-updater@2.0.0-rc.0': + dependencies: + '@tauri-apps/api': 2.0.0-rc.4 + '@testing-library/dom@10.1.0': dependencies: '@babel/code-frame': 7.24.7 @@ -23936,7 +23946,7 @@ snapshots: storybook-solidjs-vite@1.0.0-beta.2(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2)(terser@5.31.6)): dependencies: - '@storybook/builder-vite': 8.3.0-beta.4(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2)(terser@5.31.6)) + '@storybook/builder-vite': 8.4.0-alpha.0(storybook@8.2.9(@babel/preset-env@7.25.3(@babel/core@7.25.2))(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typescript@5.5.4)(vite@5.4.0(@types/node@20.14.2)(terser@5.31.6)) transitivePeerDependencies: - '@preact/preset-vite' - storybook