// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use anki_proto::generic; use anki_proto::launcher::get_langs_response; use anki_proto::launcher::get_mirrors_response; use anki_proto::launcher::ChooseVersionRequest; use anki_proto::launcher::ChooseVersionResponse; use anki_proto::launcher::GetLangsResponse; use anki_proto::launcher::GetMirrorsResponse; use anki_proto::launcher::GetVersionsResponse; use anki_proto::launcher::I18nResourcesRequest; use anki_proto::launcher::Mirror; use anki_proto::launcher::Options; use anki_proto::launcher::ZoomWebviewRequest; use anyhow::Context; use anyhow::Result; use strum::IntoEnumIterator; use tauri::AppHandle; use tauri::Manager; use tauri::Runtime; use tauri::WebviewWindow; use tauri_plugin_os::locale; use crate::lang::get_tr; use crate::lang::setup_i18n; use crate::lang::LANGS; use crate::lang::LANGS_DEFAULT_REGION; use crate::lang::LANGS_WITH_REGIONS; use crate::uv; pub async fn i18n_resources( app: AppHandle, _window: WebviewWindow, input: I18nResourcesRequest, ) -> Result { let tr = get_tr(&app)?; serde_json::to_vec(&tr.resources_for_js(&input.modules)) .with_context(|| "failed to serialise i18n resources") .map(Into::into) } pub async fn get_langs( _app: AppHandle, _window: WebviewWindow, ) -> Result { let langs = LANGS .into_iter() .map(|(locale, name)| get_langs_response::Pair { name: name.to_string(), locale: locale.to_string(), }) .collect(); let user_locale = locale() .and_then(|l| { if LANGS.contains_key(&l) { Some(l) } else { LANGS_DEFAULT_REGION .get(l.split('-').next().unwrap()) .or_else(|| LANGS_DEFAULT_REGION.get("en")) .map(ToString::to_string) } }) .unwrap(); Ok(GetLangsResponse { user_locale, langs }) } pub async fn set_lang( app: AppHandle, _window: WebviewWindow, input: generic::String, ) -> Result<()> { // python's lang_to_disk_lang let input = input.val; let input = if LANGS_WITH_REGIONS.contains(input.as_str()) { input } else { input.split('-').next().unwrap().to_owned() }; setup_i18n(&app, &[&*input]); Ok(()) } pub async fn get_mirrors( app: AppHandle, _window: WebviewWindow, ) -> Result { let tr = get_tr(&app)?; Ok(GetMirrorsResponse { mirrors: Mirror::iter() .map(|mirror| get_mirrors_response::Pair { mirror: mirror.into(), name: match mirror { Mirror::Disabled => tr.launcher_mirror_no_mirror(), Mirror::China => tr.launcher_mirror_china(), } .into(), }) .collect(), }) } pub async fn get_options( app: AppHandle, _window: WebviewWindow, ) -> Result { let state = app.state::(); let allow_betas = state.prerelease_marker.exists(); let download_caching = !state.no_cache_marker.exists(); let mirror = if state.mirror_path.exists() { Mirror::China } else { Mirror::Disabled } .into(); Ok(Options { allow_betas, download_caching, mirror, }) } pub async fn get_versions( app: AppHandle, _window: WebviewWindow, ) -> Result { let state = (*app.state::()).clone(); // TODO: why... let mut state1 = state.clone(); let releases_fut = tauri::async_runtime::spawn_blocking(move || uv::get_releases(&state)); let check_fut = tauri::async_runtime::spawn_blocking(move || uv::check_versions(&mut state1)); let (releases, check) = futures::future::join(releases_fut, check_fut).await; // TODO: handle errors properly let uv::Releases { latest, all } = releases.unwrap().unwrap(); let (current, previous) = check.unwrap().unwrap(); Ok(GetVersionsResponse { latest, all, current, previous, }) } pub async fn choose_version( app: AppHandle, _window: WebviewWindow, input: ChooseVersionRequest, ) -> Result { let state = (*app.state::()).clone(); let version = input.version.clone(); tauri::async_runtime::spawn_blocking(move || -> Result<()> { if let Some(options) = input.options { uv::set_allow_betas(&state, options.allow_betas)?; uv::set_cache_enabled(&state, options.download_caching)?; uv::set_mirror(&state, options.mirror != Mirror::Disabled as i32)?; } if !input.keep_existing || state.pyproject_modified_by_user { // install or resync let res = uv::handle_version_install_or_update( app.clone(), &state, &input.version, input.keep_existing, ); println!("handle_version_install_or_update: {res:?}"); res?; } uv::post_install(&state)?; // TODO: show some sort of notification before closing // if let Some(window) = app.get_webview_window("main") { // let _ = window.destroy(); // } // // app.exit can't be called from the main thread // app.exit(0); Ok(()) }) .await??; Ok(ChooseVersionResponse { version }) } /// NOTE: [zoomHotkeysEnabled](https://v2.tauri.app/reference/config/#zoomhotkeysenabled) exists /// but the polyfill it uses on lin doesn't allow regular scrolling pub async fn zoom_webview( _app: AppHandle, window: WebviewWindow, input: ZoomWebviewRequest, ) -> Result<()> { let factor = input.scale_factor.into(); // NOTE: not supported on windows let _ = window.set_zoom(factor); Ok(()) } pub async fn window_ready(_app: AppHandle, window: WebviewWindow) -> Result<()> { window .show() .with_context(|| format!("could not show window: {}", window.label())) }