eb452bf57e
Windows/Linux WebviewWindow have no set_badge_label method — build failed on GitHub Actions windows runner.
198 lines
7.5 KiB
Rust
198 lines
7.5 KiB
Rust
mod crypto;
|
|
|
|
#[cfg(feature = "rust-livekit")]
|
|
mod livekit_bridge;
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
use tauri::{
|
|
menu::{Menu, MenuEvent, MenuItem, PredefinedMenuItem},
|
|
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
|
AppHandle, Listener, Manager,
|
|
};
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
use serde::Deserialize;
|
|
|
|
// Payload for the `tray-unread-update` event the JS layer emits whenever the
|
|
// aggregate unread-count changes. 0 hides the badge / resets the tooltip;
|
|
// non-zero sets a count indicator.
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
#[derive(Deserialize)]
|
|
struct TrayUnreadPayload {
|
|
count: u32,
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
fn show_main_window(app: &AppHandle) {
|
|
if let Some(win) = app.get_webview_window("main") {
|
|
let _ = win.show();
|
|
let _ = win.unminimize();
|
|
let _ = win.set_focus();
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
fn hide_main_window(app: &AppHandle) {
|
|
if let Some(win) = app.get_webview_window("main") {
|
|
let _ = win.hide();
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
fn handle_menu_event(app: &AppHandle, event: MenuEvent) {
|
|
match event.id.as_ref() {
|
|
"tray-show" => show_main_window(app),
|
|
"tray-hide" => hide_main_window(app),
|
|
"tray-quit" => {
|
|
app.exit(0);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
pub fn run() {
|
|
#[cfg(not(feature = "rust-livekit"))]
|
|
let mut builder = tauri::Builder::default()
|
|
.invoke_handler(tauri::generate_handler![
|
|
crypto::crypto_random_bytes,
|
|
crypto::crypto_secretbox_encrypt,
|
|
crypto::crypto_secretbox_decrypt,
|
|
crypto::crypto_box_keypair,
|
|
crypto::crypto_box_encrypt,
|
|
crypto::crypto_box_decrypt,
|
|
crypto::crypto_box_seal,
|
|
crypto::crypto_box_seal_open,
|
|
crypto::crypto_pwhash,
|
|
])
|
|
.plugin(tauri_plugin_notification::init());
|
|
|
|
#[cfg(feature = "rust-livekit")]
|
|
let mut builder = tauri::Builder::default()
|
|
.manage(livekit_bridge::LivekitState::new())
|
|
.invoke_handler(tauri::generate_handler![
|
|
crypto::crypto_random_bytes,
|
|
crypto::crypto_secretbox_encrypt,
|
|
crypto::crypto_secretbox_decrypt,
|
|
crypto::crypto_box_keypair,
|
|
crypto::crypto_box_encrypt,
|
|
crypto::crypto_box_decrypt,
|
|
crypto::crypto_box_seal,
|
|
crypto::crypto_box_seal_open,
|
|
crypto::crypto_pwhash,
|
|
livekit_bridge::livekit_connect,
|
|
livekit_bridge::livekit_disconnect,
|
|
livekit_bridge::livekit_send_data,
|
|
livekit_bridge::livekit_set_mic,
|
|
livekit_bridge::livekit_set_camera,
|
|
])
|
|
.plugin(tauri_plugin_notification::init());
|
|
|
|
builder = builder
|
|
.plugin(tauri_plugin_sql::Builder::default().build())
|
|
.plugin(tauri_plugin_fs::init())
|
|
.plugin(
|
|
tauri_plugin_stronghold::Builder::new(|password| {
|
|
// TODO: derive stronghold key from password using argon2 / blake2b.
|
|
// Placeholder so project compiles.
|
|
password.as_bytes().to_vec()
|
|
})
|
|
.build(),
|
|
);
|
|
|
|
// Global shortcut + updater + window-state plugins are desktop-only
|
|
// (no mobile support — mobile windows are OS-managed).
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
{
|
|
builder = builder
|
|
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
|
.plugin(tauri_plugin_updater::Builder::new().build())
|
|
.plugin(tauri_plugin_window_state::Builder::new().build());
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
{
|
|
builder = builder.setup(|app| {
|
|
// Tray icon with a minimal menu. Left-click toggles window
|
|
// visibility; right-click shows the menu. Badge / tooltip updates
|
|
// come from the JS side via `tray-unread-update` events.
|
|
let show = MenuItem::with_id(app, "tray-show", "Öffnen", true, None::<&str>)?;
|
|
let hide = MenuItem::with_id(app, "tray-hide", "Ausblenden", true, None::<&str>)?;
|
|
let sep = PredefinedMenuItem::separator(app)?;
|
|
let quit = MenuItem::with_id(app, "tray-quit", "Beenden", true, None::<&str>)?;
|
|
let menu = Menu::with_items(app, &[&show, &hide, &sep, &quit])?;
|
|
|
|
let mut tray_builder = TrayIconBuilder::with_id("chatapp-tray")
|
|
.menu(&menu)
|
|
.show_menu_on_left_click(false)
|
|
.tooltip("ChatApp")
|
|
.on_menu_event(|app, event| handle_menu_event(app, event))
|
|
.on_tray_icon_event(|tray, event| {
|
|
if let TrayIconEvent::Click {
|
|
button: MouseButton::Left,
|
|
button_state: MouseButtonState::Up,
|
|
..
|
|
} = event
|
|
{
|
|
let app = tray.app_handle();
|
|
if let Some(win) = app.get_webview_window("main") {
|
|
if win.is_visible().unwrap_or(false) {
|
|
let _ = win.hide();
|
|
} else {
|
|
let _ = win.show();
|
|
let _ = win.set_focus();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// `default_window_icon` returns Option<&Image>; only attach if
|
|
// we actually have one bundled (should always be true via the
|
|
// tauri.conf.json icon list, but guard to stay typesafe).
|
|
if let Some(icon) = app.default_window_icon() {
|
|
tray_builder = tray_builder.icon(icon.clone());
|
|
}
|
|
|
|
let tray = tray_builder.build(app)?;
|
|
|
|
// Listen for JS-side unread updates and mirror them into the tray
|
|
// tooltip + macOS dock badge. `tray` is cheap to clone (internal
|
|
// Arc) so we can move it into the listener closure directly.
|
|
let tray_handle = tray.clone();
|
|
let badge_window = app.get_webview_window("main");
|
|
app.listen("tray-unread-update", move |event| {
|
|
let Ok(payload) = serde_json::from_str::<TrayUnreadPayload>(event.payload())
|
|
else {
|
|
return;
|
|
};
|
|
let tooltip = if payload.count == 0 {
|
|
"ChatApp".to_string()
|
|
} else {
|
|
format!("ChatApp · {} neu", payload.count)
|
|
};
|
|
let _ = tray_handle.set_tooltip(Some(tooltip));
|
|
// Dock/taskbar badge. `set_badge_label` is macOS-only.
|
|
// Windows taskbar uses an overlay icon (different API).
|
|
// Linux has no cross-DE badge API — skip.
|
|
#[cfg(target_os = "macos")]
|
|
if let Some(win) = badge_window.as_ref() {
|
|
let badge = if payload.count == 0 {
|
|
None
|
|
} else {
|
|
Some(payload.count.to_string())
|
|
};
|
|
let _ = win.set_badge_label(badge);
|
|
}
|
|
#[cfg(not(target_os = "macos"))]
|
|
let _ = &badge_window;
|
|
});
|
|
|
|
Ok(())
|
|
});
|
|
}
|
|
|
|
builder
|
|
.run(tauri::generate_context!())
|
|
.expect("error while running tauri application");
|
|
}
|