Compare commits

..
14 Commits
Author SHA1 Message Date
reya a5255fa503 chore: bump version 2024-07-31 12:51:41 +07:00
reya 954a17b541 fix: build on linux 2024-07-31 12:51:17 +07:00
reya a55b31b0e6 feat: add keyring support for linux and windows 2024-07-31 10:59:54 +07:00
reya bdf3ffd7bf fix: tray panel is missing 2024-07-19 13:58:32 +07:00
reya 07ce253f5b fix: child webview is not reposition after scroll 2024-07-19 13:10:29 +07:00
reya f3db010c74 chore: update tauri config 2024-07-19 10:01:20 +07:00
reya dcf2791fe5 fix: build 2024-07-19 09:28:03 +07:00
reya 8fcf3551d8 chore: bump version 2024-07-19 08:33:46 +07:00
reya 2d987849d8 feat: use native border in macos 2024-07-19 08:33:16 +07:00
reya 3b99926f3b feat: adapt latest changes in tauri v2 2024-07-19 08:25:36 +07:00
reya 113d69a4df chore: update deps 2024-07-18 18:57:28 +07:00
reya 5d12ba7216 fix: some screen too large 2024-07-03 14:36:54 +07:00
reya 72b59020b4 fix: build on linux and windows 2024-07-03 14:09:07 +07:00
XIAO YUandGitHub 4c323b9daa chore: Refactor code to use HashSet for account search results (#222) 2024-07-03 07:33:16 +07:00
53 changed files with 3729 additions and 3701 deletions
+4 -4
View File
@@ -18,10 +18,10 @@ jobs:
args: "--target x86_64-apple-darwin" args: "--target x86_64-apple-darwin"
- platform: "macos-latest" # for Intel based macs. - platform: "macos-latest" # for Intel based macs.
args: "--target universal-apple-darwin" args: "--target universal-apple-darwin"
#- platform: 'ubuntu-22.04' - platform: 'ubuntu-22.04'
# args: '' args: ''
#- platform: 'windows-latest' - platform: 'windows-latest'
# args: '--target x86_64-pc-windows-msvc' args: '--target x86_64-pc-windows-msvc'
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
+13 -14
View File
@@ -21,20 +21,20 @@
"@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.2",
"@tanstack/query-persist-client-core": "^5.49.1", "@tanstack/query-persist-client-core": "^5.51.9",
"@tanstack/react-query": "^5.49.2", "@tanstack/react-query": "^5.51.9",
"@tanstack/react-router": "^1.43.3", "@tanstack/react-router": "^1.45.4",
"embla-carousel-react": "^8.1.5", "embla-carousel-react": "^8.1.6",
"i18next": "^23.11.5", "i18next": "^23.12.1",
"i18next-resources-to-backend": "^1.2.1", "i18next-resources-to-backend": "^1.2.1",
"minidenticons": "^4.2.1", "minidenticons": "^4.2.1",
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"nostr-tools": "^2.7.0", "nostr-tools": "^2.7.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-currency-input-field": "^3.8.0", "react-currency-input-field": "^3.8.0",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.52.0", "react-hook-form": "^7.52.1",
"react-i18next": "^14.1.2", "react-i18next": "^14.1.3",
"react-string-replace": "^1.1.1", "react-string-replace": "^1.1.1",
"slate": "^0.103.0", "slate": "^0.103.0",
"slate-react": "^0.105.0", "slate-react": "^0.105.0",
@@ -45,17 +45,16 @@
"@lume/tailwindcss": "workspace:^", "@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^", "@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^", "@lume/types": "workspace:^",
"@tanstack/router-devtools": "^1.43.3", "@tanstack/router-devtools": "^1.45.4",
"@tanstack/router-vite-plugin": "^1.43.1", "@tanstack/router-vite-plugin": "^1.45.3",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@vitejs/plugin-react-swc": "^3.7.0", "@vitejs/plugin-react-swc": "^3.7.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"postcss": "^8.4.39", "postcss": "^8.4.39",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.6",
"typescript": "^5.5.2", "typescript": "^5.5.3",
"vite": "^5.3.2", "vite": "^5.3.4",
"vite-plugin-top-level-await": "^1.4.1",
"vite-tsconfig-paths": "^4.3.2" "vite-tsconfig-paths": "^4.3.2"
} }
} }
+8 -5
View File
@@ -3,7 +3,7 @@ import type { LumeColumn } from "@lume/types";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu"; import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
import { getCurrent } from "@tauri-apps/api/webviewWindow"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { memo, useCallback, useEffect, useRef, useState } from "react"; import { memo, useCallback, useEffect, useRef, useState } from "react";
type WindowEvent = { type WindowEvent = {
@@ -106,7 +106,7 @@ function Header({
const [isChanged, setIsChanged] = useState(false); const [isChanged, setIsChanged] = useState(false);
const saveNewTitle = async () => { const saveNewTitle = async () => {
const mainWindow = getCurrent(); const mainWindow = getCurrentWindow();
await mainWindow.emit("columns", { type: "set_title", label, title }); await mainWindow.emit("columns", { type: "set_title", label, title });
// update search params // update search params
@@ -135,7 +135,7 @@ function Header({
MenuItem.new({ MenuItem.new({
text: "Move left", text: "Move left",
action: async () => { action: async () => {
await getCurrent().emit("columns", { await getCurrentWindow().emit("columns", {
type: "move", type: "move",
label, label,
direction: "left", direction: "left",
@@ -145,7 +145,7 @@ function Header({
MenuItem.new({ MenuItem.new({
text: "Move right", text: "Move right",
action: async () => { action: async () => {
await getCurrent().emit("columns", { await getCurrentWindow().emit("columns", {
type: "move", type: "move",
label, label,
direction: "right", direction: "right",
@@ -156,7 +156,10 @@ function Header({
MenuItem.new({ MenuItem.new({
text: "Close", text: "Close",
action: async () => { action: async () => {
await getCurrent().emit("columns", { type: "remove", label }); await getCurrentWindow().emit("columns", {
type: "remove",
label,
});
}, },
}), }),
]); ]);
+8 -8
View File
@@ -5,7 +5,7 @@ import { NostrQuery } from "@lume/system";
import type { ColumnEvent, LumeColumn } from "@lume/types"; import type { ColumnEvent, LumeColumn } from "@lume/types";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import useEmblaCarousel from "embla-carousel-react"; import useEmblaCarousel from "embla-carousel-react";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
@@ -30,23 +30,23 @@ function Screen() {
}); });
const scrollPrev = useCallback(() => { const scrollPrev = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev(true); if (emblaApi) emblaApi.scrollPrev();
}, [emblaApi]); }, [emblaApi]);
const scrollNext = useCallback(() => { const scrollNext = useCallback(() => {
if (emblaApi) emblaApi.scrollNext(true); if (emblaApi) emblaApi.scrollNext();
}, [emblaApi]); }, [emblaApi]);
const emitScrollEvent = useCallback(() => { const emitScrollEvent = useCallback(() => {
getCurrent().emit("child_webview", { scroll: true }); getCurrentWindow().emit("child_webview", { scroll: true });
}, []); }, []);
const emitResizeEvent = useCallback(() => { const emitResizeEvent = useCallback(() => {
getCurrent().emit("child_webview", { resize: true, direction: "x" }); getCurrentWindow().emit("child_webview", { resize: true, direction: "x" });
}, []); }, []);
const openLumeStore = useCallback(async () => { const openLumeStore = useCallback(async () => {
await getCurrent().emit("columns", { await getCurrentWindow().emit("columns", {
type: "add", type: "add",
column: { column: {
label: "store", label: "store",
@@ -101,10 +101,10 @@ function Screen() {
switch (event.code) { switch (event.code) {
case "ArrowLeft": case "ArrowLeft":
if (emblaApi) emblaApi.scrollPrev(true); if (emblaApi) emblaApi.scrollPrev();
break; break;
case "ArrowRight": case "ArrowRight":
if (emblaApi) emblaApi.scrollNext(true); if (emblaApi) emblaApi.scrollNext();
break; break;
default: default:
break; break;
+3 -3
View File
@@ -9,7 +9,7 @@ import { LumeWindow, NostrAccount, NostrQuery } from "@lume/system";
import { cn } from "@lume/utils"; import { cn } from "@lume/utils";
import { Outlet, createFileRoute } from "@tanstack/react-router"; import { Outlet, createFileRoute } from "@tanstack/react-router";
import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu"; import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { message } from "@tauri-apps/plugin-dialog"; import { message } from "@tauri-apps/plugin-dialog";
import { memo, useCallback, useState } from "react"; import { memo, useCallback, useState } from "react";
@@ -30,7 +30,7 @@ function Screen() {
const { settings, platform } = Route.useRouteContext(); const { settings, platform } = Route.useRouteContext();
const openLumeStore = async () => { const openLumeStore = async () => {
await getCurrent().emit("columns", { await getCurrentWindow().emit("columns", {
type: "add", type: "add",
column: { column: {
label: "store", label: "store",
@@ -137,7 +137,7 @@ const Accounts = memo(function Accounts() {
if (select) { if (select) {
// Reset current columns // Reset current columns
await getCurrent().emit("columns", { type: "reset" }); await getCurrentWindow().emit("columns", { type: "reset" });
// Redirect to new account // Redirect to new account
return navigate({ return navigate({
-1
View File
@@ -11,7 +11,6 @@ interface RouterContext {
export const Route = createRootRouteWithContext<RouterContext>()({ export const Route = createRootRouteWithContext<RouterContext>()({
component: () => <Outlet />, component: () => <Outlet />,
pendingComponent: Pending, pendingComponent: Pending,
wrapInSuspense: true,
}); });
function Pending() { function Pending() {
+3 -3
View File
@@ -1,4 +1,4 @@
import { Box, Container } from "@lume/ui"; import { Container } from "@lume/ui";
import { Outlet, createLazyFileRoute } from "@tanstack/react-router"; import { Outlet, createLazyFileRoute } from "@tanstack/react-router";
export const Route = createLazyFileRoute("/auth")({ export const Route = createLazyFileRoute("/auth")({
@@ -8,9 +8,9 @@ export const Route = createLazyFileRoute("/auth")({
function Screen() { function Screen() {
return ( return (
<Container withDrag> <Container withDrag>
<Box className="px-3 pt-3"> <div className="max-w-sm mx-auto size-full">
<Outlet /> <Outlet />
</Box> </div>
</Container> </Container>
); );
} }
@@ -55,12 +55,13 @@ function Screen() {
}; };
return ( return (
<div className="flex flex-col items-center justify-center w-full h-full gap-6 px-5 mx-auto xl:max-w-xl"> <div className="flex flex-col items-center justify-center size-full gap-4">
<div className="text-center"> <div className="text-center">
<h3 className="text-xl font-semibold">Let's set up your profile.</h3> <h3 className="text-xl font-semibold">Let's set up your profile.</h3>
</div> </div>
<div> <form onSubmit={handleSubmit(onSubmit)} className="w-full mb-0">
<div className="relative rounded-full size-24 bg-gradient-to-tr from-orange-100 via-red-50 to-blue-200"> <div className="flex flex-col gap-3 w-full p-3 overflow-hidden bg-white rounded-xl shadow-primary dark:bg-white/10 dark:ring-1 ring-white/15">
<div className="self-center relative rounded-full size-20 bg-neutral-200 dark:bg-white/70 my-3">
{picture ? ( {picture ? (
<img <img
src={picture} src={picture}
@@ -77,11 +78,6 @@ function Screen() {
<PlusIcon className="size-8" /> <PlusIcon className="size-8" />
</AvatarUploader> </AvatarUploader>
</div> </div>
</div>
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col w-full gap-3"
>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label htmlFor="display_name" className="font-medium"> <label htmlFor="display_name" className="font-medium">
Display Name * Display Name *
@@ -129,9 +125,11 @@ function Screen() {
className="px-3 border-transparent rounded-lg h-11 bg-neutral-100 placeholder:text-neutral-500 focus:border-blue-500 focus:ring-0 dark:bg-white/10 dark:placeholder:text-neutral-400" className="px-3 border-transparent rounded-lg h-11 bg-neutral-100 placeholder:text-neutral-500 focus:border-blue-500 focus:ring-0 dark:bg-white/10 dark:placeholder:text-neutral-400"
/> />
</div> </div>
</div>
<button <button
type="submit" type="submit"
className="inline-flex items-center justify-center w-full mt-3 font-semibold text-white bg-blue-500 rounded-lg h-11 shrink-0 hover:bg-blue-600 disabled:opacity-50" disabled={loading}
className="inline-flex items-center justify-center w-full h-9 mt-4 text-sm font-semibold text-white bg-blue-500 rounded-lg shrink-0 hover:bg-blue-600 disabled:opacity-50"
> >
{loading ? <Spinner /> : "Continue"} {loading ? <Spinner /> : "Continue"}
</button> </button>
@@ -38,11 +38,12 @@ function Screen() {
}; };
return ( return (
<div className="flex flex-col items-center justify-center w-full h-full gap-6 px-5 mx-auto xl:max-w-xl"> <div className="flex flex-col items-center justify-center size-full gap-4">
<div className="text-center"> <div className="text-center">
<h3 className="text-xl font-semibold">Continue with Private Key</h3> <h3 className="text-xl font-semibold">Continue with Private Key</h3>
</div> </div>
<div className="flex flex-col w-full gap-3"> <div className="flex flex-col w-full">
<div className="flex flex-col gap-3 w-full p-3 overflow-hidden bg-white rounded-xl shadow-primary dark:bg-white/10 dark:ring-1 ring-white/15">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label <label
htmlFor="key" htmlFor="key"
@@ -74,11 +75,12 @@ function Screen() {
className="px-3 border-transparent rounded-lg h-11 bg-neutral-100 placeholder:text-neutral-600 focus:border-blue-500 focus:ring-0 dark:bg-white/10 dark:placeholder:text-neutral-400" className="px-3 border-transparent rounded-lg h-11 bg-neutral-100 placeholder:text-neutral-600 focus:border-blue-500 focus:ring-0 dark:bg-white/10 dark:placeholder:text-neutral-400"
/> />
</div> </div>
</div>
<button <button
type="button" type="button"
onClick={() => submit()} onClick={() => submit()}
disabled={loading} disabled={loading}
className="inline-flex items-center justify-center w-full mt-3 font-semibold text-white bg-blue-500 rounded-lg h-11 shrink-0 hover:bg-blue-600 disabled:opacity-50" className="inline-flex items-center justify-center w-full h-9 mt-4 text-sm font-semibold text-white bg-blue-500 rounded-lg shrink-0 hover:bg-blue-600 disabled:opacity-50"
> >
{loading ? <Spinner /> : "Login"} {loading ? <Spinner /> : "Login"}
</button> </button>
@@ -37,12 +37,12 @@ function Screen() {
}; };
return ( return (
<div className="flex flex-col items-center justify-center w-full h-full gap-6 px-5 mx-auto xl:max-w-xl"> <div className="flex flex-col items-center justify-center size-full gap-4">
<div className="text-center"> <div className="text-center">
<h3 className="text-xl font-semibold">Continue with Nostr Connect</h3> <h3 className="text-xl font-semibold">Continue with Nostr Connect</h3>
</div> </div>
<div className="flex flex-col w-full gap-3"> <div className="flex flex-col w-full">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1 w-full p-3 overflow-hidden bg-white rounded-xl shadow-primary dark:bg-white/10 dark:ring-1 ring-white/15">
<label <label
htmlFor="uri" htmlFor="uri"
className="font-medium text-neutral-900 dark:text-neutral-100" className="font-medium text-neutral-900 dark:text-neutral-100"
@@ -63,7 +63,7 @@ function Screen() {
type="button" type="button"
onClick={() => submit()} onClick={() => submit()}
disabled={loading} disabled={loading}
className="inline-flex items-center justify-center w-full mt-3 font-semibold text-white bg-blue-500 rounded-lg h-11 shrink-0 hover:bg-blue-600 disabled:opacity-50" className="inline-flex items-center justify-center w-full h-9 mt-4 text-sm font-semibold text-white bg-blue-500 rounded-lg shrink-0 hover:bg-blue-600 disabled:opacity-50"
> >
{loading ? <Spinner /> : "Login"} {loading ? <Spinner /> : "Login"}
</button> </button>
@@ -65,9 +65,9 @@ function Screen() {
data-tauri-drag-region data-tauri-drag-region
className="absolute top-0 left-0 h-14 w-full" className="absolute top-0 left-0 h-14 w-full"
/> />
<div className="flex items-end justify-center flex-1 w-full px-4 pb-10"> <div className="flex items-end justify-center flex-1 w-full px-4 pb-4">
<div className="text-center"> <div className="text-center">
<h2 className="text-2xl font-semibold">Customize Bootstrap Relays</h2> <h2 className="text-xl font-semibold">Customize Bootstrap Relays</h2>
</div> </div>
</div> </div>
<div className="flex flex-col items-center flex-1 w-full"> <div className="flex flex-col items-center flex-1 w-full">
@@ -3,7 +3,7 @@ import { NostrQuery } from "@lume/system";
import { Spinner } from "@lume/ui"; import { Spinner } from "@lume/ui";
import { insertImage, isImagePath } from "@lume/utils"; import { insertImage, isImagePath } from "@lume/utils";
import type { UnlistenFn } from "@tauri-apps/api/event"; import type { UnlistenFn } from "@tauri-apps/api/event";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { message } from "@tauri-apps/plugin-dialog"; import { message } from "@tauri-apps/plugin-dialog";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSlateStatic } from "slate-react"; import { useSlateStatic } from "slate-react";
@@ -32,7 +32,7 @@ export function MediaButton() {
let unlisten: UnlistenFn = undefined; let unlisten: UnlistenFn = undefined;
async function listenFileDrop() { async function listenFileDrop() {
const window = getCurrent(); const window = getCurrentWindow();
if (!unlisten) { if (!unlisten) {
unlisten = await window.listen("tauri://file-drop", async (event) => { unlisten = await window.listen("tauri://file-drop", async (event) => {
// @ts-ignore, lfg !!! // @ts-ignore, lfg !!!
+8 -5
View File
@@ -3,7 +3,7 @@ import { LumeEvent, NostrQuery } from "@lume/system";
import type { Meta } from "@lume/types"; import type { Meta } from "@lume/types";
import * as ScrollArea from "@radix-ui/react-scroll-area"; import * as ScrollArea from "@radix-ui/react-scroll-area";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { Virtualizer } from "virtua"; import { Virtualizer } from "virtua";
import NoteParent from "./-components/parent"; import NoteParent from "./-components/parent";
@@ -83,14 +83,17 @@ function ReplyList() {
const [replies, setReplies] = useState<LumeEvent[]>([]); const [replies, setReplies] = useState<LumeEvent[]>([]);
useEffect(() => { useEffect(() => {
const unlistenEvent = getCurrent().listen<Payload>("new_reply", (data) => { const unlistenEvent = getCurrentWindow().listen<Payload>(
"new_reply",
(data) => {
const event = LumeEvent.from(data.payload.raw, data.payload.parsed); const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
setReplies((prev) => [event, ...prev]); setReplies((prev) => [event, ...prev]);
}); },
);
const unlistenWindow = getCurrent().onCloseRequested(async () => { const unlistenWindow = getCurrentWindow().onCloseRequested(async () => {
await event.unlistenEventReply(); await event.unlistenEventReply();
await getCurrent().destroy(); await getCurrentWindow().destroy();
}); });
return () => { return () => {
+2 -2
View File
@@ -10,7 +10,7 @@ import * as ScrollArea from "@radix-ui/react-scroll-area";
import { type InfiniteData, useInfiniteQuery } from "@tanstack/react-query"; import { type InfiniteData, useInfiniteQuery } from "@tanstack/react-query";
import { createFileRoute, redirect } from "@tanstack/react-router"; import { createFileRoute, redirect } from "@tanstack/react-router";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { Virtualizer } from "virtua"; import { Virtualizer } from "virtua";
@@ -195,7 +195,7 @@ function Listerner() {
}; };
useEffect(() => { useEffect(() => {
const unlisten = getCurrent().listen<Payload>("new_event", (data) => { const unlisten = getCurrentWindow().listen<Payload>("new_event", (data) => {
const event = LumeEvent.from(data.payload.raw, data.payload.parsed); const event = LumeEvent.from(data.payload.raw, data.payload.parsed);
setEvents((prev) => [event, ...prev]); setEvents((prev) => [event, ...prev]);
}); });
+6 -2
View File
@@ -15,12 +15,15 @@ import { useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu"; import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { open } from "@tauri-apps/plugin-shell"; import { open } from "@tauri-apps/plugin-shell";
import { type ReactNode, useCallback, useEffect, useRef } from "react"; import { type ReactNode, useCallback, useEffect, useRef } from "react";
import { Virtualizer } from "virtua"; import { Virtualizer } from "virtua";
export const Route = createFileRoute("/panel/$account")({ export const Route = createFileRoute("/panel/$account")({
beforeLoad: async ({ context }) => {
console.log(context);
},
component: Screen, component: Screen,
}); });
@@ -30,6 +33,7 @@ function Screen() {
const { isLoading, data } = useQuery({ const { isLoading, data } = useQuery({
queryKey: ["notification", account], queryKey: ["notification", account],
queryFn: async () => { queryFn: async () => {
console.log(queryClient);
const events = await NostrQuery.getNotifications(); const events = await NostrQuery.getNotifications();
return events; return events;
}, },
@@ -110,7 +114,7 @@ function Screen() {
}, []); }, []);
useEffect(() => { useEffect(() => {
const unlisten = getCurrent().listen("notification", async (data) => { const unlisten = getCurrentWindow().listen("notification", async (data) => {
const event: LumeEvent = JSON.parse(data.payload as string); const event: LumeEvent = JSON.parse(data.payload as string);
await queryClient.setQueryData( await queryClient.setQueryData(
["notification", account], ["notification", account],
@@ -1,7 +1,7 @@
import { Button, init } from "@getalby/bitcoin-connect-react"; import { Button, init } from "@getalby/bitcoin-connect-react";
import { NostrAccount } from "@lume/system"; import { NostrAccount } from "@lume/system";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { getCurrent } from "@tauri-apps/api/webviewWindow"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
export const Route = createFileRoute("/settings/bitcoin-connect")({ export const Route = createFileRoute("/settings/bitcoin-connect")({
beforeLoad: () => { beforeLoad: () => {
@@ -17,7 +17,7 @@ export const Route = createFileRoute("/settings/bitcoin-connect")({
function Screen() { function Screen() {
const setNwcUri = async (uri: string) => { const setNwcUri = async (uri: string) => {
const cmd = await NostrAccount.setWallet(uri); const cmd = await NostrAccount.setWallet(uri);
if (cmd) getCurrent().close(); if (cmd) getCurrentWebviewWindow().close();
}; };
return ( return (
+2 -2
View File
@@ -3,7 +3,7 @@ import type { LumeColumn } from "@lume/types";
import * as ScrollArea from "@radix-ui/react-scroll-area"; import * as ScrollArea from "@radix-ui/react-scroll-area";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { resolveResource } from "@tauri-apps/api/path"; import { resolveResource } from "@tauri-apps/api/path";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { readTextFile } from "@tauri-apps/plugin-fs"; import { readTextFile } from "@tauri-apps/plugin-fs";
export const Route = createFileRoute("/store")({ export const Route = createFileRoute("/store")({
@@ -24,7 +24,7 @@ function Screen() {
const { officialColumns } = Route.useRouteContext(); const { officialColumns } = Route.useRouteContext();
const install = async (column: LumeColumn) => { const install = async (column: LumeColumn) => {
const mainWindow = getCurrent(); const mainWindow = getCurrentWindow();
await mainWindow.emit("columns", { type: "add", column }); await mainWindow.emit("columns", { type: "add", column });
}; };
+2 -2
View File
@@ -1,7 +1,7 @@
import { User } from "@/components/user"; import { User } from "@/components/user";
import { NostrQuery } from "@lume/system"; import { NostrQuery } from "@lume/system";
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { getCurrent } from "@tauri-apps/api/webviewWindow"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { message } from "@tauri-apps/plugin-dialog"; import { message } from "@tauri-apps/plugin-dialog";
import { useState } from "react"; import { useState } from "react";
import CurrencyInput from "react-currency-input-field"; import CurrencyInput from "react-currency-input-field";
@@ -35,7 +35,7 @@ function Screen() {
if (val) { if (val) {
setIsCompleted(true); setIsCompleted(true);
// close current window // close current window
await getCurrent().close(); await getCurrentWebviewWindow().close();
} }
} catch (e) { } catch (e) {
setIsLoading(false); setIsLoading(false);
+1 -10
View File
@@ -1,19 +1,10 @@
import { TanStackRouterVite } from "@tanstack/router-vite-plugin"; import { TanStackRouterVite } from "@tanstack/router-vite-plugin";
import react from "@vitejs/plugin-react-swc"; import react from "@vitejs/plugin-react-swc";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import topLevelAwait from "vite-plugin-top-level-await";
import viteTsconfigPaths from "vite-tsconfig-paths"; import viteTsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [react(), viteTsconfigPaths(), TanStackRouterVite()],
react(),
viteTsconfigPaths(),
topLevelAwait({
promiseExportName: "__tla",
promiseImportName: (i) => `__tla_${i}`,
}),
TanStackRouterVite(),
],
build: { build: {
outDir: "../../dist", outDir: "../../dist",
}, },
+3 -3
View File
@@ -13,12 +13,12 @@
"@astrojs/check": "^0.5.10", "@astrojs/check": "^0.5.10",
"@astrojs/tailwind": "^5.1.0", "@astrojs/tailwind": "^5.1.0",
"@fontsource/alice": "^5.0.13", "@fontsource/alice": "^5.0.13",
"astro": "^4.11.3", "astro": "^4.11.6",
"astro-seo-meta": "^4.1.1", "astro-seo-meta": "^4.1.1",
"astro-seo-schema": "^4.0.2", "astro-seo-schema": "^4.0.2",
"schema-dts": "^1.1.2", "schema-dts": "^1.1.2",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.6",
"typescript": "^5.5.2" "typescript": "^5.5.3"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.13" "@tailwindcss/typography": "^0.5.13"
+4 -1
View File
@@ -4,7 +4,10 @@
"enabled": true "enabled": true
}, },
"files": { "files": {
"ignore": ["apps/desktop2/src/router.gen.ts"] "ignore": [
"apps/desktop2/src/router.gen.ts",
"packages/system/src/commands.ts"
]
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,
+12 -12
View File
@@ -12,7 +12,7 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.8.3", "@biomejs/biome": "^1.8.3",
"@tauri-apps/cli": "2.0.0-beta.20", "@tauri-apps/cli": "2.0.0-beta.22",
"turbo": "^1.13.4" "turbo": "^1.13.4"
}, },
"packageManager": "pnpm@8.9.0", "packageManager": "pnpm@8.9.0",
@@ -20,16 +20,16 @@
"node": ">=18" "node": ">=18"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.0.0-beta.13", "@tauri-apps/api": "2.0.0-beta.15",
"@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.3", "@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.5",
"@tauri-apps/plugin-dialog": "2.0.0-beta.5", "@tauri-apps/plugin-dialog": "2.0.0-beta.7",
"@tauri-apps/plugin-fs": "2.0.0-beta.5", "@tauri-apps/plugin-fs": "2.0.0-beta.7",
"@tauri-apps/plugin-http": "2.0.0-beta.5", "@tauri-apps/plugin-http": "2.0.0-beta.8",
"@tauri-apps/plugin-os": "github:tauri-apps/tauri-plugin-os#v2", "@tauri-apps/plugin-os": "2.0.0-beta.7",
"@tauri-apps/plugin-process": "2.0.0-beta.5", "@tauri-apps/plugin-process": "2.0.0-beta.7",
"@tauri-apps/plugin-shell": "2.0.0-beta.6", "@tauri-apps/plugin-shell": "2.0.0-beta.8",
"@tauri-apps/plugin-updater": "2.0.0-beta.5", "@tauri-apps/plugin-updater": "2.0.0-beta.7",
"@tauri-apps/plugin-upload": "2.0.0-beta.6", "@tauri-apps/plugin-upload": "2.0.0-beta.8",
"@tauri-apps/plugin-window-state": "2.0.0-beta.6" "@tauri-apps/plugin-window-state": "2.0.0-beta.8"
} }
} }
+1 -1
View File
@@ -9,6 +9,6 @@
"devDependencies": { "devDependencies": {
"@lume/tsconfig": "workspace:*", "@lume/tsconfig": "workspace:*",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"typescript": "^5.5.2" "typescript": "^5.5.3"
} }
} }
+4 -4
View File
@@ -5,15 +5,15 @@
"main": "./src/index.ts", "main": "./src/index.ts",
"dependencies": { "dependencies": {
"@lume/utils": "workspace:^", "@lume/utils": "workspace:^",
"@tanstack/query-persist-client-core": "^5.49.1", "@tanstack/query-persist-client-core": "^5.51.9",
"@tanstack/react-query": "^5.49.2", "@tanstack/react-query": "^5.51.9",
"nostr-tools": "^2.7.0", "nostr-tools": "^2.7.1",
"react": "^18.3.1" "react": "^18.3.1"
}, },
"devDependencies": { "devDependencies": {
"@lume/tsconfig": "workspace:^", "@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^", "@lume/types": "workspace:^",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"typescript": "^5.5.2" "typescript": "^5.5.3"
} }
} }
+1 -6
View File
@@ -4,12 +4,7 @@ import { type Result, commands } from "./commands";
export const NostrAccount = { export const NostrAccount = {
getAccounts: async () => { getAccounts: async () => {
const query = await commands.getAccounts(); const query = await commands.getAccounts();
return query;
if (query.status === "ok") {
return query.data;
} else {
return [];
}
}, },
loadAccount: async (npub: string) => { loadAccount: async (npub: string) => {
const bunker: string = localStorage.getItem(`${npub}_bunker`); const bunker: string = localStorage.getItem(`${npub}_bunker`);
+2 -7
View File
@@ -44,13 +44,8 @@ try {
else return { status: "error", error: e as any }; else return { status: "error", error: e as any };
} }
}, },
async getAccounts() : Promise<Result<string[], string>> { async getAccounts() : Promise<string[]> {
try { return await TAURI_INVOKE("get_accounts");
return { status: "ok", data: await TAURI_INVOKE("get_accounts") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
}, },
async createAccount() : Promise<Result<Account, string>> { async createAccount() : Promise<Result<Account, string>> {
try { try {
+3 -3
View File
@@ -1,6 +1,6 @@
import type { LumeColumn, Metadata, NostrEvent, Relay } from "@lume/types"; import type { LumeColumn, Metadata, NostrEvent, Relay } from "@lume/types";
import { resolveResource } from "@tauri-apps/api/path"; import { resolveResource } from "@tauri-apps/api/path";
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
import { readFile, readTextFile } from "@tauri-apps/plugin-fs"; import { readFile, readTextFile } from "@tauri-apps/plugin-fs";
import { relaunch } from "@tauri-apps/plugin-process"; import { relaunch } from "@tauri-apps/plugin-process";
@@ -201,7 +201,7 @@ export const NostrQuery = {
} }
}, },
listenLocalEvent: async () => { listenLocalEvent: async () => {
const label = getCurrent().label; const label = getCurrentWindow().label;
const query = await commands.listenLocalEvent(label); const query = await commands.listenLocalEvent(label);
if (query.status === "ok") { if (query.status === "ok") {
@@ -394,7 +394,7 @@ export const NostrQuery = {
} }
}, },
unlisten: async (id?: string) => { unlisten: async (id?: string) => {
const label = id ? id : getCurrent().label; const label = id ? id : getCurrentWindow().label;
const query = await commands.unlisten(label); const query = await commands.unlisten(label);
if (query.status === "ok") { if (query.status === "ok") {
+1 -1
View File
@@ -15,7 +15,7 @@
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",
"tailwind-gradient-mask-image": "^1.2.0", "tailwind-gradient-mask-image": "^1.2.0",
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.6",
"tailwindcss-content-visibility": "^0.2.0" "tailwindcss-content-visibility": "^0.2.0"
} }
} }
+1 -1
View File
@@ -9,6 +9,6 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5.5.2" "typescript": "^5.5.3"
} }
} }
+2 -2
View File
@@ -15,7 +15,7 @@
"@lume/tsconfig": "workspace:^", "@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^", "@lume/types": "workspace:^",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"tailwindcss": "^3.4.4", "tailwindcss": "^3.4.6",
"typescript": "^5.5.2" "typescript": "^5.5.3"
} }
} }
+3 -3
View File
@@ -12,7 +12,7 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"light-bolt11-decoder": "^3.1.1", "light-bolt11-decoder": "^3.1.1",
"nostr-tools": "^2.7.0", "nostr-tools": "^2.7.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"slate": "^0.103.0", "slate": "^0.103.0",
@@ -23,7 +23,7 @@
"@lume/types": "workspace:^", "@lume/types": "workspace:^",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.4.0",
"typescript": "^5.5.2" "typescript": "^5.5.3"
} }
} }
+836 -927
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
tab_spaces=2
+273 -219
View File
File diff suppressed because it is too large Load Diff
+10 -5
View File
@@ -11,7 +11,7 @@ rust-version = "1.70"
tauri-build = { version = "2.0.0-beta", features = [] } tauri-build = { version = "2.0.0-beta", features = [] }
[dependencies] [dependencies]
nostr-sdk = { version = "0.32", features = ["sqlite"] } nostr-sdk = { version = "0.33", features = ["sqlite"] }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
@@ -33,19 +33,23 @@ tauri-plugin-process = { git = "https://github.com/tauri-apps/plugins-workspace"
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tauri-plugin-updater = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } tauri-plugin-upload = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
tauri-specta = { git = "https://github.com/reyamir/tauri-specta", features = [ tauri-specta = { git = "https://github.com/reyamir/tauri-specta", branch = "feat/tauri-v2", features = [
"typescript", "typescript",
] } ] }
tauri-plugin-theme = "0.4.1" tauri-plugin-theme = "0.4.1"
tauri-plugin-decorum = "0.1.0" tauri-plugin-decorum = { git = "https://github.com/reyamir/tauri-plugin-decorum", branch = "feat/tauri-v2" }
specta = "^2.0.0-rc.12" specta = "^2.0.0-rc.12"
keyring = "2"
keyring-search = "0.2.0"
reqwest = "0.12.4" reqwest = "0.12.4"
url = "2.5.0" url = "2.5.0"
futures = "0.3.30" futures = "0.3.30"
linkify = "0.10.0" linkify = "0.10.0"
regex = "1.10.4" regex = "1.10.4"
keyring = { version = "3", features = [
"apple-native",
"windows-native",
"sync-secret-service",
] }
keyring-search = "1.2.0"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25.0" cocoa = "0.25.0"
@@ -53,6 +57,7 @@ objc = "0.2.7"
rand = "0.8.5" rand = "0.8.5"
monitor = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" } monitor = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }
border = { git = "https://github.com/ahkohd/tauri-toolkit", branch = "v2" }
[profile.release] [profile.release]
codegen-units = 1 codegen-units = 1
File diff suppressed because one or more lines are too long
+14
View File
@@ -6738,6 +6738,13 @@
"window:allow-set-title" "window:allow-set-title"
] ]
}, },
{
"description": "window:allow-set-title-bar-style -> Enables the set_title_bar_style command without any pre-configured scope.",
"type": "string",
"enum": [
"window:allow-set-title-bar-style"
]
},
{ {
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.", "description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
"type": "string", "type": "string",
@@ -7179,6 +7186,13 @@
"window:deny-set-title" "window:deny-set-title"
] ]
}, },
{
"description": "window:deny-set-title-bar-style -> Denies the set_title_bar_style command without any pre-configured scope.",
"type": "string",
"enum": [
"window:deny-set-title-bar-style"
]
},
{ {
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.", "description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
"type": "string", "type": "string",
+14
View File
@@ -6738,6 +6738,13 @@
"window:allow-set-title" "window:allow-set-title"
] ]
}, },
{
"description": "window:allow-set-title-bar-style -> Enables the set_title_bar_style command without any pre-configured scope.",
"type": "string",
"enum": [
"window:allow-set-title-bar-style"
]
},
{ {
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.", "description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
"type": "string", "type": "string",
@@ -7179,6 +7186,13 @@
"window:deny-set-title" "window:deny-set-title"
] ]
}, },
{
"description": "window:deny-set-title-bar-style -> Denies the set_title_bar_style command without any pre-configured scope.",
"type": "string",
"enum": [
"window:deny-set-title-bar-style"
]
},
{ {
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.", "description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
"type": "string", "type": "string",
+2 -5
View File
@@ -1,6 +1,6 @@
use std::ffi::CString; use std::ffi::CString;
use tauri::{AppHandle, Manager, WebviewWindow}; use tauri::{AppHandle, Emitter, Listener, Manager, WebviewWindow};
use tauri_nspanel::{ use tauri_nspanel::{
block::ConcreteBlock, block::ConcreteBlock,
cocoa::{ cocoa::{
@@ -27,12 +27,9 @@ pub fn swizzle_to_menubar_panel(app_handle: &tauri::AppHandle) {
let handle = app_handle.clone(); let handle = app_handle.clone();
panel_delegate.set_listener(Box::new(move |delegate_name: String| { panel_delegate.set_listener(Box::new(move |delegate_name: String| {
match delegate_name.as_str() { if delegate_name.as_str() == "window_did_resign_key" {
"window_did_resign_key" => {
let _ = handle.emit("menubar_panel_did_resign_key", ()); let _ = handle.emit("menubar_panel_did_resign_key", ());
} }
_ => (),
}
})); }));
panel.set_level(NSMainMenuWindowLevel + 1); panel.set_level(NSMainMenuWindowLevel + 1);
+2
View File
@@ -1,3 +1,5 @@
#[cfg(target_os = "macos")]
pub mod fns; pub mod fns;
#[cfg(target_os = "macos")]
pub mod tray; pub mod tray;
pub mod window; pub mod window;
+33 -13
View File
@@ -1,16 +1,21 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
#[cfg(target_os = "macos")]
use border::WebviewWindowExt as BorderWebviewWindowExt;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use cocoa::{appkit::NSApp, base::nil, foundation::NSString}; use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specta::Type; use specta::Type;
#[cfg(not(target_os = "linux"))]
use tauri::utils::config::WindowEffectsConfig; use tauri::utils::config::WindowEffectsConfig;
#[cfg(not(target_os = "linux"))]
use tauri::window::Effect; use tauri::window::Effect;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use tauri::TitleBarStyle; use tauri::TitleBarStyle;
use tauri::WebviewWindowBuilder; use tauri::WebviewWindowBuilder;
use tauri::{LogicalPosition, LogicalSize, Manager, State, WebviewUrl}; use tauri::{LogicalPosition, LogicalSize, Manager, State, WebviewUrl};
#[cfg(not(target_os = "linux"))]
use tauri_plugin_decorum::WebviewWindowExt; use tauri_plugin_decorum::WebviewWindowExt;
use url::Url; use url::Url;
@@ -177,6 +182,7 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
.title_bar_style(TitleBarStyle::Overlay) .title_bar_style(TitleBarStyle::Overlay)
.minimizable(window.minimizable) .minimizable(window.minimizable)
.maximizable(window.maximizable) .maximizable(window.maximizable)
.transparent(true)
.effects(WindowEffectsConfig { .effects(WindowEffectsConfig {
state: None, state: None,
effects: vec![Effect::UnderWindowBackground], effects: vec![Effect::UnderWindowBackground],
@@ -187,10 +193,16 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
.unwrap(); .unwrap();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let window = WebviewWindowBuilder::new(&app_handle, label, WebviewUrl::App(PathBuf::from(url))) let window = WebviewWindowBuilder::new(
.title(title) &app_handle,
.min_inner_size(width, height) &window.label,
.inner_size(width, height) WebviewUrl::App(PathBuf::from(window.url)),
)
.title(&window.title)
.min_inner_size(window.width, window.height)
.inner_size(window.width, window.height)
.minimizable(window.minimizable)
.maximizable(window.maximizable)
.effects(WindowEffectsConfig { .effects(WindowEffectsConfig {
state: None, state: None,
effects: vec![Effect::Mica], effects: vec![Effect::Mica],
@@ -201,19 +213,26 @@ pub fn open_window(window: Window, app_handle: tauri::AppHandle) -> Result<(), S
.unwrap(); .unwrap();
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
let window = WebviewWindowBuilder::new(&app_handle, label, WebviewUrl::App(PathBuf::from(url))) let window = WebviewWindowBuilder::new(
.title(title) &app_handle,
.min_inner_size(width, height) &window.label,
.inner_size(width, height) WebviewUrl::App(PathBuf::from(window.url)),
)
.title(&window.title)
.min_inner_size(window.width, window.height)
.inner_size(window.width, window.height)
.minimizable(window.minimizable)
.maximizable(window.maximizable)
.build() .build()
.unwrap(); .unwrap();
// Set decoration // Set decoration
#[cfg(not(target_os = "linux"))]
window.create_overlay_titlebar().unwrap(); window.create_overlay_titlebar().unwrap();
// Make main window transparent // Restore native border
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
window.make_transparent().unwrap(); window.add_border(None);
} }
Ok(()) Ok(())
@@ -230,14 +249,15 @@ pub fn open_main_window(app: tauri::AppHandle) {
let _ = window.set_focus(); let _ = window.set_focus();
}; };
} else { } else {
let window = WebviewWindowBuilder::from_config(&app, app.config().app.windows.first().unwrap()) let window =
WebviewWindowBuilder::from_config(&app, app.config().app.windows.first().unwrap())
.unwrap() .unwrap()
.build() .build()
.unwrap(); .unwrap();
// Make main window transparent // Restore native border
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
window.make_transparent().unwrap(); window.add_border(None);
} }
} }
+35 -20
View File
@@ -9,6 +9,8 @@ extern crate cocoa;
#[macro_use] #[macro_use]
extern crate objc; extern crate objc;
#[cfg(target_os = "macos")]
use border::WebviewWindowExt as BorderWebviewWindowExt;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specta::Type; use specta::Type;
@@ -20,6 +22,7 @@ use std::{
str::FromStr, str::FromStr,
}; };
use tauri::{path::BaseDirectory, Manager}; use tauri::{path::BaseDirectory, Manager};
#[cfg(not(target_os = "linux"))]
use tauri_plugin_decorum::WebviewWindowExt; use tauri_plugin_decorum::WebviewWindowExt;
pub mod commands; pub mod commands;
@@ -137,22 +140,45 @@ fn main() {
builder.build().unwrap() builder.build().unwrap()
}; };
tauri::Builder::default() #[cfg(target_os = "macos")]
let builder = tauri::Builder::default().plugin(tauri_nspanel::init());
#[cfg(not(target_os = "macos"))]
let builder = tauri::Builder::default();
builder
.setup(|app| { .setup(|app| {
#[cfg(target_os = "macos")]
app.handle().plugin(tauri_nspanel::init()).unwrap();
#[cfg(not(target_os = "linux"))]
let main_window = app.get_webview_window("main").unwrap(); let main_window = app.get_webview_window("main").unwrap();
// Set custom decoration for Windows // Set custom decoration for Windows
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
main_window.create_overlay_titlebar().unwrap(); main_window.create_overlay_titlebar().unwrap();
// Make main window transparent // Restore native border
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
main_window.make_transparent().unwrap(); main_window.add_border(None);
// Set a custom inset to the traffic lights // Set a custom inset to the traffic lights
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
main_window.set_traffic_lights_inset(8.0, 16.0).unwrap(); main_window.set_traffic_lights_inset(8.0, 16.0).unwrap();
#[cfg(target_os = "macos")]
let win = main_window.clone();
#[cfg(target_os = "macos")]
main_window.on_window_event(move |event| {
if let tauri::WindowEvent::ThemeChanged(_) = event {
win.set_traffic_lights_inset(8.0, 16.0).unwrap();
}
if let tauri::WindowEvent::Resized(_) = event {
win.set_traffic_lights_inset(8.0, 16.0).unwrap();
}
});
// Create data folder if not exist // Create data folder if not exist
let home_dir = app.path().home_dir().unwrap(); let home_dir = app.path().home_dir().unwrap();
let _ = fs::create_dir_all(home_dir.join("Lume/")); let _ = fs::create_dir_all(home_dir.join("Lume/"));
@@ -186,7 +212,10 @@ fn main() {
if let Some((relay, option)) = line.split_once(',') { if let Some((relay, option)) = line.split_once(',') {
match RelayMetadata::from_str(option) { match RelayMetadata::from_str(option) {
Ok(meta) => { Ok(meta) => {
println!("connecting to bootstrap relay...: {} - {}", relay, meta); println!(
"connecting to bootstrap relay...: {} - {}",
relay, meta
);
let opts = if meta == RelayMetadata::Read { let opts = if meta == RelayMetadata::Read {
RelayOptions::new().read(true).write(false) RelayOptions::new().read(true).write(false)
} else { } else {
@@ -216,7 +245,6 @@ fn main() {
Ok(()) Ok(())
}) })
.plugin(tauri_nspanel::init())
.plugin(tauri_plugin_theme::init(ctx.config_mut())) .plugin(tauri_plugin_theme::init(ctx.config_mut()))
.plugin(tauri_plugin_decorum::init()) .plugin(tauri_plugin_decorum::init())
.plugin(tauri_plugin_clipboard_manager::init()) .plugin(tauri_plugin_clipboard_manager::init())
@@ -229,20 +257,7 @@ fn main() {
.plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_upload::init()) .plugin(tauri_plugin_upload::init())
.plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_updater::Builder::new().build())
.plugin(
tauri_plugin_window_state::Builder::new()
.with_denylist(&["panel"])
.build(),
)
.invoke_handler(invoke_handler) .invoke_handler(invoke_handler)
.build(ctx) .run(ctx)
.expect("error while running tauri application") .expect("error while running tauri application");
.run(|_, 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();
}
});
} }
+4 -3
View File
@@ -192,7 +192,7 @@ pub async fn listen_event_reply(id: &str, state: State<'_, Nostr>) -> Result<(),
.since(Timestamp::now()); .since(Timestamp::now());
// Subscribe // Subscribe
client.subscribe_with_id(sub_id, vec![filter], None).await; let _ = client.subscribe_with_id(sub_id, vec![filter], None).await;
Ok(()) Ok(())
} }
@@ -308,7 +308,7 @@ pub async fn listen_local_event(label: &str, state: State<'_, Nostr>) -> Result<
.since(Timestamp::now()); .since(Timestamp::now());
// Subscribe // Subscribe
client.subscribe_with_id(sub_id, vec![filter], None).await; let _ = client.subscribe_with_id(sub_id, vec![filter], None).await;
Ok(()) Ok(())
} }
@@ -656,7 +656,8 @@ pub async fn user_to_bech32(user: &str, state: State<'_, Nostr>) -> Result<Strin
.into_iter() .into_iter()
.map(|i| i.0.to_string()) .map(|i| i.0.to_string())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let profile = Nip19Profile::new(public_key, relays).map_err(|err| err.to_string())?; let profile =
Nip19Profile::new(public_key, relays).map_err(|err| err.to_string())?;
Ok(profile.to_bech32().map_err(|err| err.to_string())?) Ok(profile.to_bech32().map_err(|err| err.to_string())?)
} }
+49 -39
View File
@@ -1,18 +1,20 @@
use keyring::Entry; #[cfg(target_os = "macos")]
use keyring_search::{Limit, List, Search};
use nostr_sdk::prelude::*;
use serde::Serialize;
use specta::Type;
use std::time::Duration;
use tauri::{EventTarget, Manager, State};
use tauri_plugin_notification::NotificationExt;
use crate::commands::tray::create_tray_panel; use crate::commands::tray::create_tray_panel;
use crate::nostr::event::RichEvent; use crate::nostr::event::RichEvent;
use crate::nostr::internal::{get_user_settings, init_nip65}; use crate::nostr::internal::{get_user_settings, init_nip65};
use crate::nostr::utils::parse_event; use crate::nostr::utils::parse_event;
use crate::{Nostr, NEWSFEED_NEG_LIMIT, NOTIFICATION_NEG_LIMIT}; use crate::{Nostr, NEWSFEED_NEG_LIMIT, NOTIFICATION_NEG_LIMIT};
use keyring::Entry;
use keyring_search::{Limit, List, Search};
use nostr_sdk::prelude::*;
use serde::Serialize;
use specta::Type;
use std::collections::HashSet;
use std::time::Duration;
use tauri::{Emitter, EventTarget, Manager, State};
use tauri_plugin_notification::NotificationExt;
#[derive(Serialize, Type)] #[derive(Serialize, Type)]
pub struct Account { pub struct Account {
npub: String, npub: String,
@@ -21,28 +23,17 @@ pub struct Account {
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub fn get_accounts() -> Result<Vec<String>, String> { pub fn get_accounts() -> Vec<String> {
let search = Search::new().unwrap(); let search = Search::new().expect("Unexpected.");
let results = search.by("Account", "nostr_secret"); let results = search.by_service("Lume");
let list = List::list_credentials(&results, Limit::All);
match List::list_credentials(results, Limit::All) { let accounts: HashSet<String> = list
Ok(list) => {
let search: Vec<String> = list
.split_whitespace() .split_whitespace()
.map(|v| v.to_string())
.filter(|v| v.starts_with("npub1")) .filter(|v| v.starts_with("npub1"))
.map(String::from)
.collect(); .collect();
let accounts: Vec<String> = search accounts.into_iter().collect()
.into_iter()
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
Ok(accounts)
}
Err(_) => Err("Empty.".into()),
}
} }
#[tauri::command] #[tauri::command]
@@ -63,7 +54,7 @@ pub fn create_account() -> Result<Account, String> {
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub fn get_private_key(npub: &str) -> Result<String, String> { pub fn get_private_key(npub: &str) -> Result<String, String> {
let keyring = Entry::new(npub, "nostr_secret").unwrap(); let keyring = Entry::new("Lume", npub).unwrap();
if let Ok(nsec) = keyring.get_password() { if let Ok(nsec) = keyring.get_password() {
let secret_key = SecretKey::from_bech32(nsec).unwrap(); let secret_key = SecretKey::from_bech32(nsec).unwrap();
@@ -95,7 +86,7 @@ pub async fn save_account(
let npub = nostr_keys.public_key().to_bech32().unwrap(); let npub = nostr_keys.public_key().to_bech32().unwrap();
let nsec = nostr_keys.secret_key().unwrap().to_bech32().unwrap(); let nsec = nostr_keys.secret_key().unwrap().to_bech32().unwrap();
let keyring = Entry::new(&npub, "nostr_secret").unwrap(); let keyring = Entry::new("Lume", &npub).unwrap();
let _ = keyring.set_password(&nsec); let _ = keyring.set_password(&nsec);
let signer = NostrSigner::Keys(nostr_keys); let signer = NostrSigner::Keys(nostr_keys);
@@ -126,7 +117,7 @@ pub async fn connect_remote_account(uri: &str, state: State<'_, Nostr>) -> Resul
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(120), None).await { match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(120), None).await {
Ok(signer) => { Ok(signer) => {
let keyring = Entry::new(&remote_npub, "nostr_secret").unwrap(); let keyring = Entry::new("Lume", &remote_npub).unwrap();
let _ = keyring.set_password(&app_secret); let _ = keyring.set_password(&app_secret);
// Update signer // Update signer
@@ -144,7 +135,7 @@ pub async fn connect_remote_account(uri: &str, state: State<'_, Nostr>) -> Resul
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub async fn get_encrypted_key(npub: &str, password: &str) -> Result<String, String> { pub async fn get_encrypted_key(npub: &str, password: &str) -> Result<String, String> {
let keyring = Entry::new(npub, "nostr_secret").unwrap(); let keyring = Entry::new("Lume", npub).unwrap();
if let Ok(nsec) = keyring.get_password() { if let Ok(nsec) = keyring.get_password() {
let secret_key = SecretKey::from_bech32(nsec).unwrap(); let secret_key = SecretKey::from_bech32(nsec).unwrap();
@@ -170,7 +161,7 @@ pub async fn load_account(
) -> Result<bool, String> { ) -> Result<bool, String> {
let handle = app.clone(); let handle = app.clone();
let client = &state.client; let client = &state.client;
let keyring = Entry::new(npub, "nostr_secret").unwrap(); let keyring = Entry::new("Lume", npub).unwrap();
let password = match keyring.get_password() { let password = match keyring.get_password() {
Ok(pw) => pw, Ok(pw) => pw,
@@ -179,11 +170,14 @@ pub async fn load_account(
match bunker { match bunker {
Some(uri) => { Some(uri) => {
let app_keys = Keys::parse(password).expect("Secret Key is modified, please check again."); let app_keys =
Keys::parse(password).expect("Secret Key is modified, please check again.");
match NostrConnectURI::parse(uri) { match NostrConnectURI::parse(uri) {
Ok(bunker_uri) => { Ok(bunker_uri) => {
match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(30), None).await { match Nip46Signer::new(bunker_uri, app_keys, Duration::from_secs(30), None)
.await
{
Ok(signer) => client.set_signer(Some(signer.into())).await, Ok(signer) => client.set_signer(Some(signer.into())).await,
Err(err) => return Err(err.to_string()), Err(err) => return Err(err.to_string()),
} }
@@ -284,7 +278,7 @@ pub async fn load_account(
.since(Timestamp::now()); .since(Timestamp::now());
// Subscribing for new notification... // Subscribing for new notification...
client let _ = client
.subscribe_with_id(subscription_id, vec![subscription], None) .subscribe_with_id(subscription_id, vec![subscription], None)
.await; .await;
@@ -320,7 +314,11 @@ pub async fn load_account(
.notification() .notification()
.builder() .builder()
.body("Mentioned you in a thread.") .body("Mentioned you in a thread.")
.title(author.display_name.unwrap_or_else(|| "Lume".to_string())) .title(
author
.display_name
.unwrap_or_else(|| "Lume".to_string()),
)
.show() .show()
{ {
println!("Failed to show notification: {:?}", e); println!("Failed to show notification: {:?}", e);
@@ -331,7 +329,11 @@ pub async fn load_account(
.notification() .notification()
.builder() .builder()
.body("Reposted your note.") .body("Reposted your note.")
.title(author.display_name.unwrap_or_else(|| "Lume".to_string())) .title(
author
.display_name
.unwrap_or_else(|| "Lume".to_string()),
)
.show() .show()
{ {
println!("Failed to show notification: {:?}", e); println!("Failed to show notification: {:?}", e);
@@ -343,7 +345,11 @@ pub async fn load_account(
.notification() .notification()
.builder() .builder()
.body(content) .body(content)
.title(author.display_name.unwrap_or_else(|| "Lume".to_string())) .title(
author
.display_name
.unwrap_or_else(|| "Lume".to_string()),
)
.show() .show()
{ {
println!("Failed to show notification: {:?}", e); println!("Failed to show notification: {:?}", e);
@@ -354,7 +360,11 @@ pub async fn load_account(
.notification() .notification()
.builder() .builder()
.body("Zapped you.") .body("Zapped you.")
.title(author.display_name.unwrap_or_else(|| "Lume".to_string())) .title(
author
.display_name
.unwrap_or_else(|| "Lume".to_string()),
)
.show() .show()
{ {
println!("Failed to show notification: {:?}", e); println!("Failed to show notification: {:?}", e);
+6 -3
View File
@@ -359,7 +359,7 @@ pub async fn remove_wallet(state: State<'_, Nostr>) -> Result<(), ()> {
let client = &state.client; let client = &state.client;
let keyring = Entry::new("Lume Secret", "Bitcoin Connect").unwrap(); let keyring = Entry::new("Lume Secret", "Bitcoin Connect").unwrap();
match keyring.delete_password() { match keyring.delete_credential() {
Ok(_) => { Ok(_) => {
client.unset_zapper().await; client.unset_zapper().await;
Ok(()) Ok(())
@@ -449,7 +449,9 @@ pub async fn friend_to_friend(npub: &str, state: State<'_, Nostr>) -> Result<boo
.kind(Kind::ContactList) .kind(Kind::ContactList)
.limit(1); .limit(1);
if let Ok(contact_list_events) = client.get_events_of(vec![contact_list_filter], None).await { if let Ok(contact_list_events) =
client.get_events_of(vec![contact_list_filter], None).await
{
for event in contact_list_events.into_iter() { for event in contact_list_events.into_iter() {
for tag in event.into_iter_tags() { for tag in event.into_iter_tags() {
if let Some(TagStandard::PublicKey { if let Some(TagStandard::PublicKey {
@@ -575,7 +577,8 @@ pub async fn get_settings(state: State<'_, Nostr>) -> Result<Settings, ()> {
#[tauri::command] #[tauri::command]
#[specta::specta] #[specta::specta]
pub async fn set_new_settings(settings: &str, state: State<'_, Nostr>) -> Result<(), ()> { pub async fn set_new_settings(settings: &str, state: State<'_, Nostr>) -> Result<(), ()> {
let parsed: Settings = serde_json::from_str(settings).expect("Could not parse settings payload"); let parsed: Settings =
serde_json::from_str(settings).expect("Could not parse settings payload");
*state.settings.lock().unwrap() = parsed; *state.settings.lock().unwrap() = parsed;
Ok(()) Ok(())
+4 -1
View File
@@ -39,7 +39,8 @@ pub async fn get_relays(state: State<'_, Nostr>) -> Result<Relays, String> {
match client.get_events_of(vec![filter], None).await { match client.get_events_of(vec![filter], None).await {
Ok(events) => { Ok(events) => {
if let Some(event) = events.first() { if let Some(event) = events.first() {
let nip65_list = nip65::extract_relay_list(event); let nip65_list = nip65::extract_relay_list(event).collect::<Vec<_>>();
let read = nip65_list let read = nip65_list
.iter() .iter()
.filter_map(|(url, meta)| { .filter_map(|(url, meta)| {
@@ -50,6 +51,7 @@ pub async fn get_relays(state: State<'_, Nostr>) -> Result<Relays, String> {
} }
}) })
.collect(); .collect();
let write = nip65_list let write = nip65_list
.iter() .iter()
.filter_map(|(url, meta)| { .filter_map(|(url, meta)| {
@@ -60,6 +62,7 @@ pub async fn get_relays(state: State<'_, Nostr>) -> Result<Relays, String> {
} }
}) })
.collect(); .collect();
let both = nip65_list let both = nip65_list
.iter() .iter()
.filter_map(|(url, meta)| { .filter_map(|(url, meta)| {
+7 -19
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/schema.json",
"productName": "Lume", "productName": "Lume",
"version": "4.0.13", "version": "4.0.15",
"identifier": "nu.lume.Lume", "identifier": "nu.lume.Lume",
"build": { "build": {
"beforeBuildCommand": "pnpm desktop:build", "beforeBuildCommand": "pnpm desktop:build",
@@ -34,6 +34,7 @@
}, },
"bundle": { "bundle": {
"licenseFile": "../LICENSE", "licenseFile": "../LICENSE",
"homepage": "https://lume.nu",
"longDescription": "nostr client for desktop", "longDescription": "nostr client for desktop",
"shortDescription": "nostr client", "shortDescription": "nostr client",
"targets": "all", "targets": "all",
@@ -65,21 +66,6 @@
} }
}, },
"macOS": { "macOS": {
"dmg": {
"appPosition": {
"x": 180,
"y": 170
},
"applicationFolderPosition": {
"x": 480,
"y": 170
},
"windowSize": {
"height": 400,
"width": 660
}
},
"files": {},
"minimumSystemVersion": "10.15" "minimumSystemVersion": "10.15"
}, },
"windows": { "windows": {
@@ -99,7 +85,7 @@
"fileAssociations": [ "fileAssociations": [
{ {
"name": "bech32", "name": "bech32",
"description": "Nostr Bech32", "description": "Nostr BECH32",
"ext": [ "ext": [
"npub", "npub",
"nsec", "nsec",
@@ -110,7 +96,8 @@
], ],
"role": "Viewer" "role": "Viewer"
} }
] ],
"createUpdaterArtifacts": true
}, },
"plugins": { "plugins": {
"updater": { "updater": {
@@ -121,7 +108,8 @@
}, },
"endpoints": [ "endpoints": [
"https://lus.reya3772.workers.dev/v1/{{target}}/{{arch}}/{{current_version}}", "https://lus.reya3772.workers.dev/v1/{{target}}/{{arch}}/{{current_version}}",
"https://lus.reya3772.workers.dev/{{target}}/{{current_version}}" "https://lus.reya3772.workers.dev/{{target}}/{{current_version}}",
"https://github.com/lumehq/lume/releases/latest/download/latest.json"
] ]
} }
} }
+3 -4
View File
@@ -5,11 +5,10 @@
{ {
"title": "Lume", "title": "Lume",
"label": "main", "label": "main",
"titleBarStyle": "Overlay", "width": 1045,
"width": 500,
"height": 800, "height": 800,
"minWidth": 500, "minWidth": 480,
"minHeight": 800 "minHeight": 760
} }
] ]
} }
+1
View File
@@ -17,6 +17,7 @@
"minWidth": 480, "minWidth": 480,
"minHeight": 760, "minHeight": 760,
"hiddenTitle": true, "hiddenTitle": true,
"transparent": true,
"windowEffects": { "windowEffects": {
"state": "followsWindowActiveState", "state": "followsWindowActiveState",
"effects": [ "effects": [
+6 -4
View File
@@ -5,13 +5,15 @@
{ {
"title": "Lume", "title": "Lume",
"label": "main", "label": "main",
"width": 500, "width": 1045,
"height": 800, "height": 800,
"minWidth": 500, "minWidth": 480,
"minHeight": 800, "minHeight": 760,
"transparent": true, "transparent": true,
"windowEffects": { "windowEffects": {
"effects": ["mica"] "effects": [
"mica"
]
} }
} }
] ]