clean up and improve perf
This commit is contained in:
@@ -21,8 +21,10 @@
|
|||||||
"@dnd-kit/core": "^6.0.8",
|
"@dnd-kit/core": "^6.0.8",
|
||||||
"@getalby/sdk": "^2.4.0",
|
"@getalby/sdk": "^2.4.0",
|
||||||
"@nostr-dev-kit/ndk": "^1.3.0",
|
"@nostr-dev-kit/ndk": "^1.3.0",
|
||||||
|
"@nostr-dev-kit/ndk-cache-dexie": "^1.3.0",
|
||||||
"@nostr-fetch/adapter-ndk": "^0.12.2",
|
"@nostr-fetch/adapter-ndk": "^0.12.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@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-collapsible": "^1.0.3",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
|
|||||||
48
pnpm-lock.yaml
generated
48
pnpm-lock.yaml
generated
@@ -14,12 +14,18 @@ dependencies:
|
|||||||
'@nostr-dev-kit/ndk':
|
'@nostr-dev-kit/ndk':
|
||||||
specifier: ^1.3.0
|
specifier: ^1.3.0
|
||||||
version: 1.3.0(typescript@5.2.2)
|
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':
|
'@nostr-fetch/adapter-ndk':
|
||||||
specifier: ^0.12.2
|
specifier: ^0.12.2
|
||||||
version: 0.12.2(@nostr-dev-kit/ndk@1.3.0)(nostr-fetch@0.13.0)
|
version: 0.12.2(@nostr-dev-kit/ndk@1.3.0)(nostr-fetch@0.13.0)
|
||||||
'@radix-ui/react-alert-dialog':
|
'@radix-ui/react-alert-dialog':
|
||||||
specifier: ^1.0.5
|
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)
|
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':
|
'@radix-ui/react-collapsible':
|
||||||
specifier: ^1.0.3
|
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)
|
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
|
fastq: 1.15.0
|
||||||
dev: true
|
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):
|
/@nostr-dev-kit/ndk@1.3.0(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-ZRne/TF1IRqy35ZwLq3452YD+AUux0r5A+mZgHuGNRv7GJzr2gUl1QNRjs+Db+emXdaHn82LKTRn6cFrni1oPA==}
|
resolution: {integrity: sha512-ZRne/TF1IRqy35ZwLq3452YD+AUux0r5A+mZgHuGNRv7GJzr2gUl1QNRjs+Db+emXdaHn82LKTRn6cFrni1oPA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -883,6 +902,30 @@ packages:
|
|||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
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):
|
/@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==}
|
resolution: {integrity: sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3225,6 +3268,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/dexie@3.2.4:
|
||||||
|
resolution: {integrity: sha512-VKoTQRSv7+RnffpOJ3Dh6ozknBqzWw/F3iqMdsZg958R0AS8AnY9x9d1lbwENr0gzeGJHXKcGhAMRaqys6SxqA==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/didyoumean@1.2.2:
|
/didyoumean@1.2.2:
|
||||||
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|||||||
@@ -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
|
|
||||||
);
|
|
||||||
@@ -239,12 +239,6 @@ fn main() {
|
|||||||
sql: include_str!("../migrations/20230918235335_add_uniq_to_relay.sql"),
|
sql: include_str!("../migrations/20230918235335_add_uniq_to_relay.sql"),
|
||||||
kind: MigrationKind::Up,
|
kind: MigrationKind::Up,
|
||||||
},
|
},
|
||||||
Migration {
|
|
||||||
version: 20230921085234,
|
|
||||||
description: "add metadata",
|
|
||||||
sql: include_str!("../migrations/20230921085234_add_metadata_table.sql"),
|
|
||||||
kind: MigrationKind::Up,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.build(),
|
.build(),
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
import NDK from '@nostr-dev-kit/ndk';
|
import NDK from '@nostr-dev-kit/ndk';
|
||||||
|
import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie';
|
||||||
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
|
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
|
||||||
import { message } from '@tauri-apps/api/dialog';
|
import { message } from '@tauri-apps/api/dialog';
|
||||||
import { fetch } from '@tauri-apps/api/http';
|
import { fetch } from '@tauri-apps/api/http';
|
||||||
import { NostrFetcher } from 'nostr-fetch';
|
import { NostrFetcher } from 'nostr-fetch';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import TauriAdapter from '@libs/ndk/cache';
|
|
||||||
import { useStorage } from '@libs/storage/provider';
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
export const NDKInstance = () => {
|
export const NDKInstance = () => {
|
||||||
const { db } = useStorage();
|
|
||||||
|
|
||||||
const [ndk, setNDK] = useState<NDK | undefined>(undefined);
|
const [ndk, setNDK] = useState<NDK | undefined>(undefined);
|
||||||
const [relayUrls, setRelayUrls] = useState<string[]>([]);
|
const [relayUrls, setRelayUrls] = useState<string[]>([]);
|
||||||
|
|
||||||
const cacheAdapter = useMemo(() => new TauriAdapter(), []);
|
const { db } = useStorage();
|
||||||
const fetcher = useMemo(
|
const fetcher = useMemo(
|
||||||
() => (ndk ? NostrFetcher.withCustomPool(ndkAdapter(ndk)) : null),
|
() => (ndk ? NostrFetcher.withCustomPool(ndkAdapter(ndk)) : null),
|
||||||
[ndk]
|
[ndk]
|
||||||
@@ -57,9 +55,10 @@ export const NDKInstance = () => {
|
|||||||
|
|
||||||
async function initNDK() {
|
async function initNDK() {
|
||||||
const explicitRelayUrls = await getExplicitRelays();
|
const explicitRelayUrls = await getExplicitRelays();
|
||||||
|
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'lume_ndkcache' });
|
||||||
const instance = new NDK({
|
const instance = new NDK({
|
||||||
explicitRelayUrls,
|
explicitRelayUrls,
|
||||||
cacheAdapter,
|
cacheAdapter: dexieAdapter,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -77,10 +76,6 @@ export const NDKInstance = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ndk) initNDK();
|
if (!ndk) initNDK();
|
||||||
|
|
||||||
return () => {
|
|
||||||
cacheAdapter.saveCache();
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -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 { BaseDirectory, removeFile } from '@tauri-apps/api/fs';
|
||||||
import { Platform } from '@tauri-apps/api/os';
|
import { Platform } from '@tauri-apps/api/os';
|
||||||
import Database from 'tauri-plugin-sql-api';
|
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 { FULL_RELAYS } from '@stores/constants';
|
||||||
|
|
||||||
import { toRawEvent } from '@utils/rawEvent';
|
|
||||||
import { Account, DBEvent, Relays, Widget } from '@utils/types';
|
import { Account, DBEvent, Relays, Widget } from '@utils/types';
|
||||||
|
|
||||||
export class LumeStorage {
|
export class LumeStorage {
|
||||||
@@ -124,6 +123,7 @@ export class LumeStorage {
|
|||||||
|
|
||||||
public async updateLastLogin() {
|
public async updateLastLogin() {
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
this.account.last_login_at = now;
|
||||||
return await this.db.execute(
|
return await this.db.execute(
|
||||||
'UPDATE accounts SET last_login_at = $1 WHERE id = $2;',
|
'UPDATE accounts SET last_login_at = $1 WHERE id = $2;',
|
||||||
[now, this.account.id]
|
[now, this.account.id]
|
||||||
@@ -298,38 +298,6 @@ export class LumeStorage {
|
|||||||
return results.length < 1;
|
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() {
|
public async getExplicitRelayUrls() {
|
||||||
if (!this.account) return FULL_RELAYS;
|
if (!this.account) return FULL_RELAYS;
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function ComposerModal() {
|
|||||||
<ComposeIcon className="h-4 w-4 text-white" />
|
<ComposeIcon className="h-4 w-4 text-white" />
|
||||||
</button>
|
</button>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
<Dialog.Portal className="relative z-10">
|
<Dialog.Portal>
|
||||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
|
||||||
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { minidenticon } from 'minidenticons';
|
import { minidenticon } from 'minidenticons';
|
||||||
import { ImgHTMLAttributes, useState } from 'react';
|
import { ImgHTMLAttributes, memo, useState } from 'react';
|
||||||
|
|
||||||
export function Image({ src, ...props }: ImgHTMLAttributes<HTMLImageElement>) {
|
export const Image = memo(function Image({
|
||||||
|
src,
|
||||||
|
...props
|
||||||
|
}: ImgHTMLAttributes<HTMLImageElement>) {
|
||||||
const [isError, setIsError] = useState(false);
|
const [isError, setIsError] = useState(false);
|
||||||
|
|
||||||
if (isError || !src) {
|
if (isError || !src) {
|
||||||
@@ -20,9 +23,10 @@ export function Image({ src, ...props }: ImgHTMLAttributes<HTMLImageElement>) {
|
|||||||
currentTarget.onerror = null;
|
currentTarget.onerror = null;
|
||||||
setIsError(true);
|
setIsError(true);
|
||||||
}}
|
}}
|
||||||
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
alt="lume default img"
|
alt="lume default img"
|
||||||
style={{ contentVisibility: 'auto' }}
|
style={{ contentVisibility: 'auto' }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
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 { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
import { ChatsList } from '@app/chats/components/list';
|
import { ChatsList } from '@app/chats/components/list';
|
||||||
|
|
||||||
|
import { useStorage } from '@libs/storage/provider';
|
||||||
|
|
||||||
import { ActiveAccount } from '@shared/accounts/active';
|
import { ActiveAccount } from '@shared/accounts/active';
|
||||||
import { ComposerModal } from '@shared/composer';
|
import { ComposerModal } from '@shared/composer';
|
||||||
import { Frame } from '@shared/frame';
|
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 { useActivities } from '@stores/activities';
|
||||||
import { useSidebar } from '@stores/sidebar';
|
import { useSidebar } from '@stores/sidebar';
|
||||||
@@ -15,6 +25,9 @@ import { useSidebar } from '@stores/sidebar';
|
|||||||
import { compactNumber } from '@utils/number';
|
import { compactNumber } from '@utils/number';
|
||||||
|
|
||||||
export function Navigation() {
|
export function Navigation() {
|
||||||
|
const { db } = useStorage();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
const totalNewActivities = useActivities((state) => state.totalNewActivities);
|
const totalNewActivities = useActivities((state) => state.totalNewActivities);
|
||||||
|
|
||||||
const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]);
|
const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]);
|
||||||
@@ -30,8 +43,28 @@ export function Navigation() {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className="inline-flex h-16 w-full items-center justify-end px-3"
|
className="inline-flex h-16 w-full items-center justify-between px-3"
|
||||||
>
|
>
|
||||||
|
{db.platform !== 'darwin' ? (
|
||||||
|
<div className="inline-flex items-center gap-4 pl-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
className="inline-flex h-9 items-center justify-center"
|
||||||
|
>
|
||||||
|
<ArrowLeftIcon className="h-5 w-5 text-white/50" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigate(1)}
|
||||||
|
className="inline-flex h-9 items-center justify-center"
|
||||||
|
>
|
||||||
|
<ArrowRightIcon className="h-5 w-5 text-white/50" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
<ComposerModal />
|
<ComposerModal />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function ChildNote({ id, root }: { id: string; root?: string }) {
|
|||||||
<div className="absolute bottom-0 left-[18px] h-[calc(100%-3.4rem)] w-0.5 bg-gradient-to-t from-white/20 to-white/10" />
|
<div className="absolute bottom-0 left-[18px] h-[calc(100%-3.4rem)] w-0.5 bg-gradient-to-t from-white/20 to-white/10" />
|
||||||
<div className="relative mb-5 flex flex-col">
|
<div className="relative mb-5 flex flex-col">
|
||||||
<div className="relative z-10 flex items-start gap-3">
|
<div className="relative z-10 flex items-start gap-3">
|
||||||
<div className="inline-flex h-11 w-11 items-end justify-center rounded-lg bg-black pb-1">
|
<div className="inline-flex h-10 w-10 items-end justify-center rounded-lg bg-black pb-1">
|
||||||
<img src="/lume.png" alt="lume" className="h-auto w-1/3" />
|
<img src="/lume.png" alt="lume" className="h-auto w-1/3" />
|
||||||
</div>
|
</div>
|
||||||
<h5 className="truncate font-semibold leading-none text-white">
|
<h5 className="truncate font-semibold leading-none text-white">
|
||||||
|
|||||||
@@ -12,19 +12,26 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
|||||||
<div className="relative z-10">
|
<div className="relative z-10">
|
||||||
<div className="relative flex flex-col">
|
<div className="relative flex flex-col">
|
||||||
<User pubkey={event.pubkey} time={event.created_at} />
|
<User pubkey={event.pubkey} time={event.created_at} />
|
||||||
<div className="-mt-6 flex items-start gap-3">
|
<div className="-mt-5 flex items-start gap-3">
|
||||||
<div className="w-11 shrink-0" />
|
<div className="w-10 shrink-0" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<TextNote content={event.content} />
|
<TextNote content={event.content} />
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} root={root} />
|
<NoteActions
|
||||||
|
id={event.id}
|
||||||
|
pubkey={event.pubkey}
|
||||||
|
root={root}
|
||||||
|
extraButtons={false}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{event.replies ? (
|
<div className="pl-14">
|
||||||
event.replies.map((sub) => <SubReply key={sub.id} event={sub} />)
|
{event.replies ? (
|
||||||
) : (
|
event.replies.map((sub) => <SubReply key={sub.id} event={sub} />)
|
||||||
<div className="pb-3" />
|
) : (
|
||||||
)}
|
<div className="pb-3" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function RepliesList({ id }: { id: string }) {
|
|||||||
<h5 className="mb-2 text-lg font-semibold text-white">
|
<h5 className="mb-2 text-lg font-semibold text-white">
|
||||||
{data?.length || 0} replies
|
{data?.length || 0} replies
|
||||||
</h5>
|
</h5>
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-5">
|
||||||
{data?.length === 0 ? (
|
{data?.length === 0 ? (
|
||||||
<div className="mt-2 flex w-full items-center justify-center rounded-xl bg-white/10 backdrop-blur-xl">
|
<div className="mt-2 flex w-full items-center justify-center rounded-xl bg-white/10 backdrop-blur-xl">
|
||||||
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ export function SubReply({ event }: { event: NDKEvent }) {
|
|||||||
return (
|
return (
|
||||||
<div className="relative z-10 mb-3 mt-5 flex flex-col">
|
<div className="relative z-10 mb-3 mt-5 flex flex-col">
|
||||||
<User pubkey={event.pubkey} time={event.created_at} />
|
<User pubkey={event.pubkey} time={event.created_at} />
|
||||||
<div className="-mt-6 flex items-start gap-3">
|
<div className="-mt-5 flex items-start gap-3">
|
||||||
<div className="w-11 shrink-0" />
|
<div className="w-10 shrink-0" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<TextNote />
|
<TextNote content={event.content} />
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
<NoteActions id={event.id} pubkey={event.pubkey} extraButtons={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export function NoteSkeleton() {
|
|||||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
<div className="relative h-11 w-11 shrink overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-3 w-20 rounded bg-white/10 backdrop-blur-xl" />
|
<div className="h-3 w-20 rounded bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
<div className="-mt-6 animate-pulse pl-[49px]">
|
<div className="-mt-5 flex animate-pulse gap-3">
|
||||||
|
<div className="w-10 shrink-0" />
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div className="h-3 w-full rounded bg-white/10 backdrop-blur-xl" />
|
<div className="h-3 w-full rounded bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-3 w-2/3 rounded bg-white/10 backdrop-blur-xl" />
|
<div className="h-3 w-2/3 rounded bg-white/10 backdrop-blur-xl" />
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
|
import * as Avatar from '@radix-ui/react-avatar';
|
||||||
import * as HoverCard from '@radix-ui/react-hover-card';
|
import * as HoverCard from '@radix-ui/react-hover-card';
|
||||||
|
import { minidenticon } from 'minidenticons';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|
||||||
import { RepostIcon, WorldIcon } from '@shared/icons';
|
import { RepostIcon, WorldIcon } from '@shared/icons';
|
||||||
import { Image } from '@shared/image';
|
|
||||||
import { NIP05 } from '@shared/nip05';
|
import { NIP05 } from '@shared/nip05';
|
||||||
|
|
||||||
import { formatCreatedAt } from '@utils/createdAt';
|
import { formatCreatedAt } from '@utils/createdAt';
|
||||||
@@ -32,7 +33,10 @@ export const User = memo(function User({
|
|||||||
embedProfile?: string;
|
embedProfile?: string;
|
||||||
}) {
|
}) {
|
||||||
const { status, user } = useProfile(pubkey, embedProfile);
|
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 (status === 'loading') {
|
||||||
if (variant === 'avatar') {
|
if (variant === 'avatar') {
|
||||||
@@ -51,8 +55,8 @@ export const User = memo(function User({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="relative z-10 h-10 w-10 shrink-0 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
<div className="h-10 w-10 shrink-0 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
||||||
<div className="h-3.5 w-36 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
<div className="h-3.5 w-36 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -60,14 +64,20 @@ export const User = memo(function User({
|
|||||||
|
|
||||||
if (variant === 'mention') {
|
if (variant === 'mention') {
|
||||||
return (
|
return (
|
||||||
<div className="relative z-10 flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button type="button" className="relative z-40 h-6 w-6 shrink-0 overflow-hidden">
|
<Avatar.Root className="shrink-0">
|
||||||
<Image
|
<Avatar.Image
|
||||||
src={user?.picture || user?.image}
|
src={user?.picture || user?.image}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
className="h-6 w-6 rounded object-cover"
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-6 w-6 rounded"
|
||||||
/>
|
/>
|
||||||
</button>
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img src={svgURI} alt={pubkey} className="h-6 w-6 rounded bg-black" />
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
<div className="flex flex-1 items-baseline gap-2">
|
<div className="flex flex-1 items-baseline gap-2">
|
||||||
<h5 className="max-w-[10rem] truncate font-semibold leading-none text-white">
|
<h5 className="max-w-[10rem] truncate font-semibold leading-none text-white">
|
||||||
{user?.name ||
|
{user?.name ||
|
||||||
@@ -85,11 +95,19 @@ export const User = memo(function User({
|
|||||||
if (variant === 'large') {
|
if (variant === 'large') {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-col gap-2.5">
|
<div className="flex h-full w-full flex-col gap-2.5">
|
||||||
<Image
|
<Avatar.Root className="shrink-0">
|
||||||
src={user?.picture || user?.image}
|
<Avatar.Image
|
||||||
alt={pubkey}
|
src={user?.picture || user?.image}
|
||||||
className="h-14 w-14 shrink-0 rounded-lg object-cover"
|
alt={pubkey}
|
||||||
/>
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-14 w-14 rounded-lg"
|
||||||
|
/>
|
||||||
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img src={svgURI} alt={pubkey} className="h-14 w-14 rounded-lg bg-black" />
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
<div className="flex h-full flex-col items-start justify-between">
|
<div className="flex h-full flex-col items-start justify-between">
|
||||||
<div className="flex flex-col items-start gap-1 text-start">
|
<div className="flex flex-col items-start gap-1 text-start">
|
||||||
<p className="max-w-[15rem] truncate text-lg font-semibold leading-none text-white">
|
<p className="max-w-[15rem] truncate text-lg font-semibold leading-none text-white">
|
||||||
@@ -125,11 +143,19 @@ export const User = memo(function User({
|
|||||||
if (variant === 'simple') {
|
if (variant === 'simple') {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2.5">
|
<div className="flex items-center gap-2.5">
|
||||||
<Image
|
<Avatar.Root className="shrink-0">
|
||||||
src={user?.picture || user?.image}
|
<Avatar.Image
|
||||||
alt={pubkey}
|
src={user?.picture || user?.image}
|
||||||
className="h-12 w-12 shrink-0 rounded-lg object-cover"
|
alt={pubkey}
|
||||||
/>
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-12 w-12 rounded-lg"
|
||||||
|
/>
|
||||||
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img src={svgURI} alt={pubkey} className="h-12 w-12 rounded-lg bg-black" />
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
<div className="flex w-full flex-col items-start gap-1">
|
<div className="flex w-full flex-col items-start gap-1">
|
||||||
<h3 className="max-w-[15rem] truncate font-medium leading-none text-white">
|
<h3 className="max-w-[15rem] truncate font-medium leading-none text-white">
|
||||||
{user?.name || user?.display_name || user?.displayName}
|
{user?.name || user?.display_name || user?.displayName}
|
||||||
@@ -144,11 +170,19 @@ export const User = memo(function User({
|
|||||||
|
|
||||||
if (variant === 'avatar') {
|
if (variant === 'avatar') {
|
||||||
return (
|
return (
|
||||||
<Image
|
<Avatar.Root>
|
||||||
src={user?.picture || user?.image}
|
<Avatar.Image
|
||||||
alt={pubkey}
|
src={user?.picture || user?.image}
|
||||||
className="h-12 w-12 shrink-0 rounded-lg object-cover"
|
alt={pubkey}
|
||||||
/>
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-12 w-12 rounded-lg"
|
||||||
|
/>
|
||||||
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img src={svgURI} alt={pubkey} className="h-12 w-12 rounded-lg bg-black" />
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,11 +193,19 @@ export const User = memo(function User({
|
|||||||
<RepostIcon className="h-6 w-6 text-blue-500" />
|
<RepostIcon className="h-6 w-6 text-blue-500" />
|
||||||
</div>
|
</div>
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2">
|
||||||
<Image
|
<Avatar.Root className="shrink-0">
|
||||||
src={user?.picture || user?.image}
|
<Avatar.Image
|
||||||
alt={pubkey}
|
src={user?.picture || user?.image}
|
||||||
className="relative z-20 inline-block h-6 w-6 rounded"
|
alt={pubkey}
|
||||||
/>
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-6 w-6 rounded"
|
||||||
|
/>
|
||||||
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img src={svgURI} alt={pubkey} className="h-6 w-6 rounded bg-black" />
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
<div className="inline-flex items-baseline gap-1">
|
<div className="inline-flex items-baseline gap-1">
|
||||||
<h5 className="max-w-[10rem] truncate font-medium leading-none text-white/80">
|
<h5 className="max-w-[10rem] truncate font-medium leading-none text-white/80">
|
||||||
{user?.name ||
|
{user?.name ||
|
||||||
@@ -181,14 +223,22 @@ export const User = memo(function User({
|
|||||||
if (variant === 'thread') {
|
if (variant === 'thread') {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Image
|
<Avatar.Root className="shrink-0">
|
||||||
src={user?.picture || user?.image}
|
<Avatar.Image
|
||||||
alt={pubkey}
|
src={user?.picture || user?.image}
|
||||||
className="relative z-20 inline-block h-10 w-10 rounded-lg"
|
alt={pubkey}
|
||||||
/>
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-10 w-10 rounded-lg"
|
||||||
|
/>
|
||||||
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img src={svgURI} alt={pubkey} className="h-10 w-10 rounded-lg bg-black" />
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
<div className="flex flex-1 flex-col gap-2">
|
<div className="flex flex-1 flex-col gap-2">
|
||||||
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
||||||
{user?.name || user?.display_name || user?.displayName}
|
{user?.name || user?.display_name || user?.displayName || 'Anon'}
|
||||||
</h5>
|
</h5>
|
||||||
<div className="inline-flex items-center gap-2">
|
<div className="inline-flex items-center gap-2">
|
||||||
<span className="leading-none text-white/50">{createdAt}</span>
|
<span className="leading-none text-white/50">{createdAt}</span>
|
||||||
@@ -204,16 +254,23 @@ export const User = memo(function User({
|
|||||||
<HoverCard.Root>
|
<HoverCard.Root>
|
||||||
<div className="relative z-10 flex items-start gap-3">
|
<div className="relative z-10 flex items-start gap-3">
|
||||||
<HoverCard.Trigger asChild>
|
<HoverCard.Trigger asChild>
|
||||||
<button
|
<Avatar.Root className="shrink-0">
|
||||||
type="button"
|
<Avatar.Image
|
||||||
className="relative z-40 h-10 w-10 shrink-0 overflow-hidden"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={user?.picture || user?.image}
|
src={user?.picture || user?.image}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
className="h-10 w-10 rounded-lg object-cover"
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-10 w-10 rounded-lg border border-white/5"
|
||||||
/>
|
/>
|
||||||
</button>
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img
|
||||||
|
src={svgURI}
|
||||||
|
alt={pubkey}
|
||||||
|
className="h-10 w-10 rounded-lg border border-white/5 bg-black"
|
||||||
|
/>
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
</HoverCard.Trigger>
|
</HoverCard.Trigger>
|
||||||
<div className="flex flex-1 items-baseline gap-2">
|
<div className="flex flex-1 items-baseline gap-2">
|
||||||
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
||||||
@@ -232,11 +289,23 @@ export const User = memo(function User({
|
|||||||
sideOffset={5}
|
sideOffset={5}
|
||||||
>
|
>
|
||||||
<div className="flex gap-2.5 border-b border-white/5 px-3 py-3">
|
<div className="flex gap-2.5 border-b border-white/5 px-3 py-3">
|
||||||
<Image
|
<Avatar.Root className="shrink-0">
|
||||||
src={user?.picture || user?.image}
|
<Avatar.Image
|
||||||
alt={pubkey}
|
src={user?.picture || user?.image}
|
||||||
className="h-10 w-10 shrink-0 rounded-lg object-cover"
|
alt={pubkey}
|
||||||
/>
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
style={{ contentVisibility: 'auto' }}
|
||||||
|
className="h-10 w-10 rounded-lg border border-white/5"
|
||||||
|
/>
|
||||||
|
<Avatar.Fallback delayMs={300}>
|
||||||
|
<img
|
||||||
|
src={svgURI}
|
||||||
|
alt={pubkey}
|
||||||
|
className="h-10 w-10 rounded-lg border border-white/5 bg-black"
|
||||||
|
/>
|
||||||
|
</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
<div className="flex flex-1 flex-col gap-2">
|
<div className="flex flex-1 flex-col gap-2">
|
||||||
<div className="inline-flex flex-col gap-1">
|
<div className="inline-flex flex-col gap-1">
|
||||||
<h5 className="text-sm font-semibold leading-none">
|
<h5 className="text-sm font-semibold leading-none">
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export function EventLoader({ firstTime }: { firstTime: boolean }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getEvents() {
|
async function getEvents() {
|
||||||
const events = await getAllEventsSinceLastLogin();
|
const events = await getAllEventsSinceLastLogin();
|
||||||
|
console.log('total event found: ', events.data.length);
|
||||||
|
|
||||||
const promises = await Promise.all(
|
const promises = await Promise.all(
|
||||||
events.data.map(async (event) => await db.createEvent(event))
|
events.data.map(async (event) => await db.createEvent(event))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ export function useNostr() {
|
|||||||
|
|
||||||
if (!customSince) {
|
if (!customSince) {
|
||||||
if (dbEventsEmpty || db.account.last_login_at === 0) {
|
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 {
|
} else {
|
||||||
since = db.account.last_login_at;
|
since = db.account.last_login_at;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { useNDK } from '@libs/ndk/provider';
|
import { useNDK } from '@libs/ndk/provider';
|
||||||
import { useStorage } from '@libs/storage/provider';
|
|
||||||
|
|
||||||
export function useProfile(pubkey: string, embed?: string) {
|
export function useProfile(pubkey: string, embed?: string) {
|
||||||
const { db } = useStorage();
|
|
||||||
const { ndk } = useNDK();
|
const { ndk } = useNDK();
|
||||||
const {
|
const {
|
||||||
status,
|
status,
|
||||||
@@ -21,13 +19,7 @@ export function useProfile(pubkey: string, embed?: string) {
|
|||||||
|
|
||||||
const cleanPubkey = pubkey.replace('-', '');
|
const cleanPubkey = pubkey.replace('-', '');
|
||||||
const user = ndk.getUser({ hexpubkey: cleanPubkey });
|
const user = ndk.getUser({ hexpubkey: cleanPubkey });
|
||||||
|
return await user.fetchProfile();
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!ndk,
|
enabled: !!ndk,
|
||||||
@@ -35,6 +27,7 @@ export function useProfile(pubkey: string, embed?: string) {
|
|||||||
refetchOnMount: false,
|
refetchOnMount: false,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
refetchOnReconnect: false,
|
refetchOnReconnect: false,
|
||||||
|
retry: 2,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ function isURL(string: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parser(eventContent: string) {
|
export function parser(eventContent: string) {
|
||||||
|
if (!eventContent) return '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content: RichContent = {
|
const content: RichContent = {
|
||||||
parsed: null,
|
parsed: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user