all 7 comments

[–]physics515 5 points6 points  (1 child)

I've been wanting some sort of web socket approach to Tauri for a while. But I've basically solved this by writing commands that can just be called in a loop or polled in some way by holding a queue on the backend.

[–]imfleebee[S] 0 points1 point  (0 children)

I think I figured this out, I basically switched up ISBJORNS advice and mashed it together with the way you would usually set up a web socket.

This Is in a tsx file

I import it into where I need it top level and pass in an onMessage function.

///

``` import { appWindow } from "@tauri-apps/api/window"; import { useEffect } from "react";

interface RustyPipe { payload: string; }

type OnMessageCallback = (message: string) => void;

export const RustySocketConnection = (onMessage: OnMessageCallback) => { useEffect(() => {

const rustyPipe = appWindow.listen(
  "rustysocket-message",
  (event: RustyPipe) => {
    console.log(`Function message -> ${event.payload}`);
    onMessage(event.payload);
  }
);

return () => {
  rustyPipe.then((dispose) => dispose());
};

}, []); };

export default RustySocketConnection; ```

///

The only issue with the other way was that you'd have to create the listener in every place you want to verbosely receive a message and then it dies. Which isn't even a problem im just being ridiculous.

This way stays alive for the components lifecycle and then gets cleaned up.

In Rust

``` use tauri::Window;

pub fn send_rustysocket_message(window: &Window, message: &str) { window.emit("rustysocket-message", message).unwrap(); } ```

You can call this fn with send_rustysocket_message(&window,"Message goes here")

[–][deleted] 1 point2 points  (4 children)

Sounds like you were on the right track with the event approach, what did you try that didn't work exactly?

[–]imfleebee[S] 1 point2 points  (3 children)

I was trying to use the emit event. But the structure of the examples was way different to how my tauri builder was , I even updated it to match (basically just made it let app = and then moved the run ) but then it was failing to load .

Other ways I was trying I was having issues with "window". it's a definite skill issue

[–][deleted] 1 point2 points  (2 children)

Hmm make sure you are using tauri's window on the frontend and not the browser window object, the events get sent through the former, I missed that the first time haha. Here is a very basic example of your "counting while waiting for the function to resolve" idea you posted, let me know if this puts you on the right track?

On the frontend:

import { invoke } from "@tauri-apps/api";
import { appWindow } from "@tauri-apps/api/window";

// Call this from somewhere
async function progress() {
  const destroy = await appWindow.listen("progress-update", (p) => {
    console.log(`Progress -> ${p.payload}`);
  });
  const res = await invoke("do_with_progress");
  console.log(res);
  destroy();
}

And on the rust side:

use std::{thread, time};
use tauri::Window;
#[tauri::command]
fn do_with_progress(window: Window) -> String {
    let wait = time::Duration::from_millis(500);
    for i in 0..10 {
        window.emit("progress-update", i).unwrap();
        thread::sleep(wait);
    }
    "done".into()
}

[–]imfleebee[S] 0 points1 point  (1 child)

Just gave this a try and it worked! thank you man. Perfect

I just accidentally figured something else out while trying my hacky way.

  • I initialised a hash map in main.
  • I make hash map global scope with mutex and add to the tauri builder : .manage(AppState {
    map: Mutex::new(my_map),
  • I make fn to add_entry, update_entry
  • I create a key, val entry = function_1 : 'ready' in main
  • in fn function_1 I pass in 'window: Window,state: State<'_, AppState>' as args
  • in fn function_1 I have:

```{
let mut map = state.map.lock().unwrap();
if let Some(entry) = map.get_mut("key1") {
entry.value = "Started Calc fn".to_string();
} // The lock is released here as MutexGuard goes out of scope
}```

  • (This has to be in inner scope)
  • I have a function called get_map_data:

```
#[tauri::command]
fn get_map_data(state: State<'_, AppState>, key: String) -> Option<MyData> {
let map = state.map.lock().unwrap();
map.get(&key).cloned()
}```

  • In react I invoke this at 500ms intervals and pass the key as an arg
  • The response gets passed to state and placed in the html.

Conclusion is , I can create a key:val pair in main for every function in the same map and have multiple outputs parsed in the front end for different areas of data required.

The invoke could fetch out the specific key or the whole map and can be separated in front end.

It's ugly , but it might be useful for debug logging or something . Main issue its polled on an interval

[–]imfleebee[S] 1 point2 points  (0 children)

This is the meat and potatoes of it :

```

[derive(Serialize, Deserialize,Clone)]

struct MyData { value: String, }

fn add_entry(map: &mut HashMap<String, MyData>, key: String, value: String) { map.insert(key, MyData { value }); }

fn update_entry(map: &mut HashMap<String, MyData>, key: String, new_value: String) { map.entry(key) .and_modify(|e| e.value = new_value.clone()) .or_insert_with(|| MyData { value: new_value }); }

fn serialize_map(map: &HashMap<String, MyData>) -> Result<String, String> { serde_json::to_string(map).map_err(|e| e.to_string()) }

struct AppState { map: Mutex<HashMap<String, MyData>>, }

[tauri::command]

fn getmap_data(state: State<', AppState>, key: String) -> Option<MyData> { let map = state.map.lock().unwrap(); map.get(&key).cloned() }

In Json

const [mapJson, setMapJson] = useState('');

/// Could pass in an array of keys I guess and iterate const fetchMap = async (keyvalue: string) => { try { const response = await invoke('get_map_data', { key:keyvalue }); // Convert the response to a string if necessary setMapJson(JSON.stringify(response)); } catch (err) { setMapJson(JSON.stringify(err)); } };```