From cb3c95b13323daa3d4a12071f8b22f6e9c9fdae2 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:18:04 +0700 Subject: [PATCH] clean up and improve perf --- package.json | 2 + pnpm-lock.yaml | 48 ++++++ .../20230921085234_add_metadata_table.sql | 9 - src-tauri/src/main.rs | 6 - src/libs/ndk/instance.ts | 13 +- src/libs/storage/instance.ts | 36 +--- src/shared/composer/modal.tsx | 2 +- src/shared/image.tsx | 10 +- src/shared/navigation.tsx | 39 ++++- src/shared/notes/child.tsx | 2 +- src/shared/notes/replies/item.tsx | 23 ++- src/shared/notes/replies/list.tsx | 2 +- src/shared/notes/replies/sub.tsx | 8 +- src/shared/notes/skeleton.tsx | 3 +- src/shared/user.tsx | 163 +++++++++++++----- src/shared/widgets/eventLoader.tsx | 2 + src/utils/hooks/useNostr.ts | 2 +- src/utils/hooks/useProfile.ts | 11 +- src/utils/parser.ts | 2 + 19 files changed, 246 insertions(+), 137 deletions(-) delete mode 100644 src-tauri/migrations/20230921085234_add_metadata_table.sql diff --git a/package.json b/package.json index 514478b1..5d6d0a77 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,10 @@ "@dnd-kit/core": "^6.0.8", "@getalby/sdk": "^2.4.0", "@nostr-dev-kit/ndk": "^1.3.0", + "@nostr-dev-kit/ndk-cache-dexie": "^1.3.0", "@nostr-fetch/adapter-ndk": "^0.12.2", "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-avatar": "^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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ac27a70..d1a9819d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,12 +14,18 @@ dependencies: '@nostr-dev-kit/ndk': specifier: ^1.3.0 version: 1.3.0(typescript@5.2.2) + '@nostr-dev-kit/ndk-cache-dexie': + specifier: ^1.3.0 + version: 1.3.0(typescript@5.2.2) '@nostr-fetch/adapter-ndk': specifier: ^0.12.2 version: 0.12.2(@nostr-dev-kit/ndk@1.3.0)(nostr-fetch@0.13.0) '@radix-ui/react-alert-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.2.8)(@types/react@18.2.23)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-avatar': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.8)(@types/react@18.2.23)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-collapsible': specifier: ^1.0.3 version: 1.0.3(@types/react-dom@18.2.8)(@types/react@18.2.23)(react-dom@18.2.0)(react@18.2.0) @@ -782,6 +788,19 @@ packages: fastq: 1.15.0 dev: true + /@nostr-dev-kit/ndk-cache-dexie@1.3.0(typescript@5.2.2): + resolution: {integrity: sha512-T9c2d/CennLXm/VO+4oKQgJln5+4GATM/Ko+QDkjOOKryqCYldap2QkIZ27IAsfhgGqMueyiU5g3oOcZFBgejQ==} + dependencies: + '@nostr-dev-kit/ndk': 1.3.0(typescript@5.2.2) + debug: 4.3.4 + dexie: 3.2.4 + nostr-tools: 1.16.0(typescript@5.2.2) + typescript-lru-cache: 2.0.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: false + /@nostr-dev-kit/ndk@1.3.0(typescript@5.2.2): resolution: {integrity: sha512-ZRne/TF1IRqy35ZwLq3452YD+AUux0r5A+mZgHuGNRv7GJzr2gUl1QNRjs+Db+emXdaHn82LKTRn6cFrni1oPA==} dependencies: @@ -883,6 +902,30 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.8)(@types/react@18.2.23)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.23.1 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.23)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.8)(@types/react@18.2.23)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.23)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.23)(react@18.2.0) + '@types/react': 18.2.23 + '@types/react-dom': 18.2.8 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-collapsible@1.0.3(@types/react-dom@18.2.8)(@types/react@18.2.23)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==} peerDependencies: @@ -3225,6 +3268,11 @@ packages: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false + /dexie@3.2.4: + resolution: {integrity: sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA==} + engines: {node: '>=6.0'} + dev: false + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true diff --git a/src-tauri/migrations/20230921085234_add_metadata_table.sql b/src-tauri/migrations/20230921085234_add_metadata_table.sql deleted file mode 100644 index 93c9ece9..00000000 --- a/src-tauri/migrations/20230921085234_add_metadata_table.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Add migration script here -CREATE TABLE - metadata ( - id TEXT NOT NULL PRIMARY KEY, - event TEXT NOT NULL, - author TEXT NOT NULL, - kind NUMBER NOT NULL DEFAULt 0, - created_at INTEGER NOT NULL - ); \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 4b20956b..ef748b85 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -239,12 +239,6 @@ fn main() { sql: include_str!("../migrations/20230918235335_add_uniq_to_relay.sql"), kind: MigrationKind::Up, }, - Migration { - version: 20230921085234, - description: "add metadata", - sql: include_str!("../migrations/20230921085234_add_metadata_table.sql"), - kind: MigrationKind::Up, - }, ], ) .build(), diff --git a/src/libs/ndk/instance.ts b/src/libs/ndk/instance.ts index 4bfbaef9..641d1d39 100644 --- a/src/libs/ndk/instance.ts +++ b/src/libs/ndk/instance.ts @@ -1,20 +1,18 @@ import NDK from '@nostr-dev-kit/ndk'; +import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie'; import { ndkAdapter } from '@nostr-fetch/adapter-ndk'; import { message } from '@tauri-apps/api/dialog'; import { fetch } from '@tauri-apps/api/http'; import { NostrFetcher } from 'nostr-fetch'; import { useEffect, useMemo, useState } from 'react'; -import TauriAdapter from '@libs/ndk/cache'; import { useStorage } from '@libs/storage/provider'; export const NDKInstance = () => { - const { db } = useStorage(); - const [ndk, setNDK] = useState(undefined); const [relayUrls, setRelayUrls] = useState([]); - const cacheAdapter = useMemo(() => new TauriAdapter(), []); + const { db } = useStorage(); const fetcher = useMemo( () => (ndk ? NostrFetcher.withCustomPool(ndkAdapter(ndk)) : null), [ndk] @@ -57,9 +55,10 @@ export const NDKInstance = () => { async function initNDK() { const explicitRelayUrls = await getExplicitRelays(); + const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'lume_ndkcache' }); const instance = new NDK({ explicitRelayUrls, - cacheAdapter, + cacheAdapter: dexieAdapter, }); try { @@ -77,10 +76,6 @@ export const NDKInstance = () => { useEffect(() => { if (!ndk) initNDK(); - - return () => { - cacheAdapter.saveCache(); - }; }, []); return { diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts index d57c384e..45f170c8 100644 --- a/src/libs/storage/instance.ts +++ b/src/libs/storage/instance.ts @@ -1,4 +1,4 @@ -import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk'; +import { NDKEvent } from '@nostr-dev-kit/ndk'; import { BaseDirectory, removeFile } from '@tauri-apps/api/fs'; import { Platform } from '@tauri-apps/api/os'; import Database from 'tauri-plugin-sql-api'; @@ -6,7 +6,6 @@ import { Stronghold } from 'tauri-plugin-stronghold-api'; import { FULL_RELAYS } from '@stores/constants'; -import { toRawEvent } from '@utils/rawEvent'; import { Account, DBEvent, Relays, Widget } from '@utils/types'; export class LumeStorage { @@ -124,6 +123,7 @@ export class LumeStorage { public async updateLastLogin() { const now = Math.floor(Date.now() / 1000); + this.account.last_login_at = now; return await this.db.execute( 'UPDATE accounts SET last_login_at = $1 WHERE id = $2;', [now, this.account.id] @@ -298,38 +298,6 @@ export class LumeStorage { return results.length < 1; } - public async createMetadata(event: NDKEvent) { - const rawEvent = toRawEvent(event); - - return await this.db.execute( - 'INSERT OR IGNORE INTO metadata (id, event, author, kind, created_at) VALUES ($1, $2, $3, $4, $5);', - [ - rawEvent.id, - JSON.stringify(rawEvent), - rawEvent.pubkey, - rawEvent.kind, - rawEvent.created_at, - ] - ); - } - - public async createProfile(pubkey: string, profile: NDKUserProfile) { - return await this.db.execute( - 'INSERT OR REPLACE INTO metadata (id, event, author, kind, created_at) VALUES ($1, $2, $3, $4, $5);', - [pubkey, JSON.stringify(profile), pubkey, 0, Math.round(Date.now() / 1000)] - ); - } - - public async getMetadataByPubkey(pubkey: string) { - const results: DBEvent[] = await this.db.select( - 'SELECT * FROM metadata WHERE author = $1 AND kind = "0" LIMIT 1;', - [pubkey] - ); - - if (results.length < 1) return null; - return JSON.parse(results[0].event as string) as NDKEvent; - } - public async getExplicitRelayUrls() { if (!this.account) return FULL_RELAYS; diff --git a/src/shared/composer/modal.tsx b/src/shared/composer/modal.tsx index a002c99a..41d9c99f 100644 --- a/src/shared/composer/modal.tsx +++ b/src/shared/composer/modal.tsx @@ -34,7 +34,7 @@ export function ComposerModal() { - +
) { +export const Image = memo(function Image({ + src, + ...props +}: ImgHTMLAttributes) { const [isError, setIsError] = useState(false); if (isError || !src) { @@ -20,9 +23,10 @@ export function Image({ src, ...props }: ImgHTMLAttributes) { currentTarget.onerror = null; setIsError(true); }} + loading="lazy" decoding="async" alt="lume default img" style={{ contentVisibility: 'auto' }} /> ); -} +}); diff --git a/src/shared/navigation.tsx b/src/shared/navigation.tsx index 321509dd..d3b2530a 100644 --- a/src/shared/navigation.tsx +++ b/src/shared/navigation.tsx @@ -1,13 +1,23 @@ import * as Collapsible from '@radix-ui/react-collapsible'; -import { NavLink } from 'react-router-dom'; +import { NavLink, useNavigate } from 'react-router-dom'; import { twMerge } from 'tailwind-merge'; import { ChatsList } from '@app/chats/components/list'; +import { useStorage } from '@libs/storage/provider'; + import { ActiveAccount } from '@shared/accounts/active'; import { ComposerModal } from '@shared/composer'; import { Frame } from '@shared/frame'; -import { BellIcon, NavArrowDownIcon, NwcIcon, SpaceIcon, WorldIcon } from '@shared/icons'; +import { + ArrowLeftIcon, + ArrowRightIcon, + BellIcon, + NavArrowDownIcon, + NwcIcon, + SpaceIcon, + WorldIcon, +} from '@shared/icons'; import { useActivities } from '@stores/activities'; import { useSidebar } from '@stores/sidebar'; @@ -15,6 +25,9 @@ import { useSidebar } from '@stores/sidebar'; import { compactNumber } from '@utils/number'; export function Navigation() { + const { db } = useStorage(); + + const navigate = useNavigate(); const totalNewActivities = useActivities((state) => state.totalNewActivities); const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]); @@ -30,8 +43,28 @@ export function Navigation() { >
+ {db.platform !== 'darwin' ? ( +
+ + +
+ ) : ( +
+ )}
-
+
lume
diff --git a/src/shared/notes/replies/item.tsx b/src/shared/notes/replies/item.tsx index b956c86e..90f85fda 100644 --- a/src/shared/notes/replies/item.tsx +++ b/src/shared/notes/replies/item.tsx @@ -12,19 +12,26 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
-
-
+
+
- +
- {event.replies ? ( - event.replies.map((sub) => ) - ) : ( -
- )} +
+ {event.replies ? ( + event.replies.map((sub) => ) + ) : ( +
+ )} +
); diff --git a/src/shared/notes/replies/list.tsx b/src/shared/notes/replies/list.tsx index 12a2049e..5c6c0120 100644 --- a/src/shared/notes/replies/list.tsx +++ b/src/shared/notes/replies/list.tsx @@ -51,7 +51,7 @@ export function RepliesList({ id }: { id: string }) {
{data?.length || 0} replies
-
+
{data?.length === 0 ? (
diff --git a/src/shared/notes/replies/sub.tsx b/src/shared/notes/replies/sub.tsx index e98b88f3..b70cecef 100644 --- a/src/shared/notes/replies/sub.tsx +++ b/src/shared/notes/replies/sub.tsx @@ -7,11 +7,11 @@ export function SubReply({ event }: { event: NDKEvent }) { return (
-
-
+
+
- - + +
diff --git a/src/shared/notes/skeleton.tsx b/src/shared/notes/skeleton.tsx index 3e929053..03699433 100644 --- a/src/shared/notes/skeleton.tsx +++ b/src/shared/notes/skeleton.tsx @@ -5,7 +5,8 @@ export function NoteSkeleton() {
-
+
+
diff --git a/src/shared/user.tsx b/src/shared/user.tsx index 2783e457..eddb77c9 100644 --- a/src/shared/user.tsx +++ b/src/shared/user.tsx @@ -1,11 +1,12 @@ +import * as Avatar from '@radix-ui/react-avatar'; import * as HoverCard from '@radix-ui/react-hover-card'; +import { minidenticon } from 'minidenticons'; import { memo } from 'react'; import ReactMarkdown from 'react-markdown'; import { Link } from 'react-router-dom'; import remarkGfm from 'remark-gfm'; import { RepostIcon, WorldIcon } from '@shared/icons'; -import { Image } from '@shared/image'; import { NIP05 } from '@shared/nip05'; import { formatCreatedAt } from '@utils/createdAt'; @@ -32,7 +33,10 @@ export const User = memo(function User({ embedProfile?: string; }) { const { status, user } = useProfile(pubkey, embedProfile); - const createdAt = time ? formatCreatedAt(time, variant === 'chat') : 0; + + const createdAt = formatCreatedAt(time, variant === 'chat'); + const svgURI = + 'data:image/svg+xml;utf8,' + encodeURIComponent(minidenticon(pubkey, 90, 50)); if (status === 'loading') { if (variant === 'avatar') { @@ -51,8 +55,8 @@ export const User = memo(function User({ } return ( -
-
+
+
); @@ -60,14 +64,20 @@ export const User = memo(function User({ if (variant === 'mention') { return ( -
- + + {pubkey} + +
{user?.name || @@ -85,11 +95,19 @@ export const User = memo(function User({ if (variant === 'large') { return (
- {pubkey} + + + + {pubkey} + +

@@ -125,11 +143,19 @@ export const User = memo(function User({ if (variant === 'simple') { return (

- {pubkey} + + + + {pubkey} + +

{user?.name || user?.display_name || user?.displayName} @@ -144,11 +170,19 @@ export const User = memo(function User({ if (variant === 'avatar') { return ( - {pubkey} + + + + {pubkey} + + ); } @@ -159,11 +193,19 @@ export const User = memo(function User({

- {pubkey} + + + + {pubkey} + +
{user?.name || @@ -181,14 +223,22 @@ export const User = memo(function User({ if (variant === 'thread') { return (
- {pubkey} + + + + {pubkey} + +
- {user?.name || user?.display_name || user?.displayName} + {user?.name || user?.display_name || user?.displayName || 'Anon'}
{createdAt} @@ -204,16 +254,23 @@ export const User = memo(function User({
- + + {pubkey} + +
@@ -232,11 +289,23 @@ export const User = memo(function User({ sideOffset={5} >
- {pubkey} + + + + {pubkey} + +
diff --git a/src/shared/widgets/eventLoader.tsx b/src/shared/widgets/eventLoader.tsx index 56d8b9c6..72492253 100644 --- a/src/shared/widgets/eventLoader.tsx +++ b/src/shared/widgets/eventLoader.tsx @@ -19,6 +19,8 @@ export function EventLoader({ firstTime }: { firstTime: boolean }) { useEffect(() => { async function getEvents() { const events = await getAllEventsSinceLastLogin(); + console.log('total event found: ', events.data.length); + const promises = await Promise.all( events.data.map(async (event) => await db.createEvent(event)) ); diff --git a/src/utils/hooks/useNostr.ts b/src/utils/hooks/useNostr.ts index 75f517fb..dbd4e82c 100644 --- a/src/utils/hooks/useNostr.ts +++ b/src/utils/hooks/useNostr.ts @@ -265,7 +265,7 @@ export function useNostr() { if (!customSince) { if (dbEventsEmpty || db.account.last_login_at === 0) { - since = db.account.network.length > 400 ? nHoursAgo(12) : nHoursAgo(24); + since = db.account.network.length > 500 ? nHoursAgo(12) : nHoursAgo(24); } else { since = db.account.last_login_at; } diff --git a/src/utils/hooks/useProfile.ts b/src/utils/hooks/useProfile.ts index 6306c51a..2630fbf3 100644 --- a/src/utils/hooks/useProfile.ts +++ b/src/utils/hooks/useProfile.ts @@ -2,10 +2,8 @@ import { NDKUserProfile } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { useNDK } from '@libs/ndk/provider'; -import { useStorage } from '@libs/storage/provider'; export function useProfile(pubkey: string, embed?: string) { - const { db } = useStorage(); const { ndk } = useNDK(); const { status, @@ -21,13 +19,7 @@ export function useProfile(pubkey: string, embed?: string) { const cleanPubkey = pubkey.replace('-', ''); const user = ndk.getUser({ hexpubkey: cleanPubkey }); - - const profile = await user.fetchProfile({ closeOnEose: true }); - if (!user.profile) return Promise.reject(new Error('profile not found')); - - await db.createProfile(cleanPubkey, profile); - - return user.profile; + return await user.fetchProfile(); }, { enabled: !!ndk, @@ -35,6 +27,7 @@ export function useProfile(pubkey: string, embed?: string) { refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, + retry: 2, } ); diff --git a/src/utils/parser.ts b/src/utils/parser.ts index cfe0ed78..3e6e9994 100644 --- a/src/utils/parser.ts +++ b/src/utils/parser.ts @@ -20,6 +20,8 @@ function isURL(string: string) { } export function parser(eventContent: string) { + if (!eventContent) return ''; + try { const content: RichContent = { parsed: null,