Эх сурвалжийг харах

feat(api): Expose application metadata and functions to JS api - fix #1387 (#1445)

david 4 жил өмнө
parent
commit
e511d39910

+ 6 - 0
.changes/js-app-metadata.md

@@ -0,0 +1,6 @@
+---
+"tauri-api": minor
+"tauri": minor
+---
+
+Added new Javascript API to extract `name`, `version`, `tauri version` from the running application. We exposed `relaunch` and `exit` as well to control your application state.

+ 1 - 0
api/package.json

@@ -5,6 +5,7 @@
   "main": "./dist/index.js",
   "main": "./dist/index.js",
   "exports": {
   "exports": {
     ".": "./dist/index.js",
     ".": "./dist/index.js",
+    "./app": "./dist/app.js",
     "./cli": "./dist/cli.js",
     "./cli": "./dist/cli.js",
     "./dialog": "./dist/dialog.js",
     "./dialog": "./dist/dialog.js",
     "./event": "./dist/event.js",
     "./event": "./dist/event.js",

+ 1 - 0
api/rollup.config.js

@@ -10,6 +10,7 @@ import pkg from './package.json'
 export default [
 export default [
   {
   {
     input: {
     input: {
+      app: './src/app.ts',
       fs: './src/fs.ts',
       fs: './src/fs.ts',
       path: './src/path.ts',
       path: './src/path.ts',
       dialog: './src/dialog.ts',
       dialog: './src/dialog.ts',

+ 80 - 0
api/src/app.ts

@@ -0,0 +1,80 @@
+import { invokeTauriCommand } from './helpers/tauri'
+
+/**
+ * @name getVersion
+ * @description Get application version
+ * @returns {Promise<string>} Promise resolving to application version
+ */
+async function getVersion(): Promise<string> {
+  return invokeTauriCommand<string>({
+    __tauriModule: 'App',
+    mainThread: true,
+    message: {
+      cmd: 'getAppVersion'
+    }
+  })
+}
+
+/**
+ * @name getName
+ * @description Get application name
+ * @returns {Promise<string>} Promise resolving to application name
+ */
+async function getName(): Promise<string> {
+  return invokeTauriCommand<string>({
+    __tauriModule: 'App',
+    mainThread: true,
+    message: {
+      cmd: 'getAppName'
+    }
+  })
+}
+
+/**
+ * @name getTauriVersion
+ * @description Get tauri version
+ * @returns {Promise<string>} Promise resolving to tauri version
+ */
+async function getTauriVersion(): Promise<string> {
+  return invokeTauriCommand<string>({
+    __tauriModule: 'App',
+    mainThread: true,
+    message: {
+      cmd: 'getTauriVersion'
+    }
+  })
+}
+
+/**
+ * @name exit
+ * @description Exits immediately with exitCode.
+ * @param [exitCode] defaults to 0.
+ * @returns {Promise<void>} Application is closing, nothing is returned
+ */
+async function exit(exitCode: Number = 0): Promise<void> {
+  return invokeTauriCommand<string>({
+    __tauriModule: 'App',
+    mainThread: true,
+    message: {
+      cmd: 'exit',
+      exitCode
+    }
+  })
+}
+
+/**
+ * @name relaunch
+ * @description Relaunches the app when current instance exits.
+ * @returns {Promise<void>} Application is restarting, nothing is returned
+ */
+async function relaunch(): Promise<void> {
+  return invokeTauriCommand<string>({
+    __tauriModule: 'App',
+    mainThread: true,
+    message: {
+      cmd: 'relaunch'
+    }
+  })
+}
+
+export { getName, getVersion, getTauriVersion, relaunch, exit }

+ 2 - 0
api/src/bundle.ts

@@ -1,4 +1,5 @@
 import 'regenerator-runtime/runtime'
 import 'regenerator-runtime/runtime'
+import * as app from './app'
 import * as cli from './cli'
 import * as cli from './cli'
 import * as dialog from './dialog'
 import * as dialog from './dialog'
 import * as event from './event'
 import * as event from './event'
@@ -13,6 +14,7 @@ import * as notification from './notification'
 import * as globalShortcut from './globalShortcut'
 import * as globalShortcut from './globalShortcut'
 
 
 export {
 export {
+  app,
   cli,
   cli,
   dialog,
   dialog,
   event,
   event,

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
examples/api/public/build/bundle.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
examples/api/public/build/bundle.js.map


+ 15 - 2
examples/api/src/components/Updater.svelte

@@ -1,13 +1,25 @@
 <script>
 <script>
-
+  import { onMount, onDestroy } from "svelte";
+  
   // This example show how updater events work when dialog is disabled.
   // This example show how updater events work when dialog is disabled.
   // This allow you to use custom dialog for the updater.
   // This allow you to use custom dialog for the updater.
   // This is your responsability to restart the application after you receive the STATUS: DONE.
   // This is your responsability to restart the application after you receive the STATUS: DONE.
 
 
   import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
   import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
+  import { listen } from "@tauri-apps/api/event";
+  import { relaunch } from "@tauri-apps/api/app";
 
 
   export let onMessage;
   export let onMessage;
-
+  let unlisten;
+
+  onMount(async () => {
+    unlisten = await listen("tauri://update-status", onMessage)
+  })
+  onDestroy(() => {
+    if (unlisten) {
+      unlisten()
+    }
+  })
 
 
   async function check() {
   async function check() {
     try {
     try {
@@ -31,6 +43,7 @@
 
 
       await installUpdate();
       await installUpdate();
       onMessage("Installation complete, restart required.");
       onMessage("Installation complete, restart required.");
+      await relaunch();
 
 
     } catch(e) {
     } catch(e) {
       onMessage(e);
       onMessage(e);

+ 25 - 0
examples/api/src/components/Welcome.svelte

@@ -1,6 +1,31 @@
+<script>
+  import { getName, getVersion, getTauriVersion, relaunch, exit } from "@tauri-apps/api/app";
+  let version = 0.0;
+  let tauriVersion = 0.0;
+  let appName = 'Unknown';
+
+  getName().then(n => {appName = n});
+  getVersion().then(v => {version = v});
+  getTauriVersion().then(v => {tauriVersion = v});
+
+  async function closeApp() {
+    await exit();
+  }
+
+  async function relaunchApp() {
+    await relaunch();
+  }
+</script>
 <h1>Welcome</h1>
 <h1>Welcome</h1>
 <p>
 <p>
   Tauri's API capabilities using the ` @tauri-apps/api ` package. It's used as
   Tauri's API capabilities using the ` @tauri-apps/api ` package. It's used as
   the main validation app, serving as the testbed of our development process. In
   the main validation app, serving as the testbed of our development process. In
   the future, this app will be used on Tauri's integration tests.
   the future, this app will be used on Tauri's integration tests.
 </p>
 </p>
+
+<p>Current App version: {version}</p>
+<p>Current Tauri version: {tauriVersion}</p>
+<p>Current App name: {appName}</p>
+
+<button class="button" on:click={closeApp}>Close application</button>
+<button class="button" on:click={relaunchApp}>Relaunch application</button>

+ 43 - 0
tauri-api/src/app.rs

@@ -0,0 +1,43 @@
+use std::{
+  env,
+  path::PathBuf,
+  process::{exit, Command},
+};
+
+/// Get the current binary
+pub fn current_binary() -> Option<PathBuf> {
+  let mut current_binary = None;
+
+  // if we are running with an APP Image, we should return the app image path
+  #[cfg(target_os = "linux")]
+  if let Some(app_image_path) = env::var_os("APPIMAGE") {
+    current_binary = Some(PathBuf::from(app_image_path));
+  }
+
+  // if we didn't extracted binary in previous step,
+  // let use the current_exe from current environment
+  if current_binary.is_none() {
+    if let Ok(current_process) = env::current_exe() {
+      current_binary = Some(current_process);
+    }
+  }
+
+  current_binary
+}
+
+/// Restart application
+pub fn restart_application(binary_to_start: Option<PathBuf>) {
+  let mut binary_path = binary_to_start;
+  // spawn new process
+  if binary_path.is_none() {
+    binary_path = current_binary();
+  }
+
+  if let Some(path) = binary_path {
+    Command::new(path)
+      .spawn()
+      .expect("application failed to start");
+  }
+
+  exit(0);
+}

+ 2 - 0
tauri-api/src/lib.rs

@@ -1,6 +1,8 @@
 //! The Tauri API interface.
 //! The Tauri API interface.
 #![warn(missing_docs, rust_2018_idioms)]
 #![warn(missing_docs, rust_2018_idioms)]
 
 
+/// The App API module allows you to manage application processes.
+pub mod app;
 /// The Command API module allows you to manage child processes.
 /// The Command API module allows you to manage child processes.
 pub mod command;
 pub mod command;
 /// The Dialog API module allows you to show messages and prompt for file paths.
 /// The Dialog API module allows you to show messages and prompt for file paths.

+ 0 - 22
tauri-updater/src/lib.rs

@@ -239,23 +239,6 @@ impl<'a> UpdateBuilder<'a> {
     // Get the extract_path from the provided executable_path
     // Get the extract_path from the provided executable_path
     let extract_path = extract_path_from_executable(&executable_path);
     let extract_path = extract_path_from_executable(&executable_path);
 
 
-    // current binary used to restart the application
-    #[cfg(target_os = "windows")]
-    let current_binary = None;
-
-    #[cfg(not(target_os = "windows"))]
-    let mut current_binary = None;
-
-    #[cfg(target_os = "linux")]
-    if let Some(app_image_path) = env::var_os("APPIMAGE") {
-      current_binary = Some(PathBuf::from(app_image_path));
-    }
-
-    #[cfg(target_os = "macos")]
-    if let Ok(current_process) = std::env::current_exe() {
-      current_binary = Some(current_process);
-    }
-
     // Set SSL certs for linux if they aren't available.
     // Set SSL certs for linux if they aren't available.
     // We do not require to recheck in the download_and_install as we use
     // We do not require to recheck in the download_and_install as we use
     // ENV variables, we can expect them to be set for the second call.
     // ENV variables, we can expect them to be set for the second call.
@@ -351,7 +334,6 @@ impl<'a> UpdateBuilder<'a> {
       download_url: final_release.download_url,
       download_url: final_release.download_url,
       body: final_release.body,
       body: final_release.body,
       signature: final_release.signature,
       signature: final_release.signature,
-      current_binary,
     })
     })
   }
   }
 }
 }
@@ -372,10 +354,6 @@ pub struct Update {
   pub current_version: String,
   pub current_version: String,
   /// Update publish date
   /// Update publish date
   pub date: String,
   pub date: String,
-  /// If announced, this is the process to start once the
-  /// update is completed. On linux it return the APPImage Path and macos
-  /// the current executable path. On windows we exit before this step.
-  pub current_binary: Option<PathBuf>,
   /// Target
   /// Target
   target: String,
   target: String,
   /// Extract path
   /// Extract path

+ 21 - 5
tauri/src/endpoints.rs

@@ -1,7 +1,11 @@
-use crate::{api::config::Config, hooks::InvokeMessage, runtime::Params};
+use crate::{
+  api::{config::Config, PackageInfo},
+  hooks::InvokeMessage,
+  runtime::Params,
+};
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 use serde_json::Value as JsonValue;
 use serde_json::Value as JsonValue;
-
+mod app;
 mod cli;
 mod cli;
 mod dialog;
 mod dialog;
 mod event;
 mod event;
@@ -30,6 +34,7 @@ impl<T: Serialize> From<T> for InvokeResponse {
 #[derive(Deserialize)]
 #[derive(Deserialize)]
 #[serde(tag = "module", content = "message")]
 #[serde(tag = "module", content = "message")]
 enum Module {
 enum Module {
+  App(app::Cmd),
   Fs(file_system::Cmd),
   Fs(file_system::Cmd),
   Window(Box<window::Cmd>),
   Window(Box<window::Cmd>),
   Shell(shell::Cmd),
   Shell(shell::Cmd),
@@ -43,9 +48,15 @@ enum Module {
 }
 }
 
 
 impl Module {
 impl Module {
-  fn run<M: Params>(self, message: InvokeMessage<M>, config: &Config) {
+  fn run<M: Params>(self, message: InvokeMessage<M>, config: &Config, package_info: PackageInfo) {
     let window = message.window();
     let window = message.window();
     match self {
     match self {
+      Self::App(cmd) => message.respond_async(async move {
+        cmd
+          .run(package_info)
+          .and_then(|r| r.json)
+          .map_err(|e| e.to_string())
+      }),
       Self::Fs(cmd) => message
       Self::Fs(cmd) => message
         .respond_async(async move { cmd.run().and_then(|r| r.json).map_err(|e| e.to_string()) }),
         .respond_async(async move { cmd.run().and_then(|r| r.json).map_err(|e| e.to_string()) }),
       Self::Window(cmd) => message.respond_async(async move {
       Self::Window(cmd) => message.respond_async(async move {
@@ -111,13 +122,18 @@ impl Module {
   }
   }
 }
 }
 
 
-pub(crate) fn handle<M: Params>(module: String, message: InvokeMessage<M>, config: &Config) {
+pub(crate) fn handle<M: Params>(
+  module: String,
+  message: InvokeMessage<M>,
+  config: &Config,
+  package_info: &PackageInfo,
+) {
   let mut payload = message.payload();
   let mut payload = message.payload();
   if let JsonValue::Object(ref mut obj) = payload {
   if let JsonValue::Object(ref mut obj) = payload {
     obj.insert("module".to_string(), JsonValue::String(module));
     obj.insert("module".to_string(), JsonValue::String(module));
   }
   }
   match serde_json::from_value::<Module>(payload) {
   match serde_json::from_value::<Module>(payload) {
-    Ok(module) => module.run(message, config),
+    Ok(module) => module.run(message, config, package_info.clone()),
     Err(e) => message.reject(e.to_string()),
     Err(e) => message.reject(e.to_string()),
   }
   }
 }
 }

+ 42 - 0
tauri/src/endpoints/app.rs

@@ -0,0 +1,42 @@
+use std::process::exit;
+
+use super::InvokeResponse;
+use crate::api::{app::restart_application, PackageInfo};
+use serde::Deserialize;
+
+/// The API descriptor.
+#[derive(Deserialize)]
+#[serde(tag = "cmd", rename_all = "camelCase")]
+pub enum Cmd {
+  /// Get Application Version
+  GetAppVersion,
+  /// Get Application Name
+  GetAppName,
+  /// Get Tauri Version
+  GetTauriVersion,
+  /// Relaunch application
+  Relaunch,
+  /// Close application with provided exit_code
+  #[serde(rename_all = "camelCase")]
+  Exit { exit_code: i32 },
+}
+
+impl Cmd {
+  pub fn run(self, package_info: PackageInfo) -> crate::Result<InvokeResponse> {
+    match self {
+      Self::GetAppVersion => Ok(package_info.version.into()),
+      Self::GetAppName => Ok(package_info.name.into()),
+      Self::GetTauriVersion => Ok(env!("CARGO_PKG_VERSION").into()),
+      Self::Relaunch => Ok({
+        restart_application(None);
+        ().into()
+      }),
+      Self::Exit { exit_code } => {
+        // would be great if we can have a handler inside tauri
+        // who close all window and emit an event that user can catch
+        // if they want to process something before closing the app
+        exit(exit_code);
+      }
+    }
+  }
+}

+ 2 - 20
tauri/src/runtime/updater.rs

@@ -1,14 +1,11 @@
 use crate::{
 use crate::{
   api::{
   api::{
+    app::restart_application,
     config::UpdaterConfig,
     config::UpdaterConfig,
     dialog::{ask, AskResponse},
     dialog::{ask, AskResponse},
   },
   },
   runtime::{window::Window, Params},
   runtime::{window::Window, Params},
 };
 };
-use std::{
-  path::PathBuf,
-  process::{exit, Command},
-};
 
 
 // Check for new updates
 // Check for new updates
 pub const EVENT_CHECK_UPDATE: &str = "tauri://update";
 pub const EVENT_CHECK_UPDATE: &str = "tauri://update";
@@ -181,8 +178,6 @@ pub(crate) fn listener<M: Params>(
                       );
                       );
                     } else {
                     } else {
                       // emit {"status": "DONE"}
                       // emit {"status": "DONE"}
-                      // todo(lemarier): maybe we should emit the
-                      // path of the current EXE so they can restart it
                       send_status_update(window.clone(), EVENT_STATUS_SUCCESS, None);
                       send_status_update(window.clone(), EVENT_STATUS_SUCCESS, None);
                     }
                     }
                   })
                   })
@@ -253,7 +248,7 @@ Release Notes:
       );
       );
       match should_exit {
       match should_exit {
         AskResponse::Yes => {
         AskResponse::Yes => {
-          restart_application(updater.current_binary.as_ref());
+          restart_application(None);
           // safely exit even if the process
           // safely exit even if the process
           // should be killed
           // should be killed
           return Ok(());
           return Ok(());
@@ -270,16 +265,3 @@ Release Notes:
 
 
   Ok(())
   Ok(())
 }
 }
-
-// Tested on macOS and Linux. Windows will not trigger the dialog
-// as it'll exit before, to launch the MSI installation.
-fn restart_application(binary_to_start: Option<&PathBuf>) {
-  // spawn new process
-  if let Some(path) = binary_to_start {
-    Command::new(path)
-      .spawn()
-      .expect("application failed to start");
-  }
-
-  exit(0);
-}

+ 1 - 1
tauri/src/runtime/window.rs

@@ -151,7 +151,7 @@ impl<M: Params> Window<M> {
       let message = InvokeMessage::new(self, command.to_string(), payload);
       let message = InvokeMessage::new(self, command.to_string(), payload);
       if let Some(module) = &message.payload.tauri_module {
       if let Some(module) = &message.payload.tauri_module {
         let module = module.to_string();
         let module = module.to_string();
-        crate::endpoints::handle(module, message, manager.config());
+        crate::endpoints::handle(module, message, manager.config(), manager.package_info());
       } else if command.starts_with("plugin:") {
       } else if command.starts_with("plugin:") {
         manager.extend_api(command, message);
         manager.extend_api(command, message);
       } else {
       } else {

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно