diff --git a/package.json b/package.json index 33b5a71..9e961f1 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.5", "@tauri-apps/plugin-dialog": "2.0.0-rc.0", "@tauri-apps/plugin-fs": "2.0.0-rc.0", + "@tauri-apps/plugin-notification": "2.0.0-rc.0", "@tauri-apps/plugin-os": "2.0.0-rc.0", "@tauri-apps/plugin-process": "2.0.0-rc.0", "@tauri-apps/plugin-shell": "2.0.0-rc.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b7f3df..0865ade 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@tauri-apps/plugin-fs': specifier: 2.0.0-rc.0 version: 2.0.0-rc.0 + '@tauri-apps/plugin-notification': + specifier: 2.0.0-rc.0 + version: 2.0.0-rc.0 '@tauri-apps/plugin-os': specifier: 2.0.0-rc.0 version: 2.0.0-rc.0 @@ -927,6 +930,9 @@ packages: '@tauri-apps/plugin-fs@2.0.0-rc.0': resolution: {integrity: sha512-74VCXEZlzTJ+Jv1V3KrV0qIHhSePpE/ljsF78rcEuvSfyTxLtt/Sb5CIUmVhFlKTRFOH9dX50T4dTZ3qFLyRnA==} + '@tauri-apps/plugin-notification@2.0.0-rc.0': + resolution: {integrity: sha512-0qsT/kvxQ6Ky4g6eQ4SUiHXzM4szTVc6thjz9vnGPYaApLoZrCJ9GdG+vEqeB+cT2dvE+wmxUFETh3ZXYVw8UA==} + '@tauri-apps/plugin-os@2.0.0-rc.0': resolution: {integrity: sha512-OWAl8mooKnGykSD4iog8WRqcnOSx0gGmTJBlEExHdFeIuOHg0Ezvd+WiVLhT9LBg7go3ibNWRWpe/ZG7YEp4Vw==} @@ -2449,6 +2455,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.0.0-rc.0 + '@tauri-apps/plugin-notification@2.0.0-rc.0': + dependencies: + '@tauri-apps/api': 2.0.0-rc.0 + '@tauri-apps/plugin-os@2.0.0-rc.0': dependencies: '@tauri-apps/api': 2.0.0-rc.0 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 70af9ec..572af86 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -943,6 +943,7 @@ dependencies = [ "tauri-plugin-devtools", "tauri-plugin-dialog", "tauri-plugin-fs", + "tauri-plugin-notification", "tauri-plugin-os", "tauri-plugin-prevent-default", "tauri-plugin-process", @@ -1269,6 +1270,16 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -1281,6 +1292,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -2856,6 +2878,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mac-notification-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fca4d74ff9dbaac16a01b924bc3693fa2bba0862c2c633abc73f9a8ea21f64" +dependencies = [ + "cc", + "dirs-next", + "objc-foundation", + "objc_id", + "time", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3167,6 +3202,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "notify-rust" +version = "4.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a1d03b6305ecefdd9c6c60150179bb8d9f0cd4e64bbcad1e41419e7bf5e414" +dependencies = [ + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3788,7 +3836,7 @@ checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", "indexmap 2.3.0", - "quick-xml", + "quick-xml 0.32.0", "serde", "time", ] @@ -3953,6 +4001,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.32.0" @@ -5340,6 +5397,25 @@ dependencies = [ "uuid", ] +[[package]] +name = "tauri-plugin-notification" +version = "2.0.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e69aaabfb7720083862ea35de37d9ad71b9465249762462ced38c2c92690a9e" +dependencies = [ + "log", + "notify-rust", + "rand 0.8.5", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror", + "time", + "url", +] + [[package]] name = "tauri-plugin-os" version = "2.0.0-rc.0" @@ -5550,6 +5626,17 @@ dependencies = [ "toml 0.7.8", ] +[[package]] +name = "tauri-winrt-notification" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89f5fb70d6f62381f5d9b2ba9008196150b40b75f3068eb24faeddf1c686871" +dependencies = [ + "quick-xml 0.31.0", + "windows 0.56.0", + "windows-version", +] + [[package]] name = "tempfile" version = "3.11.0" @@ -6420,8 +6507,8 @@ dependencies = [ "webview2-com-sys", "windows 0.57.0", "windows-core 0.57.0", - "windows-implement", - "windows-interface", + "windows-implement 0.57.0", + "windows-interface 0.57.0", ] [[package]] @@ -6515,6 +6602,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core 0.56.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.57.0" @@ -6543,18 +6640,41 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.57.0", + "windows-interface 0.57.0", "windows-result", "windows-targets 0.52.6", ] +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -6566,6 +6686,17 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "windows-interface" version = "0.57.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index be504da..0d94b96 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -29,6 +29,7 @@ tauri-plugin-shell = "2.0.0-rc" tauri-plugin-updater = "2.0.0-rc" tauri-plugin-process = "2.0.0-rc" tauri-plugin-fs = "2.0.0-rc" +tauri-plugin-notification = "2.0.0-rc" tauri-plugin-decorum = "1.0.0" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 3992c0a..18b8d57 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -33,6 +33,7 @@ "updater:allow-download-and-install", "clipboard-manager:allow-write-text", "clipboard-manager:allow-read-text", - "fs:allow-read-file" + "fs:allow-read-file", + "notification:default" ] } diff --git a/src-tauri/src/commands/account.rs b/src-tauri/src/commands/account.rs index eb96c8e..131682b 100644 --- a/src-tauri/src/commands/account.rs +++ b/src-tauri/src/commands/account.rs @@ -4,6 +4,7 @@ use nostr_sdk::prelude::*; use serde::Serialize; use std::{collections::HashSet, str::FromStr, time::Duration}; use tauri::{Emitter, Manager, State}; +use tauri_plugin_notification::NotificationExt; use crate::Nostr; @@ -307,16 +308,35 @@ pub async fn login( fake_sig, ); + // Save rumor to database to further query if let Err(e) = client.database().save_event(&ev).await { - println!("Error: {}", e) + println!("[save event] error: {}", e) } - let payload = EventPayload { - event: rumor.as_json(), - sender: sender.to_hex(), - }; + // Emit new event to frontend + if let Err(e) = handle.emit( + "event", + EventPayload { + event: rumor.as_json(), + sender: sender.to_hex(), + }, + ) { + println!("[emit] error: {}", e) + } - handle.emit("event", payload).unwrap(); + if let Some(window) = handle.get_webview_window("main") { + if !window.is_focused().unwrap() { + if let Err(e) = handle + .notification() + .builder() + .body("You have a new message") + .title("Coop") + .show() + { + println!("[notification] error: {}", e); + } + } + } } } } else { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b44bbcf..bc17283 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -173,6 +173,7 @@ fn main() { .plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_decorum::init()) + .plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_shell::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/commons.ts b/src/commons.ts index b8ae0fa..2cca14e 100644 --- a/src/commons.ts +++ b/src/commons.ts @@ -1,5 +1,10 @@ import { ask, message, open } from "@tauri-apps/plugin-dialog"; import { readFile } from "@tauri-apps/plugin-fs"; +import { + isPermissionGranted, + requestPermission, + sendNotification, +} from "@tauri-apps/plugin-notification"; import { relaunch } from "@tauri-apps/plugin-process"; import { check } from "@tauri-apps/plugin-updater"; import { type ClassValue, clsx } from "clsx"; @@ -87,14 +92,12 @@ export function groupEventByDate(events: NostrEvent[]) { return groups; } -/* -export function isEmojiOnly(str: string) { - const stringToTest = str.replace(/ /g, ""); - const emojiRegex = - /^(?:(?:\p{RI}\p{RI}|\p{Emoji}(?:\p{Emoji_Modifier}|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?(?:\u{200D}\p{Emoji}(?:\p{Emoji_Modifier}|\u{FE0F}\u{20E3}?|[\u{E0020}-\u{E007E}]+\u{E007F})?)*)|[\u{1f900}-\u{1f9ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}])+$/u; - return emojiRegex.test(stringToTest) && Number.isNaN(Number(stringToTest)); +export async function checkPermission() { + if (!(await isPermissionGranted())) { + return (await requestPermission()) === "granted"; + } + return true; } -*/ export async function checkForAppUpdates(silent: boolean) { try { diff --git a/src/routes/$account.chats.lazy.tsx b/src/routes/$account.chats.lazy.tsx index 8baf4ae..f97754d 100644 --- a/src/routes/$account.chats.lazy.tsx +++ b/src/routes/$account.chats.lazy.tsx @@ -16,7 +16,7 @@ import { useQuery } from "@tanstack/react-query"; import { Link, Outlet, createLazyFileRoute } from "@tanstack/react-router"; import { listen } from "@tauri-apps/api/event"; import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu"; -import { readText } from "@tauri-apps/plugin-clipboard-manager"; +import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager"; import { message } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-shell"; import { type NostrEvent, nip19 } from "nostr-tools"; @@ -460,12 +460,11 @@ function CurrentUser() { const menuItems = await Promise.all([ MenuItem.new({ - text: "Contacts", - action: () => - navigate({ - to: "/$account/contacts", - params: { account: params.account }, - }), + text: "Copy Public Key", + action: async () => { + const npub = nip19.npubEncode(params.account); + await writeText(npub); + }, }), MenuItem.new({ text: "Settings", diff --git a/src/routes/index.tsx b/src/routes/index.tsx index a4efcf1..9493b16 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,5 +1,5 @@ import { commands } from "@/commands"; -import { checkForAppUpdates } from "@/commons"; +import { checkForAppUpdates, checkPermission } from "@/commons"; import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/")({ @@ -8,6 +8,10 @@ export const Route = createFileRoute("/")({ // TODO: move this function to rust await checkForAppUpdates(true); + // Request notification permission + await checkPermission(); + + // Get all accounts from system const accounts = await commands.getAccounts(); if (!accounts.length) {