lingxia-lxapp 0.12.0

LxApp (lightweight application) container and runtime for LingXia framework
Documentation
//! Process-wide runtime/manager registry and lookup helpers.

use super::*;

// Global instance of LxApps manager
static LXAPPS_MANAGER: OnceLock<Arc<LxApps>> = OnceLock::new();
// Global runtime available as soon as facade-driven runtime initialization starts.
static RUNTIME: OnceLock<Arc<Platform>> = OnceLock::new();

pub(crate) fn set_runtime(runtime: Arc<Platform>) {
    let _ = RUNTIME.set(runtime);
}

pub(crate) fn set_lxapps_manager(manager: Arc<LxApps>) -> Result<(), LxAppError> {
    LXAPPS_MANAGER.set(manager).map_err(|_| {
        LxAppError::Runtime(
            "LxApps manager singleton had been initialized by another instance".to_string(),
        )
    })
}

/// Get access to the LxApps manager for navigation stack operations
pub(crate) fn get_lxapps_manager() -> Option<Arc<LxApps>> {
    LXAPPS_MANAGER.get().cloned()
}

/// Get the platform runtime instance.
/// Returns None if the SDK has not been initialized.
pub fn get_platform() -> Option<Arc<Platform>> {
    RUNTIME
        .get()
        .cloned()
        .or_else(|| LXAPPS_MANAGER.get().map(|manager| manager.runtime.clone()))
}

/// User override for the product display language (the settings-page
/// "Language" choice). `None` follows the system locale.
static DISPLAY_LANGUAGE: Mutex<Option<String>> = Mutex::new(None);

/// Set (or clear) the display-language override. The shell that owns the
/// language setting seeds this at startup and updates it on change so every
/// `get_display_language` consumer — native chrome i18n included — follows
/// the user's choice without re-reading the settings store.
pub fn set_display_language(language: Option<String>) {
    let normalized = language.filter(|value| !value.trim().is_empty());
    *DISPLAY_LANGUAGE.lock().unwrap_or_else(|e| e.into_inner()) = normalized;
    publish_display_language(&get_display_language());
}

/// Push the language to both halves of every running lxapp.
///
/// The native chrome reads `get_display_language` live, but a page WebView only
/// ever received the value its bootstrap was written with, and Logic only ever
/// read it on demand — so without this a language switch relabels the chrome
/// and leaves the content it frames, plus every title the app set itself, in
/// the previous language until the page is recreated.
fn publish_display_language(language: &str) {
    let Some(manager) = get_lxapps_manager() else {
        return;
    };
    let quoted = serde_json::to_string(language).unwrap_or_else(|_| "\"en-US\"".to_string());
    let script = format!("var f = globalThis.__lingxiaApplyDisplayLanguage; if (f) f({quoted});");
    use lingxia_webview::WebViewController;
    // Collect first: `publish_app_event` looks the appid up in this same map,
    // and a re-entrant read while a writer is queued deadlocks the caller.
    let apps: Vec<(String, Arc<LxApp>)> = manager
        .lxapps
        .iter()
        .map(|entry| (entry.key().clone(), entry.value().clone()))
        .collect();
    for (appid, app) in apps {
        for page in app.live_page_instances() {
            if let Some(webview) = page.webview() {
                let _ = webview.exec_js(&script);
            }
        }
        crate::appservice::event_bus::publish_app_event(
            &appid,
            crate::DISPLAY_LANGUAGE_CHANGE_EVENT,
            Some(quoted.clone()),
        );
    }
}

/// Get the product display language: the user override when set, else the
/// system locale. Returns "en-US" if the SDK has not been initialized.
pub fn get_display_language() -> String {
    if let Some(language) = DISPLAY_LANGUAGE
        .lock()
        .unwrap_or_else(|e| e.into_inner())
        .clone()
    {
        return language;
    }
    RUNTIME
        .get()
        .map(|runtime| runtime.get_system_locale().to_string())
        .unwrap_or_else(|| "en-US".to_string())
}

/// Try to get a specific LxApp instance by lxappid
pub fn try_get(appid: &str) -> Option<Arc<LxApp>> {
    LXAPPS_MANAGER
        .get()
        .and_then(|manager| manager.lxapps.get(appid).map(|lxapp| lxapp.clone()))
}

pub fn find_page_by_instance_id(id: &str) -> Option<PageInstance> {
    LXAPPS_MANAGER.get().and_then(|manager| {
        manager
            .lxapps
            .iter()
            .find_map(|entry| entry.value().get_page_by_instance_id_str(id))
    })
}