refactor(updater): migrate to tauri's new resource table (#899)

* refactor(updater): migrate to tauri's new resource table

* fmt

* fmt

* Create updater-js-started-event.md
pull/912/head
Amr Bashir 1 year ago committed by GitHub
parent 8505a756b5
commit 0879a87a7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,6 @@
---
"updater": "patch"
"updater-js": "patch"
---
Fix `Started` event not emitted to JS when downloading update.

@ -0,0 +1,5 @@
---
"updater": "patch"
---
**Breaking change**: Changed `Update::download` and `Update::download_and_install` first argument to take `FnMut` instead of just `Fn`.

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import { invoke, Channel } from "@tauri-apps/api/core";
import { invoke, Channel, Resource } from "@tauri-apps/api/core";
/** Options used to check for updates */
interface CheckOptions {
@ -25,6 +25,7 @@ interface CheckOptions {
}
interface UpdateMetadata {
rid: number;
available: boolean;
currentVersion: string;
version: string;
@ -38,7 +39,7 @@ type DownloadEvent =
| { event: "Progress"; data: { chunkLength: number } }
| { event: "Finished" };
class Update {
class Update extends Resource {
available: boolean;
currentVersion: string;
version: string;
@ -46,6 +47,7 @@ class Update {
body?: string;
constructor(metadata: UpdateMetadata) {
super(metadata.rid);
this.available = metadata.available;
this.currentVersion = metadata.currentVersion;
this.version = metadata.version;
@ -58,11 +60,12 @@ class Update {
onEvent?: (progress: DownloadEvent) => void,
): Promise<void> {
const channel = new Channel<DownloadEvent>();
if (onEvent != null) {
if (onEvent) {
channel.onmessage = onEvent;
}
return invoke("plugin:updater|download_and_install", {
onEvent: channel,
rid: this.rid,
});
}
}
@ -73,9 +76,9 @@ async function check(options?: CheckOptions): Promise<Update | null> {
options.headers = Array.from(new Headers(options.headers).entries());
}
return invoke<UpdateMetadata>("plugin:updater|check", { ...options }).then(
(meta) => (meta.available ? new Update(meta) : null),
);
return invoke<UpdateMetadata>("plugin:updater|check", {
...options,
}).then((meta) => (meta.available ? new Update(meta) : null));
}
export type { CheckOptions, DownloadEvent };

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(e){"use strict";function t(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}var r;"function"==typeof SuppressedError&&SuppressedError;class n{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,r.set(this,(()=>{})),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((e=>{t(this,r,"f").call(this,e)}))}set onmessage(e){!function(e,t,r,n,a){if("m"===n)throw new TypeError("Private method is not writable");if("a"===n&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===n?a.call(e,r):a?a.value=r:t.set(e,r)}(this,r,e,"f")}get onmessage(){return t(this,r,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}r=new WeakMap;class o{constructor(e){this.available=e.available,this.currentVersion=e.currentVersion,this.version=e.version,this.date=e.date,this.body=e.body}async downloadAndInstall(e){const t=new n;return null!=e&&(t.onmessage=e),a("plugin:updater|download_and_install",{onEvent:t})}}return e.Update=o,e.check=async function(e){return e?.headers&&(e.headers=Array.from(new Headers(e.headers).entries())),a("plugin:updater|check",{...e}).then((e=>e.available?new o(e):null))},e}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}
if("__TAURI__"in window){var __TAURI_PLUGIN_UPDATER__=function(e){"use strict";function r(e,r,t,n){if("a"===t&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof r?e!==r||!n:!r.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===t?n:"a"===t?n.call(e):n?n.value:r.get(e)}function t(e,r,t,n,s){if("m"===n)throw new TypeError("Private method is not writable");if("a"===n&&!s)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof r?e!==r||!s:!r.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===n?s.call(e,t):s?s.value=t:r.set(e,t),t}var n,s;"function"==typeof SuppressedError&&SuppressedError;class i{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),this.id=function(e,r=!1){return window.__TAURI_INTERNALS__.transformCallback(e,r)}((e=>{r(this,n,"f").call(this,e)}))}set onmessage(e){t(this,n,e,"f")}get onmessage(){return r(this,n,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function a(e,r={},t){return window.__TAURI_INTERNALS__.invoke(e,r,t)}n=new WeakMap;class o{get rid(){return r(this,s,"f")}constructor(e){s.set(this,void 0),t(this,s,e,"f")}async close(){return a("plugin:resources|close",{rid:this.rid})}}s=new WeakMap;class c extends o{constructor(e){super(e.rid),this.available=e.available,this.currentVersion=e.currentVersion,this.version=e.version,this.date=e.date,this.body=e.body}async downloadAndInstall(e){const r=new i;return e&&(r.onmessage=e),a("plugin:updater|download_and_install",{onEvent:r,rid:this.rid})}}return e.Update=c,e.check=async function(e){return e?.headers&&(e.headers=Array.from(new Headers(e.headers).entries())),a("plugin:updater|check",{...e}).then((e=>e.available?new c(e):null))},e}({});Object.defineProperty(window.__TAURI__,"updater",{value:__TAURI_PLUGIN_UPDATER__})}

@ -2,15 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{PendingUpdate, Result, UpdaterExt};
use crate::{Result, Update, UpdaterExt};
use serde::Serialize;
use tauri::{ipc::Channel, AppHandle, Runtime, State};
use tauri::{ipc::Channel, AppHandle, Manager, ResourceId, Runtime};
use std::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use std::time::Duration;
use url::Url;
#[derive(Debug, Serialize)]
@ -30,6 +27,7 @@ pub enum DownloadEvent {
#[derive(Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Metadata {
rid: Option<ResourceId>,
available: bool,
current_version: String,
version: String,
@ -40,7 +38,6 @@ pub(crate) struct Metadata {
#[tauri::command]
pub(crate) async fn check<R: Runtime>(
app: AppHandle<R>,
pending: State<'_, PendingUpdate>,
headers: Option<Vec<(String, String)>>,
timeout: Option<u64>,
proxy: Option<String>,
@ -72,7 +69,7 @@ pub(crate) async fn check<R: Runtime>(
metadata.version = update.version.clone();
metadata.date = update.date.map(|d| d.to_string());
metadata.body = update.body.clone();
pending.0.lock().await.replace(update);
metadata.rid = Some(app.resources_table().add(update));
}
Ok(metadata)
@ -80,30 +77,28 @@ pub(crate) async fn check<R: Runtime>(
#[tauri::command]
pub(crate) async fn download_and_install<R: Runtime>(
_app: AppHandle<R>,
pending: State<'_, PendingUpdate>,
app: AppHandle<R>,
rid: ResourceId,
on_event: Channel,
) -> Result<()> {
if let Some(pending) = &*pending.0.lock().await {
let first_chunk = AtomicBool::new(false);
let on_event_c = on_event.clone();
pending
.download_and_install(
move |chunk_length, content_length| {
if first_chunk.swap(false, Ordering::Acquire) {
on_event
.send(DownloadEvent::Started { content_length })
.unwrap();
}
on_event
.send(DownloadEvent::Progress { chunk_length })
.unwrap();
},
move || {
on_event_c.send(&DownloadEvent::Finished).unwrap();
},
)
.await?;
}
let update = app.resources_table().get::<Update>(rid)?;
let mut first_chunk = true;
update
.download_and_install(
|chunk_length, content_length| {
if first_chunk {
first_chunk = !first_chunk;
let _ = on_event.send(DownloadEvent::Started { content_length });
}
let _ = on_event.send(DownloadEvent::Progress { chunk_length });
},
|| {
let _ = on_event.send(&DownloadEvent::Finished);
},
)
.await?;
Ok(())
}

@ -66,6 +66,8 @@ pub enum Error {
BinaryNotFoundInArchive,
#[error(transparent)]
Http(#[from] http::Error),
#[error(transparent)]
Tauri(#[from] tauri::Error),
}
impl Serialize for Error {

@ -14,7 +14,6 @@
)]
use tauri::{
async_runtime::Mutex,
plugin::{Builder as PluginBuilder, TauriPlugin},
Manager, Runtime,
};
@ -28,8 +27,6 @@ pub use config::Config;
pub use error::{Error, Result};
pub use updater::*;
struct PendingUpdate(Mutex<Option<Update>>);
/// Extension trait to use the updater on [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`].
pub trait UpdaterExt<R: Runtime> {
/// Gets the updater builder to build and updater
@ -145,7 +142,6 @@ impl Builder {
config.installer_args = installer_args;
}
app.manage(UpdaterState { target, config });
app.manage(PendingUpdate(Default::default()));
Ok(())
})
.invoke_handler(tauri::generate_handler![

@ -20,7 +20,10 @@ use reqwest::{
};
use semver::Version;
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};
use tauri::utils::{config::UpdaterConfig, platform::current_exe};
use tauri::{
utils::{config::UpdaterConfig, platform::current_exe},
Resource,
};
use time::OffsetDateTime;
use url::Url;
@ -385,13 +388,15 @@ pub struct Update {
pub headers: HeaderMap,
}
impl Resource for Update {}
impl Update {
/// Downloads the updater package, verifies it then return it as bytes.
///
/// Use [`Update::install`] to install it
pub async fn download<C: Fn(usize, Option<u64>), D: FnOnce()>(
pub async fn download<C: FnMut(usize, Option<u64>), D: FnOnce()>(
&self,
on_chunk: C,
mut on_chunk: C,
on_download_finish: D,
) -> Result<Vec<u8>> {
// set our headers
@ -458,7 +463,7 @@ impl Update {
}
/// Downloads and installs the updater package
pub async fn download_and_install<C: Fn(usize, Option<u64>), D: FnOnce()>(
pub async fn download_and_install<C: FnMut(usize, Option<u64>), D: FnOnce()>(
&self,
on_chunk: C,
on_download_finish: D,

Loading…
Cancel
Save