diff --git a/apps/desktop2/src/routes/panel.tsx b/apps/desktop2/src/routes/panel.tsx index f1816929..f14096de 100644 --- a/apps/desktop2/src/routes/panel.tsx +++ b/apps/desktop2/src/routes/panel.tsx @@ -4,10 +4,17 @@ import { type LumeEvent, LumeWindow, NostrQuery, useEvent } from "@lume/system"; import { Kind } from "@lume/types"; import { createFileRoute } from "@tanstack/react-router"; import { getCurrent } from "@tauri-apps/api/window"; -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import * as Tabs from "@radix-ui/react-tabs"; -import { InfoIcon, RepostIcon, SettingsIcon } from "@lume/icons"; -import { decodeZapInvoice, formatCreatedAt } from "@lume/utils"; +import { HorizontalDotsIcon, InfoIcon, RepostIcon } from "@lume/icons"; +import { + checkForAppUpdates, + decodeZapInvoice, + formatCreatedAt, +} from "@lume/utils"; +import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu"; +import { open } from "@tauri-apps/plugin-shell"; +import { exit } from "@tauri-apps/plugin-process"; interface EmitAccount { account: string; @@ -66,6 +73,49 @@ function Screen() { return groups; }, [events]); + const showContextMenu = useCallback(async (e: React.MouseEvent) => { + e.preventDefault(); + + const menuItems = await Promise.all([ + MenuItem.new({ + text: "Open Lume", + action: () => LumeWindow.openMainWindow(), + }), + MenuItem.new({ + text: "New Post", + action: () => LumeWindow.openEditor(), + }), + MenuItem.new({ + text: "Search", + action: () => LumeWindow.openSearch(), + }), + PredefinedMenuItem.new({ item: "Separator" }), + MenuItem.new({ + text: "About Lume", + action: async () => await open("https://lume.nu"), + }), + MenuItem.new({ + text: "Check for Updates", + action: async () => await checkForAppUpdates(false), + }), + MenuItem.new({ + text: "Settings", + action: () => LumeWindow.openSettings(), + }), + PredefinedMenuItem.new({ item: "Separator" }), + MenuItem.new({ + text: "Quit", + action: async () => await exit(0), + }), + ]); + + const menu = await Menu.new({ + items: menuItems, + }); + + await menu.popup().catch((e) => console.error(e)); + }, []); + useEffect(() => { if (account?.length && account?.startsWith("npub1")) { NostrQuery.getNotifications() @@ -118,10 +168,10 @@ function Screen() { @@ -276,7 +326,7 @@ function TextNote({ event }: { event: LumeEvent }) { onClick={() => LumeWindow.openEvent(event)} > - + diff --git a/packages/system/src/commands.ts b/packages/system/src/commands.ts index e7313866..9f650289 100644 --- a/packages/system/src/commands.ts +++ b/packages/system/src/commands.ts @@ -401,6 +401,9 @@ try { }, async setBadge(count: number) : Promise { await TAURI_INVOKE("set_badge", { count }); +}, +async openMainWindow() : Promise { +await TAURI_INVOKE("open_main_window"); } } diff --git a/packages/system/src/window.ts b/packages/system/src/window.ts index 883bb7d4..49f913a9 100644 --- a/packages/system/src/window.ts +++ b/packages/system/src/window.ts @@ -3,6 +3,16 @@ import type { LumeEvent } from "./event"; import { commands } from "./commands"; export class LumeWindow { + static async openMainWindow() { + const query = await commands.openMainWindow(); + + if (query.status === "ok") { + return query.data; + } else { + throw new Error(query.error); + } + } + static async openEvent(event: NostrEvent | LumeEvent) { const eTags = event.tags.filter((tag) => tag[0] === "e" || tag[0] === "q"); const root: string = diff --git a/src-tauri/src/commands/window.rs b/src-tauri/src/commands/window.rs index 91ae3524..dcdbf0b3 100644 --- a/src-tauri/src/commands/window.rs +++ b/src-tauri/src/commands/window.rs @@ -1,12 +1,13 @@ +use std::path::PathBuf; + #[cfg(target_os = "macos")] use cocoa::{appkit::NSApp, base::nil, foundation::NSString}; -use std::path::PathBuf; -use tauri::utils::config::WindowEffectsConfig; -use tauri::window::Effect; +use tauri::{LogicalPosition, LogicalSize, Manager, WebviewUrl}; #[cfg(target_os = "macos")] use tauri::TitleBarStyle; +use tauri::utils::config::WindowEffectsConfig; use tauri::WebviewWindowBuilder; -use tauri::{LogicalPosition, LogicalSize, Manager, WebviewUrl}; +use tauri::window::Effect; use tauri_plugin_decorum::WebviewWindowExt; #[tauri::command] @@ -184,3 +185,21 @@ pub fn set_badge(count: i32) { let _: cocoa::base::id = msg_send![dock_tile, setBadgeLabel: label]; } } + +#[tauri::command] +#[specta::specta] +pub fn open_main_window(app: tauri::AppHandle) { + if let Some(window) = app.get_window("main") { + if window.is_visible().unwrap_or_default() { + let _ = window.set_focus(); + } else { + let _ = window.show(); + let _ = window.set_focus(); + }; + } else { + let _ = WebviewWindowBuilder::from_config(&app, app.config().app.windows.first().unwrap()) + .unwrap() + .build() + .unwrap(); + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 82c891e1..3deea394 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -95,7 +95,8 @@ fn main() { commands::window::reposition_column, commands::window::resize_column, commands::window::open_window, - commands::window::set_badge + commands::window::set_badge, + commands::window::open_main_window ]); #[cfg(debug_assertions)] @@ -130,8 +131,8 @@ fn main() { // Handle tray icon event #[cfg(target_os = "macos")] - tray.on_tray_icon_event(|tray, event| match event { - TrayIconEvent::Click { button_state, .. } => { + tray.on_tray_icon_event(|tray, event| { + if let TrayIconEvent::Click { button_state, .. } = event { if button_state == MouseButtonState::Up { let app = tray.app_handle(); let panel = app.get_webview_panel("panel").unwrap(); @@ -145,7 +146,6 @@ fn main() { } } } - _ => {} }); // Create data folder if not exist @@ -170,7 +170,7 @@ fn main() { let lines = io::BufReader::new(file).lines(); // Add bootstrap relays to relay pool - for line in lines.flatten() { + for line in lines.map_while(Result::ok) { if let Some((relay, option)) = line.split_once(',') { match RelayMetadata::from_str(option) { Ok(meta) => { @@ -216,6 +216,14 @@ fn main() { .plugin(tauri_plugin_upload::init()) .plugin(tauri_plugin_updater::Builder::new().build()) .invoke_handler(invoke_handler) - .run(ctx) + .build(ctx) .expect("error while running tauri application") + .run(|app, event| { + if let tauri::RunEvent::ExitRequested { api, .. } = event { + // Hide app icon on macOS + // let _ = app.set_activation_policy(tauri::ActivationPolicy::Accessory); + // Keep API running + api.prevent_exit(); + } + }); }