diff --git a/apps/desktop2/package.json b/apps/desktop2/package.json index 2b77d86f..1a8458f4 100644 --- a/apps/desktop2/package.json +++ b/apps/desktop2/package.json @@ -15,10 +15,6 @@ "@lume/utils": "workspace:^", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-collapsible": "^1.0.3", - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-hover-card": "^1.0.7", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-switch": "^1.0.3", @@ -37,12 +33,10 @@ "react-currency-input-field": "^3.8.0", "react-dom": "^18.3.1", "react-hook-form": "^7.52.0", - "react-hotkeys-hook": "^4.5.0", "react-i18next": "^14.1.2", "react-string-replace": "^1.1.1", "slate": "^0.103.0", "slate-react": "^0.105.0", - "sonner": "^1.5.0", "use-debounce": "^10.0.1", "virtua": "^0.31.0" }, diff --git a/apps/desktop2/src/components/avatarUploader.tsx b/apps/desktop2/src/components/avatarUploader.tsx index 2463b4f1..a3e4ce2c 100644 --- a/apps/desktop2/src/components/avatarUploader.tsx +++ b/apps/desktop2/src/components/avatarUploader.tsx @@ -1,13 +1,13 @@ import { NostrQuery } from "@lume/system"; import { Spinner } from "@lume/ui"; import { cn } from "@lume/utils"; +import { message } from "@tauri-apps/plugin-dialog"; import { type Dispatch, type ReactNode, type SetStateAction, useState, } from "react"; -import { toast } from "sonner"; export function AvatarUploader({ setPicture, @@ -27,7 +27,7 @@ export function AvatarUploader({ setPicture(image); } catch (e) { setLoading(false); - toast.error(String(e)); + await message(String(e), { title: "Lume", kind: "error" }); } }; diff --git a/apps/desktop2/src/components/note/buttons/repost.tsx b/apps/desktop2/src/components/note/buttons/repost.tsx index fe90523a..675d940b 100644 --- a/apps/desktop2/src/components/note/buttons/repost.tsx +++ b/apps/desktop2/src/components/note/buttons/repost.tsx @@ -4,8 +4,8 @@ import { Spinner } from "@lume/ui"; import { cn } from "@lume/utils"; import { useRouteContext } from "@tanstack/react-router"; import { Menu, MenuItem } from "@tauri-apps/api/menu"; +import { message } from "@tauri-apps/plugin-dialog"; import { useCallback, useState } from "react"; -import { toast } from "sonner"; import { useNoteContext } from "../provider"; export function NoteRepost({ large = false }: { large?: boolean }) { @@ -27,12 +27,12 @@ export function NoteRepost({ large = false }: { large?: boolean }) { // update state setLoading(false); setIsRepost(true); - - // notify - toast.success("You've reposted this post successfully"); } catch { setLoading(false); - toast.error("Repost failed, try again later"); + await message("Repost failed, try again later", { + title: "Lume", + kind: "info", + }); } }; diff --git a/apps/desktop2/src/components/note/content.tsx b/apps/desktop2/src/components/note/content.tsx index f2d647b4..e7670a83 100644 --- a/apps/desktop2/src/components/note/content.tsx +++ b/apps/desktop2/src/components/note/content.tsx @@ -87,8 +87,7 @@ export function NoteContent({ )); return richContent; - } catch (e) { - console.log("[parser]: ", e); + } catch { return event.content; } }, [event.content]); diff --git a/apps/desktop2/src/components/note/user.tsx b/apps/desktop2/src/components/note/user.tsx index b5007f51..e545a184 100644 --- a/apps/desktop2/src/components/note/user.tsx +++ b/apps/desktop2/src/components/note/user.tsx @@ -1,62 +1,62 @@ +import { LumeWindow } from "@lume/system"; import { cn } from "@lume/utils"; -import * as HoverCard from "@radix-ui/react-hover-card"; +import { Menu, MenuItem } from "@tauri-apps/api/menu"; +import { writeText } from "@tauri-apps/plugin-clipboard-manager"; +import { useCallback } from "react"; import { User } from "../user"; import { useNoteContext } from "./provider"; -import { LumeWindow } from "@lume/system"; export function NoteUser({ className }: { className?: string }) { const event = useNoteContext(); + const showContextMenu = useCallback(async (e: React.MouseEvent) => { + e.preventDefault(); + + const menuItems = await Promise.all([ + MenuItem.new({ + text: "View Profile", + action: () => LumeWindow.openProfile(event.pubkey), + }), + MenuItem.new({ + text: "Copy Public Key", + action: async () => { + const pubkey = await event.pubkeyAsBech32(); + await writeText(pubkey); + }, + }), + ]); + + const menu = await Menu.new({ + items: menuItems, + }); + + await menu.popup().catch((e) => console.error(e)); + }, []); + return ( - - -
- - - -
-
- - -
-
·
- -
-
-
- - +
+ -
+ + +
+
+ +
- - - - +
·
+ +
+ +
); } diff --git a/apps/desktop2/src/routes/$account.tsx b/apps/desktop2/src/routes/$account.tsx index ae36a9c8..3a879817 100644 --- a/apps/desktop2/src/routes/$account.tsx +++ b/apps/desktop2/src/routes/$account.tsx @@ -1,18 +1,14 @@ import { User } from "@/components/user"; -import { - ComposeFilledIcon, - HorizontalDotsIcon, - PlusIcon, - SearchIcon, -} from "@lume/icons"; +import { ComposeFilledIcon, HorizontalDotsIcon, PlusIcon } from "@lume/icons"; import { LumeWindow, NostrAccount } from "@lume/system"; import { cn } from "@lume/utils"; import * as Popover from "@radix-ui/react-popover"; import { Outlet, createFileRoute } from "@tanstack/react-router"; import { Link } from "@tanstack/react-router"; +import { Menu, MenuItem } from "@tauri-apps/api/menu"; import { getCurrent } from "@tauri-apps/api/window"; -import { useEffect, useMemo, useState } from "react"; -import { toast } from "sonner"; +import { message } from "@tauri-apps/plugin-dialog"; +import { useCallback, useEffect, useMemo, useState } from "react"; export const Route = createFileRoute("/$account")({ beforeLoad: async () => { @@ -63,12 +59,12 @@ function Screen() { } function Accounts() { + const navigate = Route.useNavigate(); const { accounts } = Route.useRouteContext(); const { account } = Route.useParams(); const [windowWidth, setWindowWidth] = useState(null); - const navigate = Route.useNavigate(); const sortedList = useMemo(() => { const list = accounts; @@ -82,9 +78,33 @@ function Accounts() { return list; }, [accounts]); - const changeAccount = async (npub: string) => { + const showContextMenu = useCallback( + async (e: React.MouseEvent, npub: string) => { + e.preventDefault(); + + const menuItems = await Promise.all([ + MenuItem.new({ + text: "View Profile", + action: () => LumeWindow.openProfile(npub), + }), + MenuItem.new({ + text: "Open Settings", + action: () => LumeWindow.openSettings(), + }), + ]); + + const menu = await Menu.new({ + items: menuItems, + }); + + await menu.popup().catch((e) => console.error(e)); + }, + [], + ); + + const changeAccount = async (e: React.MouseEvent, npub: string) => { if (npub === account) { - return await LumeWindow.openProfile(account); + return showContextMenu(e, npub); } // Change current account and update signer @@ -102,7 +122,7 @@ function Accounts() { replace: true, }); } else { - toast.warning("Something wrong."); + await message("Something wrong.", { title: "Accounts", kind: "error" }); } }; @@ -135,7 +155,11 @@ function Accounts() { {sortedList .slice(0, windowWidth > 500 ? account.length : 2) .map((user) => ( - @@ -122,11 +122,11 @@ function Screen() { key={item} type="button" onClick={() => toggleUser(item)} - className="inline-flex items-center justify-between px-3 py-2 rounded-lg bg-white dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50" + className="inline-flex items-center justify-between px-3 py-2 bg-white rounded-lg dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50" > - +
@@ -138,7 +138,7 @@ function Screen() { )) ) : ( -
+
Empty.
)} @@ -153,11 +153,11 @@ function Screen() { key={item} type="button" onClick={() => toggleUser(item)} - className="inline-flex items-center justify-between px-3 py-2 rounded-lg bg-white dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50" + className="inline-flex items-center justify-between px-3 py-2 bg-white rounded-lg dark:bg-black/20 backdrop-blur-lg shadow-primary dark:ring-1 ring-neutral-800/50" > - +
@@ -166,7 +166,7 @@ function Screen() { )) ) : ( -
+

Find more user at{" "} submit()} disabled={isLoading || users.length < 1} - className="inline-flex items-center justify-center w-36 rounded-full h-9 bg-blue-500 text-white text-sm font-medium hover:bg-blue-600 disabled:opacity-50" + className="inline-flex items-center justify-center text-sm font-medium text-white bg-blue-500 rounded-full w-36 h-9 hover:bg-blue-600 disabled:opacity-50" > {isLoading ? : "Confirm"} diff --git a/apps/desktop2/src/routes/create-newsfeed.f2f.tsx b/apps/desktop2/src/routes/create-newsfeed.f2f.tsx index 5a653a7b..535b72fa 100644 --- a/apps/desktop2/src/routes/create-newsfeed.f2f.tsx +++ b/apps/desktop2/src/routes/create-newsfeed.f2f.tsx @@ -2,8 +2,8 @@ import { NostrAccount } from "@lume/system"; import type { ColumnRouteSearch } from "@lume/types"; import { Spinner } from "@lume/ui"; import { createFileRoute } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; import { useState } from "react"; -import { toast } from "sonner"; export const Route = createFileRoute("/create-newsfeed/f2f")({ validateSearch: (search: Record): ColumnRouteSearch => { @@ -24,8 +24,12 @@ function Screen() { const [isLoading, setIsLoading] = useState(false); const submit = async () => { - if (!npub.startsWith("npub1")) - return toast.warning("You must enter a valid npub."); + if (!npub.startsWith("npub1")) { + return await message("You must enter a valid npub.", { + title: "Create Newsfeed", + kind: "info", + }); + } try { setIsLoading(true); @@ -37,13 +41,16 @@ function Screen() { } } catch (e) { setIsLoading(false); - toast.error(String(e)); + await message(String(e), { + title: "Create Newsfeed", + kind: "error", + }); } }; return (

-
+

You already have a friend on Nostr? @@ -60,7 +67,7 @@ function Screen() {

-
diff --git a/apps/desktop2/src/routes/create-newsfeed.users.tsx b/apps/desktop2/src/routes/create-newsfeed.users.tsx index 0a951d53..e8827906 100644 --- a/apps/desktop2/src/routes/create-newsfeed.users.tsx +++ b/apps/desktop2/src/routes/create-newsfeed.users.tsx @@ -1,11 +1,11 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { Suspense, useState } from "react"; -import { Await, defer } from "@tanstack/react-router"; import { User } from "@/components/user"; -import { Spinner } from "@lume/ui"; -import { toast } from "sonner"; -import type { ColumnRouteSearch } from "@lume/types"; import { NostrAccount } from "@lume/system"; +import type { ColumnRouteSearch } from "@lume/types"; +import { Spinner } from "@lume/ui"; +import { createFileRoute } from "@tanstack/react-router"; +import { Await, defer } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; +import { Suspense, useState } from "react"; export const Route = createFileRoute("/create-newsfeed/users")({ validateSearch: (search: Record): ColumnRouteSearch => { @@ -59,16 +59,19 @@ function Screen() { } } catch (e) { setIsLoading(false); - toast.error(String(e)); + await message(String(e), { + title: "Create Group", + kind: "error", + }); } }; return ( -
+
+
- +
@@ -119,7 +122,7 @@ function Screen() { type="button" onClick={() => submit()} disabled={isLoading || follows.length < 1} - className="inline-flex items-center justify-center w-36 rounded-full h-9 bg-blue-500 text-white text-sm font-medium hover:bg-blue-600 disabled:opacity-50" + className="inline-flex items-center justify-center text-sm font-medium text-white bg-blue-500 rounded-full w-36 h-9 hover:bg-blue-600 disabled:opacity-50" > {isLoading ? : "Confirm"} diff --git a/apps/desktop2/src/routes/create-topic.tsx b/apps/desktop2/src/routes/create-topic.tsx index 4fea300c..e88ea7b8 100644 --- a/apps/desktop2/src/routes/create-topic.tsx +++ b/apps/desktop2/src/routes/create-topic.tsx @@ -4,8 +4,8 @@ import type { ColumnRouteSearch } from "@lume/types"; import { Spinner } from "@lume/ui"; import { TOPICS } from "@lume/utils"; import { createFileRoute } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; import { useState } from "react"; -import { toast } from "sonner"; type Topic = { title: string; @@ -53,7 +53,10 @@ function Screen() { } } catch (e) { setIsLoading(false); - toast.error(String(e)); + await message(String(e), { + title: "Create Topic", + kind: "error", + }); } }; diff --git a/apps/desktop2/src/routes/editor/-components/media.tsx b/apps/desktop2/src/routes/editor/-components/media.tsx index 5e2e6d75..a216a509 100644 --- a/apps/desktop2/src/routes/editor/-components/media.tsx +++ b/apps/desktop2/src/routes/editor/-components/media.tsx @@ -4,9 +4,9 @@ import { Spinner } from "@lume/ui"; import { insertImage, isImagePath } from "@lume/utils"; import type { UnlistenFn } from "@tauri-apps/api/event"; import { getCurrent } from "@tauri-apps/api/window"; +import { message } from "@tauri-apps/plugin-dialog"; import { useEffect, useState } from "react"; import { useSlateStatic } from "slate-react"; -import { toast } from "sonner"; export function MediaButton() { const editor = useSlateStatic(); @@ -24,7 +24,7 @@ export function MediaButton() { setLoading(false); } catch (e) { setLoading(false); - toast.error(`Upload failed, error: ${e}`); + await message(String(e), { title: "Upload", kind: "error" }); } }; diff --git a/apps/desktop2/src/routes/index.tsx b/apps/desktop2/src/routes/index.tsx index 32672c5e..8c5ff832 100644 --- a/apps/desktop2/src/routes/index.tsx +++ b/apps/desktop2/src/routes/index.tsx @@ -1,12 +1,12 @@ -import { PlusIcon, RelayIcon } from "@lume/icons"; -import { Spinner } from "@lume/ui"; import { User } from "@/components/user"; +import { PlusIcon, RelayIcon } from "@lume/icons"; +import { NostrAccount } from "@lume/system"; +import { Spinner } from "@lume/ui"; import { checkForAppUpdates, displayNpub } from "@lume/utils"; import { Link } from "@tanstack/react-router"; import { createFileRoute, redirect } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; import { useState } from "react"; -import { toast } from "sonner"; -import { NostrAccount } from "@lume/system"; export const Route = createFileRoute("/")({ beforeLoad: async () => { @@ -51,7 +51,10 @@ function Screen() { } } catch (e) { setLoading({ npub: "", status: false }); - toast.error(String(e)); + await message(String(e), { + title: "Account", + kind: "error", + }); } }; diff --git a/apps/desktop2/src/routes/search.tsx b/apps/desktop2/src/routes/search.tsx index 28628fa0..a44b7ea1 100644 --- a/apps/desktop2/src/routes/search.tsx +++ b/apps/desktop2/src/routes/search.tsx @@ -1,13 +1,13 @@ -import { SearchIcon } from "@lume/icons"; -import { type NostrEvent, Kind } from "@lume/types"; -import { Spinner } from "@lume/ui"; import { Note } from "@/components/note"; import { User } from "@/components/user"; -import { createFileRoute } from "@tanstack/react-router"; -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { useDebounce } from "use-debounce"; +import { SearchIcon } from "@lume/icons"; import { LumeEvent, LumeWindow } from "@lume/system"; +import { Kind, type NostrEvent } from "@lume/types"; +import { Spinner } from "@lume/ui"; +import { createFileRoute } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; +import { useEffect, useState } from "react"; +import { useDebounce } from "use-debounce"; export const Route = createFileRoute("/search")({ component: Screen, @@ -34,7 +34,10 @@ function Screen() { setEvents(sorted); } catch (e) { setLoading(false); - toast.error(String(e)); + await message(String(e), { + title: "Search", + kind: "error", + }); } }; diff --git a/apps/desktop2/src/routes/settings/backup.tsx b/apps/desktop2/src/routes/settings/backup.tsx index 88e7ebb1..a5c997d1 100644 --- a/apps/desktop2/src/routes/settings/backup.tsx +++ b/apps/desktop2/src/routes/settings/backup.tsx @@ -1,11 +1,11 @@ import { User } from "@/components/user"; import { NostrAccount } from "@lume/system"; -import { displayNpub, displayNsec } from "@lume/utils"; +import { displayNpub } from "@lume/utils"; import { createFileRoute } from "@tanstack/react-router"; import { invoke } from "@tauri-apps/api/core"; import { writeText } from "@tauri-apps/plugin-clipboard-manager"; +import { message } from "@tauri-apps/plugin-dialog"; import { useState } from "react"; -import { toast } from "sonner"; interface Account { npub: string; @@ -43,7 +43,7 @@ function Account({ account }: { account: string }) { await writeText(data); setCopied(true); } catch (e) { - toast.error(e); + await message(String(e), { title: "Backup", kind: "error" }); } }; diff --git a/apps/desktop2/src/routes/settings/relay.tsx b/apps/desktop2/src/routes/settings/relay.tsx index b0f638e3..33f48301 100644 --- a/apps/desktop2/src/routes/settings/relay.tsx +++ b/apps/desktop2/src/routes/settings/relay.tsx @@ -1,9 +1,9 @@ import { CancelIcon, PlusIcon } from "@lume/icons"; import { NostrQuery } from "@lume/system"; import { createFileRoute } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; -import { toast } from "sonner"; export const Route = createFileRoute("/settings/relay")({ loader: async () => { @@ -33,7 +33,7 @@ function Screen() { } } catch (e) { setIsLoading(false); - toast.error(String(e)); + await message(String(e), { title: "Relay", kind: "error" }); } }; @@ -42,22 +42,22 @@ function Screen() { }, [relayList]); return ( -
+
-

+

Connected Relays

-
+
{relays.map((relay) => (
- - + + {relay}
@@ -65,7 +65,7 @@ function Screen() { @@ -75,7 +75,7 @@ function Screen() {
@@ -99,21 +99,21 @@ function Screen() {
-

+

User Relays (NIP-65)

-
+

Lume will automatically connect to the user's relay list, but the manager function (like adding, removing, changing relay purpose) is not yet available.

-
+
{relayList.read?.map((relay) => (
{relay}
READ
@@ -122,7 +122,7 @@ function Screen() { {relayList.write?.map((relay) => (
{relay}
WRITE
@@ -131,7 +131,7 @@ function Screen() { {relayList.both?.map((relay) => (
{relay}
READ + WRITE
diff --git a/apps/desktop2/src/routes/settings/user.tsx b/apps/desktop2/src/routes/settings/user.tsx index b5b1d2f5..f7c4e209 100644 --- a/apps/desktop2/src/routes/settings/user.tsx +++ b/apps/desktop2/src/routes/settings/user.tsx @@ -5,9 +5,9 @@ import type { Metadata } from "@lume/types"; import { Spinner } from "@lume/ui"; import { Link } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router"; +import { message } from "@tauri-apps/plugin-dialog"; import { useState } from "react"; import { useForm } from "react-hook-form"; -import { toast } from "sonner"; export const Route = createFileRoute("/settings/user")({ beforeLoad: async () => { @@ -34,31 +34,31 @@ function Screen() { setLoading(false); } catch (e) { setLoading(false); - toast.error(String(e)); + await message(String(e), { title: "Profile", kind: "error" }); } }; return (
-
-
+
+
{profile.picture ? ( avatar ) : null}
-
+
{profile.display_name}
{profile.nip05} @@ -66,7 +66,7 @@ function Screen() {
Backup Account @@ -78,7 +78,7 @@ function Screen() { onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-3 mb-0" > -
+
-
+
-
+
-
+
-
+
-
+
diff --git a/apps/desktop2/src/routes/settings/zap.lazy.tsx b/apps/desktop2/src/routes/settings/zap.lazy.tsx index a730ad93..f82e1ad2 100644 --- a/apps/desktop2/src/routes/settings/zap.lazy.tsx +++ b/apps/desktop2/src/routes/settings/zap.lazy.tsx @@ -1,7 +1,7 @@ import { createLazyFileRoute } from "@tanstack/react-router"; import { invoke } from "@tauri-apps/api/core"; +import { message } from "@tauri-apps/plugin-dialog"; import { useState } from "react"; -import { toast } from "sonner"; export const Route = createLazyFileRoute("/settings/zap")({ component: Screen, @@ -9,7 +9,7 @@ export const Route = createLazyFileRoute("/settings/zap")({ function Screen() { return ( -
+
@@ -27,17 +27,17 @@ function Connection() { try { await invoke("set_nwc", { uri }); } catch (e) { - toast.error(String(e)); + await message(String(e), { title: "Zap", kind: "info" }); } }; return (
-
+
Connection
-
+