From 893a6d06311a14cfcf72e2ab6a465f1010288a15 Mon Sep 17 00:00:00 2001 From: Max Coppen <44031065+mxcop@users.noreply.github.com> Date: Tue, 1 Nov 2022 21:35:10 +0100 Subject: [PATCH] wip: updater bindgen setup --- build.rs | 3 +- dist/updater.js | 185 ++++++++++++++++++++++++++++++++++++++++++++++++ src/updater.rs | 40 +++++++++++ 3 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 dist/updater.js create mode 100644 src/updater.rs diff --git a/build.rs b/build.rs index f0eae84..c7d20bc 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,7 @@ use std::process::Command; fn main() { /* Shared arguments */ - let sargs: [&str; 8] = [ + let sargs: [&str; 9] = [ "--outdir=dist", "--format=esm", "--bundle", @@ -11,6 +11,7 @@ fn main() { "tauri/tooling/api/src/tauri.ts", "tauri/tooling/api/src/event.ts", "tauri/tooling/api/src/mocks.ts", + "tauri/tooling/api/src/updater.ts", ]; if cfg!(windows) { diff --git a/dist/updater.js b/dist/updater.js new file mode 100644 index 0000000..43fb58a --- /dev/null +++ b/dist/updater.js @@ -0,0 +1,185 @@ +// tauri/tooling/api/src/tauri.ts +function uid() { + return window.crypto.getRandomValues(new Uint32Array(1))[0]; +} +function transformCallback(callback, once3 = false) { + const identifier = uid(); + const prop = `_${identifier}`; + Object.defineProperty(window, prop, { + value: (result) => { + if (once3) { + Reflect.deleteProperty(window, prop); + } + return callback?.(result); + }, + writable: false, + configurable: true + }); + return identifier; +} +async function invoke(cmd, args = {}) { + return new Promise((resolve, reject) => { + const callback = transformCallback((e) => { + resolve(e); + Reflect.deleteProperty(window, `_${error}`); + }, true); + const error = transformCallback((e) => { + reject(e); + Reflect.deleteProperty(window, `_${callback}`); + }, true); + window.__TAURI_IPC__({ + cmd, + callback, + error, + ...args + }); + }); +} + +// tauri/tooling/api/src/helpers/tauri.ts +async function invokeTauriCommand(command) { + return invoke("tauri", command); +} + +// tauri/tooling/api/src/helpers/event.ts +async function _unlisten(event, eventId) { + return invokeTauriCommand({ + __tauriModule: "Event", + message: { + cmd: "unlisten", + event, + eventId + } + }); +} +async function emit(event, windowLabel, payload) { + await invokeTauriCommand({ + __tauriModule: "Event", + message: { + cmd: "emit", + event, + windowLabel, + payload + } + }); +} +async function listen(event, windowLabel, handler) { + return invokeTauriCommand({ + __tauriModule: "Event", + message: { + cmd: "listen", + event, + windowLabel, + handler: transformCallback(handler) + } + }).then((eventId) => { + return async () => _unlisten(event, eventId); + }); +} +async function once(event, windowLabel, handler) { + return listen(event, windowLabel, (eventData) => { + handler(eventData); + _unlisten(event, eventData.id).catch(() => { + }); + }); +} + +// tauri/tooling/api/src/event.ts +async function listen2(event, handler) { + return listen(event, null, handler); +} +async function once2(event, handler) { + return once(event, null, handler); +} +async function emit2(event, payload) { + return emit(event, void 0, payload); +} + +// tauri/tooling/api/src/updater.ts +async function onUpdaterEvent(handler) { + return listen2("tauri://update-status" /* STATUS_UPDATE */, (data) => { + handler(data?.payload); + }); +} +async function installUpdate() { + let unlistenerFn; + function cleanListener() { + if (unlistenerFn) { + unlistenerFn(); + } + unlistenerFn = void 0; + } + return new Promise((resolve, reject) => { + function onStatusChange(statusResult) { + if (statusResult.error) { + cleanListener(); + return reject(statusResult.error); + } + if (statusResult.status === "DONE") { + cleanListener(); + return resolve(); + } + } + onUpdaterEvent(onStatusChange).then((fn) => { + unlistenerFn = fn; + }).catch((e) => { + cleanListener(); + throw e; + }); + emit2("tauri://update-install" /* INSTALL_UPDATE */).catch((e) => { + cleanListener(); + throw e; + }); + }); +} +async function checkUpdate() { + let unlistenerFn; + function cleanListener() { + if (unlistenerFn) { + unlistenerFn(); + } + unlistenerFn = void 0; + } + return new Promise((resolve, reject) => { + function onUpdateAvailable(manifest) { + cleanListener(); + return resolve({ + manifest, + shouldUpdate: true + }); + } + function onStatusChange(statusResult) { + if (statusResult.error) { + cleanListener(); + return reject(statusResult.error); + } + if (statusResult.status === "UPTODATE") { + cleanListener(); + return resolve({ + shouldUpdate: false + }); + } + } + once2("tauri://update-available" /* UPDATE_AVAILABLE */, (data) => { + onUpdateAvailable(data?.payload); + }).catch((e) => { + cleanListener(); + throw e; + }); + onUpdaterEvent(onStatusChange).then((fn) => { + unlistenerFn = fn; + }).catch((e) => { + cleanListener(); + throw e; + }); + emit2("tauri://update" /* CHECK_UPDATE */).catch((e) => { + cleanListener(); + throw e; + }); + }); +} +export { + checkUpdate, + installUpdate, + onUpdaterEvent +}; diff --git a/src/updater.rs b/src/updater.rs new file mode 100644 index 0000000..5ce159b --- /dev/null +++ b/src/updater.rs @@ -0,0 +1,40 @@ +/// Install the update if there's one available. +/// +/// # Example +/// +/// ```rust,no_run +/// use tauri_api::updater::install_update; +/// +/// install_update().await; +/// ``` +#[inline(always)] +pub async fn install_update() { + inner::installUpdate().await +} + +/// Checks if an update is available. +/// +/// # Example +/// +/// ```rust,no_run +/// use tauri_api::clipboard::{write_text, read_text}; +/// +/// write_text("Tauri is awesome!").await; +/// assert_eq!(read_text().await, "Tauri is awesome!"); +/// ``` +/// +/// @returns A promise indicating the success or failure of the operation. +#[inline(always)] +pub async fn check_update() { + inner::checkUpdate().await +} + +mod inner { + use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; + + #[wasm_bindgen(module = "/dist/updater.js")] + extern "C" { + pub async fn installUpdate(); + pub async fn checkUpdate(); + } +}