rustrust-tokiotaurileptos

How to listen to "Backend" value change in tauri-leptos app?


I have tauri command on backend(tauri) that returns a string(light theme value from windows registry). I need to get it on frontend + listen if this string changes. I couldn't find needed functions in leptos or tauri docs. So I came up with this crutch on frontend:

let (is_light_theme, set_is_light_theme) = create_signal(String::new());

let is_light_theme_fn = move || {
        spawn_local(async move {
            let is_light_theme_fetch = invoke("is_light_theme", to_value("").unwrap())
                .await
                .as_string()
                .unwrap();
            set_is_light_theme.set(_is_light_theme_);
            loop {
                let old_value = is_light_theme.get();
                let new_value = invoke("is_light_theme", to_value("").unwrap())
                    .await
                    .as_string()
                    .unwrap();
                if new_value != old_value {
                    set_is_light_theme.set(new_value);
                };
                // sleep(Duration::from_millis(2000)).await; // breaks loop
            }
        });
    };
    is_light_theme_fn();

tauri command on the backend:

#[tauri::command]
fn is_light_theme() -> String {
    let theme = helpers::is_light_theme();
    format!("{}", theme)
}

is_light_theme in helpers:

pub fn is_light_theme() -> bool {
    let val = CURRENT_USER
        .open(r#"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"#)
        .expect("Failed to get registry key")
        .get_u32("AppsUseLightTheme")
        .expect("Failed to get registry value");
    println!("{}", val);
    val != 0
}

This runs somewhat okay but this loop is very process-intensive. I tried adding sleep from std:thread and tokio to loop, both of them are breaking it.

Maybe there's a better a way to listen for value changes in backend?


Solution

  • Backend -> Frontend

    You can use Tauri events:

    // in your setup routine or tauri command function, where you can have access to the app handle
    app.emit_all("event-name", Payload { message: "Tauri is awesome!".into() }).unwrap();
    

    And then listen to this event in the frontend:

    import { emit, listen } from '@tauri-apps/api/event'
    
    const unlisten = await listen('event-name', (event) => {
      // event.payload is the payload object
      console.log(event);
    })
    

    ========== OLD ANSWER =============

    Frontend -> Backend

    It's usually a bad idea to continuously poll data, I might be not fully understanding your use case, but here's how I would do it, if I needed to get theme from the frontend.

    I'd use state management instead:

    use std::sync::Mutex;
    use tauri::State;
    
    struct Theme {
        theme: Mutex<String>,
    }
    
    fn main() {
        tauri::Builder::default()
                    .manage(Theme { theme: Mutex::new(String::from("default")) })
            .invoke_handler(tauri::generate_handler![update_theme])
            .run(tauri::generate_context!())
            .expect("error while running tauri application");
    }
    
    #[tauri::command]
    fn update_theme(new_theme: String, state: State<Theme>) {
            let mut theme = state.theme.lock().unwrap();
            *theme = new_theme;
    }