lingxia-logic 0.5.1

JavaScript runtime for LingXia lightweight application
use crate::I18nKey;
use crate::i18n::{js_error_from_lxapp_error, t};
use crate::update;
use lingxia_platform::traits::ui::{ToastIcon, ToastOptions, ToastPosition, UserFeedback};
use lxapp::lx;
use lxapp::{self, LxApp, LxAppError, LxAppStartupOptions, ReleaseType, UpdateManager};
use rong::{FromJSObj, JSContext, JSFunc, JSResult};
use serde::Deserialize;
use std::sync::Arc;

#[derive(FromJSObj, Deserialize)]
struct NavigateToOptions {
    #[serde(rename = "appId")]
    #[rename = "appId"]
    appid: String,
    path: Option<String>,
    #[serde(rename = "envVersion")]
    #[rename = "envVersion"]
    env_version: Option<String>,
}

fn build_startup_options(options: &NavigateToOptions) -> (LxAppStartupOptions, ReleaseType) {
    let path = options.path.as_deref().unwrap_or("");
    let mut startup_options = LxAppStartupOptions::new(path);

    let release_type = options
        .env_version
        .as_deref()
        .map(lxapp::parse_env_release_type)
        .unwrap_or(ReleaseType::Release);

    if options.env_version.is_some() {
        startup_options = startup_options.set_release_type(release_type);
    }

    (startup_options, release_type)
}

fn should_navigate_to_lxapp(
    lxapp: &LxApp,
    options: &NavigateToOptions,
) -> Result<bool, LxAppError> {
    if options.appid.is_empty() {
        return Err(LxAppError::InvalidParameter(
            "navigateToLxApp requires appId".to_string(),
        ));
    }

    if lxapp.appid == options.appid {
        return Ok(false);
    }

    Ok(true)
}

async fn do_navigate_to_lxapp(lxapp: Arc<LxApp>, options: NavigateToOptions) -> JSResult<()> {
    let (startup_options, release_type) = build_startup_options(&options);
    let target_appid = options.appid.clone();

    update::ensure_first_install(&lxapp, &target_appid, release_type).await?;
    if lxapp::is_force_update_downloading(&target_appid, release_type) {
        show_force_update_downloading_toast(&lxapp);
    }
    lxapp::ensure_force_update_for_installed(&lxapp, &target_appid, release_type)
        .await
        .map_err(|e| js_error_from_lxapp_error(&e))?;

    lxapp
        .navigate_to(target_appid.clone(), startup_options)
        .map_err(|e| js_error_from_lxapp_error(&e))?;

    UpdateManager::spawn_release_lxapp_update_check(target_appid);
    Ok(())
}

fn show_force_update_downloading_toast(lxapp: &Arc<LxApp>) {
    let title = t(I18nKey::UpdateDownloading);
    let _ = lxapp.runtime.show_toast(ToastOptions {
        title,
        icon: ToastIcon::Loading,
        image: None,
        duration: 1.5,
        mask: false,
        position: ToastPosition::Center,
    });
}

fn do_navigate_back_lxapp(lxapp: &LxApp) -> Result<(), LxAppError> {
    lxapp.navigate_back()?;
    Ok(())
}

async fn navigate_to_lxapp(ctx: JSContext, options: NavigateToOptions) -> JSResult<()> {
    let lxapp = LxApp::from_ctx(&ctx)?;

    if !should_navigate_to_lxapp(&lxapp, &options).map_err(|e| js_error_from_lxapp_error(&e))? {
        return Ok(());
    }

    do_navigate_to_lxapp(lxapp, options).await?;
    Ok(())
}

async fn navigate_back_lxapp(ctx: JSContext) -> JSResult<()> {
    let lxapp = LxApp::from_ctx(&ctx)?;
    do_navigate_back_lxapp(&lxapp).map_err(|e| js_error_from_lxapp_error(&e))?;
    Ok(())
}

pub(crate) fn init(ctx: &JSContext) -> JSResult<()> {
    // Register navigator
    let navigate_to_lxapp = JSFunc::new(ctx, navigate_to_lxapp)?;
    lx::register_js_api(ctx, "navigateToLxApp", navigate_to_lxapp)?;

    let navigate_back_lxapp = JSFunc::new(ctx, navigate_back_lxapp)?;
    lx::register_js_api(ctx, "navigateBackLxApp", navigate_back_lxapp)?;

    Ok(())
}