From 0061ecea78ec3f7808d6ccfcc2c980bd8434bc4d Mon Sep 17 00:00:00 2001
From: reya <123083837+reyamir@users.noreply.github.com>
Date: Tue, 18 Jun 2024 09:07:58 +0700
Subject: [PATCH] feat: use native context menu in tray panel
---
apps/desktop2/src/routes/panel.tsx | 62 +++++++++++++++++++++++++++---
packages/system/src/commands.ts | 3 ++
packages/system/src/window.ts | 10 +++++
src-tauri/src/commands/window.rs | 27 +++++++++++--
src-tauri/src/main.rs | 20 +++++++---
5 files changed, 106 insertions(+), 16 deletions(-)
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();
+ }
+ });
}