212 lines
6.4 KiB
Rust
212 lines
6.4 KiB
Rust
//! Tauri commands: thin wrappers over `Launcher` (see docs/CONTRACT.md).
|
|
|
|
use tauri::{AppHandle, State};
|
|
use tauri_plugin_dialog::DialogExt;
|
|
use tauri_plugin_opener::OpenerExt;
|
|
|
|
use crate::launcher::{Launcher, Market, Status};
|
|
use crate::market;
|
|
use crate::settings::Settings;
|
|
use crate::stack::StackEvent;
|
|
use crate::sync::Conflict;
|
|
|
|
type CmdResult<T> = Result<T, String>;
|
|
|
|
#[derive(serde::Serialize)]
|
|
pub struct SignedIn {
|
|
username: String,
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn release_plan(launcher: State<'_, Launcher>) -> CmdResult<crate::release::ReleasePlan> {
|
|
Ok(launcher.release_plan().await)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn release_run(
|
|
launcher: State<'_, Launcher>,
|
|
password: String,
|
|
with_api: bool,
|
|
with_mcp: bool,
|
|
with_web: bool,
|
|
) -> CmdResult<()> {
|
|
launcher.release_run(password, with_api, with_mcp, with_web).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn refresh_run(
|
|
launcher: State<'_, Launcher>,
|
|
password: String,
|
|
pg_password: String,
|
|
tile_root: String,
|
|
) -> CmdResult<()> {
|
|
launcher.refresh_run(password, pg_password, tile_root).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub fn release_console(launcher: State<'_, Launcher>, since: usize) -> CmdResult<crate::release::ConsoleTail> {
|
|
Ok(launcher.release_console(since))
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_status(launcher: State<'_, Launcher>) -> CmdResult<Status> {
|
|
Ok(launcher.status().await)
|
|
}
|
|
|
|
/// The marketplace: every tile set and source file of the version, required ones first. With
|
|
/// `drive`, what that drive can install instead of the DHP.
|
|
#[tauri::command]
|
|
pub async fn get_market(app: AppHandle, launcher: State<'_, Launcher>, drive: Option<String>) -> CmdResult<Market> {
|
|
launcher.market(&app, drive.map(std::path::PathBuf::from)).await
|
|
}
|
|
|
|
/// Choose the optional items (their keys) for an install about to run, without touching what
|
|
/// is installed. `drive` names the drive whose items these are.
|
|
#[tauri::command]
|
|
pub async fn set_market(
|
|
app: AppHandle,
|
|
launcher: State<'_, Launcher>,
|
|
selected: Vec<String>,
|
|
drive: Option<String>,
|
|
) -> CmdResult<()> {
|
|
launcher.choose_market(&app, selected, drive.map(std::path::PathBuf::from)).await
|
|
}
|
|
|
|
/// Choose the optional items (their keys) and apply the choice; see `Launcher::apply_market`.
|
|
#[tauri::command]
|
|
pub async fn finish_market(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.finish_market(&app).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn check_market(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Vec<market::Check>> {
|
|
launcher.check_market(&app).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn apply_market(app: AppHandle, launcher: State<'_, Launcher>, selected: Vec<String>) -> CmdResult<()> {
|
|
launcher.apply_market(&app, selected).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn refresh(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Status> {
|
|
Ok(launcher.refresh(&app).await)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn sign_in(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<SignedIn> {
|
|
launcher.sign_in(&app).await.map(|username| SignedIn { username })
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn cancel_sign_in(launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.cancel_sign_in();
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn sign_out(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Status> {
|
|
Ok(launcher.sign_out(&app).await)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_notes(launcher: State<'_, Launcher>, version: Option<String>) -> CmdResult<String> {
|
|
launcher.notes(version).await
|
|
}
|
|
|
|
/// Links in patch notes open in the system browser; the launcher window never navigates.
|
|
#[tauri::command]
|
|
pub async fn open_external(app: AppHandle, url: String) -> CmdResult<()> {
|
|
let parsed = url::Url::parse(&url).map_err(|_| "That link is not a valid address.".to_string())?;
|
|
if !matches!(parsed.scheme(), "http" | "https") {
|
|
return Err("Only web links can be opened.".to_string());
|
|
}
|
|
app
|
|
.opener()
|
|
.open_url(parsed.as_str(), None::<&str>)
|
|
.map_err(|e| format!("cannot open the browser: {e}"))
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn start_install(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.start_install(&app, None).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn start_install_from_drive(app: AppHandle, launcher: State<'_, Launcher>, path: String) -> CmdResult<()> {
|
|
launcher.start_install(&app, Some(std::path::PathBuf::from(path))).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn cancel_install(launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.cancel_install()
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn launch(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.launch(&app).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn stop(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.stop(&app).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_stack(launcher: State<'_, Launcher>) -> CmdResult<Vec<StackEvent>> {
|
|
Ok(launcher.stack_states())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn sync_tiles(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.sync_tiles(&app).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn sync_now(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
|
|
launcher.sync_now(&app).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_sync_conflicts(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Vec<Conflict>> {
|
|
launcher.sync_conflicts(&app).await
|
|
}
|
|
|
|
/// `choice`: "theirs", "mine", "value" (with `value`) or "dismiss" (refused edits only).
|
|
#[tauri::command]
|
|
pub async fn resolve_sync_conflict(
|
|
app: AppHandle,
|
|
launcher: State<'_, Launcher>,
|
|
tbl: String,
|
|
key: serde_json::Value,
|
|
choice: String,
|
|
value: Option<serde_json::Value>,
|
|
) -> CmdResult<Vec<Conflict>> {
|
|
launcher.resolve_sync_conflict(&app, tbl, key, choice, value).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_settings(launcher: State<'_, Launcher>) -> CmdResult<Settings> {
|
|
Ok(launcher.settings().await)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn set_settings(app: AppHandle, launcher: State<'_, Launcher>, settings: Settings) -> CmdResult<Status> {
|
|
launcher.set_settings(&app, settings).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn pick_folder(app: AppHandle) -> CmdResult<Option<String>> {
|
|
// The native dialog blocks its thread, so keep it off the async runtime's workers.
|
|
tauri::async_runtime::spawn_blocking(move || {
|
|
app
|
|
.dialog()
|
|
.file()
|
|
.blocking_pick_folder()
|
|
.and_then(|picked| picked.into_path().ok())
|
|
.map(|path| path.display().to_string())
|
|
})
|
|
.await
|
|
.map_err(|e| e.to_string())
|
|
}
|