From 1ddd93a10857ca750531cb5abcf2c224b4316826 Mon Sep 17 00:00:00 2001 From: Wesley Moore Date: Sun, 18 Feb 2024 16:59:48 +1000 Subject: [PATCH] Emit periodic events from the backend and listen to them in Gleam --- src-tauri/build.rs | 2 +- src-tauri/src/lib.rs | 6 +++--- src-tauri/src/main.rs | 41 ++++++++++++++++++++++++++++++++++++----- src/ffi/commands.js | 10 ++++++++++ src/ffi/js_extra.js | 7 +++++++ src/videopls.gleam | 42 ++++++++++++++++++++++++++++++++++++++++-- style.css | 3 +++ 7 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 src/ffi/js_extra.js diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 795b9b7..d860e1e 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,3 @@ fn main() { - tauri_build::build() + tauri_build::build() } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index cfef8d1..3b48e91 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,6 +1,6 @@ #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { - tauri::Builder::default() - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + tauri::Builder::default() + .run(tauri::generate_context!()) + .expect("error while running tauri application"); } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0b2e5fe..6217932 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,14 +1,45 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use tauri::EventTarget; +use tauri::Manager; + fn main() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![greet]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + tauri::Builder::default() + .setup(|app| { + let app = app.handle().clone(); + std::thread::spawn(move || { + loop { + // app.emit_all("event-name", Payload { message: "Tauri is awesome!".into() }).unwrap(); + let now = SystemTime::now(); + let duration = now.duration_since(UNIX_EPOCH).unwrap(); + app.emit_to(EventTarget::any(), "tick", duration.as_secs()) + .unwrap(); + std::thread::sleep(Duration::from_secs(1)); + } + }); + Ok(()) + }) + .invoke_handler(tauri::generate_handler![greet]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); } #[tauri::command] fn greet(name: &str) -> String { - format!("Hello, {}!", name) + format!("Hello, {}!", name) +} + +#[tauri::command] +fn clock(app: tauri::AppHandle) { + std::thread::spawn(move || { + loop { + let now = SystemTime::now(); + let duration = now.duration_since(UNIX_EPOCH).unwrap(); + app.emit_to(EventTarget::any(), "tick", duration.as_secs()) + .unwrap(); + std::thread::sleep(Duration::from_secs(1)); + } + }); } diff --git a/src/ffi/commands.js b/src/ffi/commands.js index 06b0e7b..4f4b9d2 100644 --- a/src/ffi/commands.js +++ b/src/ffi/commands.js @@ -1,4 +1,5 @@ import { invoke } from '@tauri-apps/api/core'; +import { listen } from '@tauri-apps/api/event'; import { Ok, Error } from "../../build/dev/javascript/videopls/gleam.mjs"; // FIXME: Is this the right way to do this? export async function greet(name) { @@ -8,3 +9,12 @@ export async function greet(name) { return new Error(error.toString()); } } + +export async function listenForTick(handler) { + console.log("listenForTick"); + await listen('tick', (event) => { + console.log(event.payload); + handler(event.payload); + }); +} + diff --git a/src/ffi/js_extra.js b/src/ffi/js_extra.js new file mode 100644 index 0000000..22c013d --- /dev/null +++ b/src/ffi/js_extra.js @@ -0,0 +1,7 @@ +export function from_unix(timestamp) { + return new Date(timestamp * 1000); +} + +export function date_to_string(date) { + return date.toString(); +} diff --git a/src/videopls.gleam b/src/videopls.gleam index 10b28ee..e3503e1 100644 --- a/src/videopls.gleam +++ b/src/videopls.gleam @@ -15,11 +15,11 @@ pub fn main() { } type Model { - Model(count: Int, greeting: String, name: String) + Model(count: Int, greeting: String, name: String, time: Int) } fn init(_) -> #(Model, Effect(Msg)) { - #(Model(0, "", ""), effect.none()) + #(Model(0, "", "", 0), bind_clock()) } pub type Msg { @@ -28,6 +28,7 @@ pub type Msg { Greet GotGreeting(String) UpdateName(String) + Tick(Int) } fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { @@ -40,6 +41,7 @@ fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { effect.none(), ) UpdateName(name) -> #(Model(..model, name: name), effect.none()) + Tick(time) -> #(Model(..model, time: time), effect.none()) } } @@ -61,17 +63,50 @@ fn do_get_greeting(name: String, dispatch: fn(Msg) -> Nil) -> Nil { Nil } +fn bind_clock() -> Effect(Msg) { + effect.from(fn(dispatch) { + listen_for_tick(fn(time) { + tick(time) + |> dispatch + }) + + Nil + }) +} + @external(javascript, "./ffi/commands.js", "greet") pub fn greet(name: String) -> Promise(Result(String, String)) +type UnlistenFn = + fn() -> Nil + +@external(javascript, "./ffi/commands.js", "listenForTick") +pub fn listen_for_tick(handler: fn(Int) -> Nil) -> Promise(UnlistenFn) + +pub type Date + +@external(javascript, "./ffi/js_extra.js", "from_unix") +pub fn new_date(timestamp: Int) -> Date + +@external(javascript, "./ffi/js_extra.js", "date_to_string") +pub fn date_to_string(date: Date) -> String + fn update_name(text: String) -> Msg { UpdateName(text) } +fn tick(time: Int) -> Msg { + Tick(time) +} + // -- VIEW fn view(model: Model) -> Element(Msg) { let count = int.to_string(model.count) + let time = + model.time + |> new_date + |> date_to_string html.div([], [ html.h1([], [element.text("Gleam + Vite + Tauri")]), @@ -92,5 +127,8 @@ fn view(model: Model) -> Element(Msg) { html.button([event.on_click(Increment)], [element.text("+")]), html.button([event.on_click(Greet)], [element.text("Greet")]), ]), + html.div([attr.class("clock text-center")], [ + element.text("Clock: " <> time), + ]), ]) } diff --git a/style.css b/style.css index e6646a1..2531a7f 100644 --- a/style.css +++ b/style.css @@ -67,6 +67,9 @@ h1 { .field { margin-top: 0.5em; } +.clock { + margin-top: 2em; +} .text-center { text-align: center; }