From 9779d020c7dd1c4794eca2171eda6019122e7e30 Mon Sep 17 00:00:00 2001 From: reya <123083837+reyamir@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:22:28 +0700 Subject: [PATCH] feat: improve list virtualization --- apps/desktop2/package.json | 1 + apps/desktop2/src/app.css | 12 -- apps/desktop2/src/components/note/content.tsx | 4 +- .../src/components/note/preview/images.tsx | 4 +- apps/desktop2/src/routes/global.tsx | 104 +++++++----- apps/desktop2/src/routes/group.tsx | 104 +++++++----- apps/desktop2/src/routes/newsfeed.tsx | 104 +++++++----- apps/desktop2/src/routes/panel.tsx | 139 +++++++++------ apps/desktop2/src/routes/topic.tsx | 104 +++++++----- apps/desktop2/src/routes/trending.notes.tsx | 67 +++++--- apps/desktop2/src/routes/users/$pubkey.tsx | 18 +- packages/ui/src/carousel.tsx | 40 +++-- pnpm-lock.yaml | 159 ++++++++++++++++++ src-tauri/src/fns.rs | 33 ++-- src-tauri/src/nostr/event.rs | 2 +- 15 files changed, 582 insertions(+), 313 deletions(-) diff --git a/apps/desktop2/package.json b/apps/desktop2/package.json index 67529ce0..2b77d86f 100644 --- a/apps/desktop2/package.json +++ b/apps/desktop2/package.json @@ -20,6 +20,7 @@ "@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", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", diff --git a/apps/desktop2/src/app.css b/apps/desktop2/src/app.css index 2a29365f..fec12321 100644 --- a/apps/desktop2/src/app.css +++ b/apps/desktop2/src/app.css @@ -2,18 +2,6 @@ @tailwind utilities; @tailwind components; -*::-webkit-scrollbar { - @apply w-[5px]; -} - -*::-webkit-scrollbar-track { - @apply bg-transparent; -} - -*::-webkit-scrollbar-thumb { - @apply rounded bg-black dark:bg-white; -} - @layer utilities { .content-break { word-break: break-word; diff --git a/apps/desktop2/src/components/note/content.tsx b/apps/desktop2/src/components/note/content.tsx index 6abdfea5..e0095ee0 100644 --- a/apps/desktop2/src/components/note/content.tsx +++ b/apps/desktop2/src/components/note/content.tsx @@ -96,7 +96,9 @@ export function NoteContent({
620 ? "max-h-[250px] gradient-mask-b-0" : "", + event.meta?.content.length > 400 + ? "max-h-[250px] gradient-mask-b-0" + : "", className, )} > diff --git a/apps/desktop2/src/components/note/preview/images.tsx b/apps/desktop2/src/components/note/preview/images.tsx index e6b2be76..401b0ae1 100644 --- a/apps/desktop2/src/components/note/preview/images.tsx +++ b/apps/desktop2/src/components/note/preview/images.tsx @@ -42,8 +42,8 @@ export function Images({ urls }: { urls: string[] }) { return ( ( - + renderItem={({ item, index, isSnapPoint }) => ( + {item}(null); + const renderItem = useCallback( (event: LumeEvent) => { if (!event) return; @@ -70,48 +73,63 @@ export function Screen() { ); return ( -
- {isFetching && !isLoading && !isFetchingNextPage ? ( -
-
- - Fetching new notes... -
-
- ) : null} - {isLoading ? ( -
- - Loading... -
- ) : !data.length ? ( -
- Yo. You're catching up on all the things happening around you. -
- ) : ( - - {data.map((item) => renderItem(item))} - - )} - {data?.length && hasNextPage ? ( -
- -
- ) : null} -
+ Loading... +
+ ) : !data.length ? ( +
+ Yo. You're catching up on all the things happening around you. +
+ ) : ( + data.map((item) => renderItem(item)) + )} + {data?.length && hasNextPage ? ( +
+ +
+ ) : null} + + + + + + + ); } diff --git a/apps/desktop2/src/routes/group.tsx b/apps/desktop2/src/routes/group.tsx index f7fddbbd..d67751ff 100644 --- a/apps/desktop2/src/routes/group.tsx +++ b/apps/desktop2/src/routes/group.tsx @@ -6,9 +6,10 @@ import { ArrowRightCircleIcon } from "@lume/icons"; import { type LumeEvent, NostrQuery } from "@lume/system"; import { type ColumnRouteSearch, Kind } from "@lume/types"; import { Spinner } from "@lume/ui"; +import * as ScrollArea from "@radix-ui/react-scroll-area"; import { useInfiniteQuery } from "@tanstack/react-query"; import { createFileRoute, redirect } from "@tanstack/react-router"; -import { useCallback } from "react"; +import { useCallback, useRef } from "react"; import { Virtualizer } from "virtua"; export const Route = createFileRoute("/group")({ @@ -61,6 +62,8 @@ export function Screen() { refetchOnWindowFocus: false, }); + const ref = useRef(null); + const renderItem = useCallback( (event: LumeEvent) => { if (!event) return; @@ -84,48 +87,63 @@ export function Screen() { ); return ( -
- {isFetching && !isLoading && !isFetchingNextPage ? ( -
-
- - Fetching new notes... -
-
- ) : null} - {isLoading ? ( -
- - Loading... -
- ) : !data.length ? ( -
- Yo. You're catching up on all the things happening around you. -
- ) : ( - - {data.map((item) => renderItem(item))} - - )} - {data?.length && hasNextPage ? ( -
- -
- ) : null} -
+ Loading... + + ) : !data.length ? ( +
+ Yo. You're catching up on all the things happening around you. +
+ ) : ( + data.map((item) => renderItem(item)) + )} + {data?.length && hasNextPage ? ( +
+ +
+ ) : null} + + + + + + + ); } diff --git a/apps/desktop2/src/routes/newsfeed.tsx b/apps/desktop2/src/routes/newsfeed.tsx index 29e2fe10..075b13fd 100644 --- a/apps/desktop2/src/routes/newsfeed.tsx +++ b/apps/desktop2/src/routes/newsfeed.tsx @@ -6,9 +6,10 @@ import { ArrowRightCircleIcon } from "@lume/icons"; import { type LumeEvent, NostrAccount, NostrQuery } from "@lume/system"; import { type ColumnRouteSearch, Kind } from "@lume/types"; import { Spinner } from "@lume/ui"; +import * as ScrollArea from "@radix-ui/react-scroll-area"; import { useInfiniteQuery } from "@tanstack/react-query"; import { createFileRoute, redirect } from "@tanstack/react-router"; -import { useCallback } from "react"; +import { useCallback, useRef } from "react"; import { Virtualizer } from "virtua"; export const Route = createFileRoute("/newsfeed")({ @@ -59,6 +60,8 @@ export function Screen() { refetchOnWindowFocus: false, }); + const ref = useRef(null); + const renderItem = useCallback( (event: LumeEvent) => { if (!event) return; @@ -82,48 +85,63 @@ export function Screen() { ); return ( -
- {isFetching && !isLoading && !isFetchingNextPage ? ( -
-
- - Fetching new notes... -
-
- ) : null} - {isLoading ? ( -
- - Loading... -
- ) : !data.length ? ( -
- Yo. You're catching up on all the things happening around you. -
- ) : ( - - {data.map((item) => renderItem(item))} - - )} - {data?.length && hasNextPage ? ( -
- -
- ) : null} -
+ Loading... + + ) : !data.length ? ( +
+ Yo. You're catching up on all the things happening around you. +
+ ) : ( + data.map((item) => renderItem(item)) + )} + {data?.length && hasNextPage ? ( +
+ +
+ ) : null} + + + + + + + ); } diff --git a/apps/desktop2/src/routes/panel.tsx b/apps/desktop2/src/routes/panel.tsx index 5d5cc47f..7bbe7758 100644 --- a/apps/desktop2/src/routes/panel.tsx +++ b/apps/desktop2/src/routes/panel.tsx @@ -13,13 +13,22 @@ import { decodeZapInvoice, formatCreatedAt, } from "@lume/utils"; +import * as ScrollArea from "@radix-ui/react-scroll-area"; import * as Tabs from "@radix-ui/react-tabs"; import { createFileRoute } from "@tanstack/react-router"; import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu"; import { getCurrent } from "@tauri-apps/api/window"; import { exit } from "@tauri-apps/plugin-process"; import { open } from "@tauri-apps/plugin-shell"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { + type ReactNode, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { Virtualizer } from "virtua"; interface EmitAccount { account: string; @@ -161,7 +170,7 @@ function Screen() { return (
-
+

Notifications

@@ -193,35 +202,36 @@ function Screen() { > Replies Reactions Zaps -
- - {texts.map((event) => ( - +
+ + {texts.map((event, index) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: + ))} - - + + {[...reactions.entries()].map(([root, events]) => (
@@ -250,12 +260,12 @@ function Screen() {
))} - - + + {[...zaps.entries()].map(([root, events]) => (
@@ -279,13 +289,38 @@ function Screen() {
))} - +
); } +function Tab({ value, children }: { value: string; children: ReactNode[] }) { + const ref = useRef(null); + + return ( + + + + {children} + + + + + + + + ); +} + function RootNote({ id }: { id: string }) { const { isLoading, isError, data } = useEvent(id); @@ -332,46 +367,40 @@ function TextNote({ event }: { event: LumeEvent }) { .slice(0, 3); return ( - +
+ + +
+
+
{event.content}
+
+ + ); } diff --git a/apps/desktop2/src/routes/topic.tsx b/apps/desktop2/src/routes/topic.tsx index d887b445..87ae1fbd 100644 --- a/apps/desktop2/src/routes/topic.tsx +++ b/apps/desktop2/src/routes/topic.tsx @@ -6,9 +6,10 @@ import { ArrowRightCircleIcon } from "@lume/icons"; import { type LumeEvent, NostrQuery } from "@lume/system"; import { type ColumnRouteSearch, Kind } from "@lume/types"; import { Spinner } from "@lume/ui"; +import * as ScrollArea from "@radix-ui/react-scroll-area"; import { useInfiniteQuery } from "@tanstack/react-query"; import { createFileRoute, redirect } from "@tanstack/react-router"; -import { useCallback } from "react"; +import { useCallback, useRef } from "react"; import { Virtualizer } from "virtua"; type Topic = { @@ -71,6 +72,8 @@ export function Screen() { refetchOnWindowFocus: false, }); + const ref = useRef(null); + const renderItem = useCallback( (event: LumeEvent) => { if (!event) return; @@ -94,48 +97,63 @@ export function Screen() { ); return ( -
- {isFetching && !isLoading && !isFetchingNextPage ? ( -
-
- - Fetching new notes... -
-
- ) : null} - {isLoading ? ( -
- - Loading... -
- ) : !data.length ? ( -
- Yo. You're catching up on all the things happening around you. -
- ) : ( - - {data.map((item) => renderItem(item))} - - )} - {data?.length && hasNextPage ? ( -
- -
- ) : null} -
+ Loading... +
+ ) : !data.length ? ( +
+ Yo. You're catching up on all the things happening around you. +
+ ) : ( + data.map((item) => renderItem(item)) + )} + {data?.length && hasNextPage ? ( +
+ +
+ ) : null} + + + + + + + ); } diff --git a/apps/desktop2/src/routes/trending.notes.tsx b/apps/desktop2/src/routes/trending.notes.tsx index c728309e..e340937d 100644 --- a/apps/desktop2/src/routes/trending.notes.tsx +++ b/apps/desktop2/src/routes/trending.notes.tsx @@ -2,9 +2,10 @@ import { TextNote } from "@/components/text"; import { LumeEvent } from "@lume/system"; import type { NostrEvent } from "@lume/types"; import { Spinner } from "@lume/ui"; +import * as ScrollArea from "@radix-ui/react-scroll-area"; import { Await, createFileRoute } from "@tanstack/react-router"; import { defer } from "@tanstack/react-router"; -import { Suspense } from "react"; +import { Suspense, useRef } from "react"; import { Virtualizer } from "virtua"; export const Route = createFileRoute("/trending/notes")({ @@ -34,33 +35,47 @@ export const Route = createFileRoute("/trending/notes")({ export function Screen() { const { data } = Route.useLoaderData(); + const ref = useRef(null); return ( -
- - - -
- } - > - - {(notes) => - notes.map((event) => ( - - )) + + + + + +
} - - - -
+ > + + {(notes) => + notes.map((event) => ( + + )) + } + + + + + + + + + ); } diff --git a/apps/desktop2/src/routes/users/$pubkey.tsx b/apps/desktop2/src/routes/users/$pubkey.tsx index b0be43e0..611ad3aa 100644 --- a/apps/desktop2/src/routes/users/$pubkey.tsx +++ b/apps/desktop2/src/routes/users/$pubkey.tsx @@ -1,17 +1,21 @@ -import { Box, Container, Spinner } from "@lume/ui"; -import { User } from "@/components/user"; -import { createFileRoute, defer } from "@tanstack/react-router"; -import { WindowVirtualizer } from "virtua"; import { Conversation } from "@/components/conversation"; import { Quote } from "@/components/quote"; import { RepostNote } from "@/components/repost"; import { TextNote } from "@/components/text"; -import { Kind } from "@lume/types"; -import { Suspense, useCallback } from "react"; -import { Await } from "@tanstack/react-router"; +import { User } from "@/components/user"; import { type LumeEvent, NostrQuery } from "@lume/system"; +import { Kind } from "@lume/types"; +import { Box, Container, Spinner } from "@lume/ui"; +import { createFileRoute, defer } from "@tanstack/react-router"; +import { Await } from "@tanstack/react-router"; +import { Suspense, useCallback } from "react"; +import { WindowVirtualizer } from "virtua"; export const Route = createFileRoute("/users/$pubkey")({ + beforeLoad: async () => { + const settings = await NostrQuery.getUserSettings(); + return { settings }; + }, loader: async ({ params }) => { return { data: defer(NostrQuery.getUserEvents(params.pubkey)) }; }, diff --git a/packages/ui/src/carousel.tsx b/packages/ui/src/carousel.tsx index 51013a04..5430165b 100644 --- a/packages/ui/src/carousel.tsx +++ b/packages/ui/src/carousel.tsx @@ -11,29 +11,25 @@ interface CarouselProps { interface CarouselRenderItemProps { readonly item: T; + readonly index: number; readonly isSnapPoint: boolean; } export const Carousel = ({ items, renderItem }: CarouselProps) => { - const { - scrollRef, - pages, - activePageIndex, - prev, - next, - goTo, - snapPointIndexes, - } = useSnapCarousel(); + const { scrollRef, pages, activePageIndex, prev, next, snapPointIndexes } = + useSnapCarousel(); + return (
    - {items.map((item, i) => + {items.map((item, index) => renderItem({ item, - isSnapPoint: snapPointIndexes.has(i), + index, + isSnapPoint: snapPointIndexes.has(index), }), )}
@@ -74,13 +70,15 @@ interface CarouselItemProps { readonly children?: React.ReactNode; } -export const CarouselItem = ({ isSnapPoint, children }: CarouselItemProps) => ( -
  • - {children} -
  • -); +export const CarouselItem = ({ isSnapPoint, children }: CarouselItemProps) => { + return ( +
  • + {children} +
  • + ); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e0f35cc..c668659d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,6 +87,9 @@ importers: '@radix-ui/react-popover': specifier: ^1.0.7 version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-scroll-area': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-switch': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) @@ -1498,12 +1501,20 @@ packages: requiresBuild: true optional: true + /@radix-ui/number@1.1.0: + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + dev: false + /@radix-ui/primitive@1.0.1: resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} dependencies: '@babel/runtime': 7.24.7 dev: false + /@radix-ui/primitive@1.1.0: + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + dev: false + /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -1643,6 +1654,19 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.3)(react@18.3.1): + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.3 + react: 18.3.1 + dev: false + /@radix-ui/react-context@1.0.1(@types/react@18.3.3)(react@18.3.1): resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: @@ -1657,6 +1681,19 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-context@1.1.0(@types/react@18.3.3)(react@18.3.1): + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.3 + react: 18.3.1 + dev: false + /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} peerDependencies: @@ -1705,6 +1742,19 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-direction@1.1.0(@types/react@18.3.3)(react@18.3.1): + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.3 + react: 18.3.1 + dev: false + /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: @@ -1984,6 +2034,27 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-presence@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} peerDependencies: @@ -2005,6 +2076,26 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: @@ -2034,6 +2125,34 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-scroll-area@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-9ArIZ9HWhsrfqS765h+GZuLoxaRHD/j0ZWOWilsCvYTpYJp8XwCqNG7Dt9Nu/TItKOdgLGkOPCodQvDc+UMwYg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-slot@1.0.2(@types/react@18.3.3)(react@18.3.1): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -2049,6 +2168,20 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-slot@1.1.0(@types/react@18.3.3)(react@18.3.1): + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1) + '@types/react': 18.3.3 + react: 18.3.1 + dev: false + /@radix-ui/react-switch@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==} peerDependencies: @@ -2150,6 +2283,19 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.3)(react@18.3.1): + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.3 + react: 18.3.1 + dev: false + /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.3)(react@18.3.1): resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: @@ -2194,6 +2340,19 @@ packages: react: 18.3.1 dev: false + /@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.3)(react@18.3.1): + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.3.3 + react: 18.3.1 + dev: false + /@radix-ui/react-use-previous@1.0.1(@types/react@18.3.3)(react@18.3.1): resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} peerDependencies: diff --git a/src-tauri/src/fns.rs b/src-tauri/src/fns.rs index ca48b009..13ecf547 100644 --- a/src-tauri/src/fns.rs +++ b/src-tauri/src/fns.rs @@ -1,5 +1,6 @@ -use cocoa::appkit::NSWindowCollectionBehavior; use std::ffi::CString; + +use cocoa::appkit::NSWindowCollectionBehavior; use tauri::Manager; use tauri_nspanel::{ block::ConcreteBlock, @@ -8,44 +9,44 @@ use tauri_nspanel::{ base::{id, nil}, foundation::{NSPoint, NSRect}, }, - objc::{class, msg_send, runtime::NO, sel, sel_impl}, - panel_delegate, ManagerExt, WebviewWindowExt, + ManagerExt, + objc::{class, msg_send, runtime::NO, sel, sel_impl}, panel_delegate, WebviewWindowExt, }; #[allow(non_upper_case_globals)] const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7; pub fn swizzle_to_menubar_panel(app_handle: &tauri::AppHandle) { - let window = app_handle.get_webview_window("panel").unwrap(); - let panel = window.to_panel().unwrap(); - let handle = app_handle.to_owned(); - - let delegate = panel_delegate!(MyPanelDelegate { - window_did_become_key, + let panel_delegate = panel_delegate!(SpotlightPanelDelegate { window_did_resign_key }); - delegate.set_listener(Box::new(move |delegate_name: String| { + let window = app_handle.get_webview_window("panel").unwrap(); + + let panel = window.to_panel().unwrap(); + + let handle = app_handle.clone(); + + panel_delegate.set_listener(Box::new(move |delegate_name: String| { match delegate_name.as_str() { - "window_did_become_key" => { - let app_name = handle.package_info().name.to_owned(); - println!("[info]: {:?} panel becomes key window!", app_name); - } "window_did_resign_key" => { - println!("[info]: panel resigned from key window!"); + let _ = handle.emit("menubar_panel_did_resign_key", ()); } _ => (), } })); panel.set_level(NSMainMenuWindowLevel + 1); + panel.set_style_mask(NSWindowStyleMaskNonActivatingPanel); + panel.set_collection_behaviour( NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehavior::NSWindowCollectionBehaviorStationary | NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary, ); - panel.set_delegate(delegate); + + panel.set_delegate(panel_delegate); } pub fn setup_menubar_panel_listeners(app_handle: &tauri::AppHandle) { diff --git a/src-tauri/src/nostr/event.rs b/src-tauri/src/nostr/event.rs index da67c39a..8bc72a25 100644 --- a/src-tauri/src/nostr/event.rs +++ b/src-tauri/src/nostr/event.rs @@ -6,8 +6,8 @@ use serde::Serialize; use specta::Type; use tauri::State; -use crate::nostr::utils::{create_event_tags, dedup_event, parse_event, Meta}; use crate::Nostr; +use crate::nostr::utils::{create_event_tags, dedup_event, Meta, parse_event}; #[derive(Debug, Serialize, Type)] pub struct RichEvent {