diff --git a/packages/app/src/Components/Tasks/BackupKey.tsx b/packages/app/src/Components/Tasks/BackupKey.tsx index 2464daf0..7c194bd8 100644 --- a/packages/app/src/Components/Tasks/BackupKey.tsx +++ b/packages/app/src/Components/Tasks/BackupKey.tsx @@ -1,4 +1,4 @@ -import { MetadataCache } from "@snort/system"; +import { CachedMetadata } from "@snort/system"; import { FormattedMessage } from "react-intl"; import { Link } from "react-router-dom"; @@ -10,7 +10,7 @@ export class BackupKeyTask extends BaseUITask { id = "backup-key"; noBaseStyle = true; - check(_: MetadataCache, session: LoginSession): boolean { + check(_: CachedMetadata, session: LoginSession): boolean { return !this.state.muted && session.type == "private_key"; } diff --git a/packages/app/src/Components/Tasks/Nip5Task.tsx b/packages/app/src/Components/Tasks/Nip5Task.tsx index 44dd5f22..da874fc6 100644 --- a/packages/app/src/Components/Tasks/Nip5Task.tsx +++ b/packages/app/src/Components/Tasks/Nip5Task.tsx @@ -1,4 +1,4 @@ -import { MetadataCache } from "@snort/system"; +import { CachedMetadata } from "@snort/system"; import { FormattedMessage } from "react-intl"; import { Link } from "react-router-dom"; @@ -7,7 +7,7 @@ import { BaseUITask } from "@/Components/Tasks/index"; export class Nip5Task extends BaseUITask { id = "buy-nip5"; - check(user: MetadataCache): boolean { + check(user: CachedMetadata): boolean { return !this.state.muted && !user.nip05; } diff --git a/packages/app/src/Components/Tasks/RenewSubscription.tsx b/packages/app/src/Components/Tasks/RenewSubscription.tsx index e07e6f2d..1ad43270 100644 --- a/packages/app/src/Components/Tasks/RenewSubscription.tsx +++ b/packages/app/src/Components/Tasks/RenewSubscription.tsx @@ -1,4 +1,4 @@ -import { MetadataCache } from "@snort/system"; +import { CachedMetadata } from "@snort/system"; import { FormattedMessage } from "react-intl"; import { BaseUITask } from "@/Components/Tasks/index"; @@ -9,7 +9,7 @@ import { getCurrentSubscription } from "@/Utils/Subscription"; export class RenewSubTask extends BaseUITask { id = "renew-sub"; - check(user: MetadataCache, session: LoginSession): boolean { + check(user: CachedMetadata, session: LoginSession): boolean { const sub = getCurrentSubscription(session.subscriptions); return !sub && session.subscriptions.length > 0; } diff --git a/packages/app/src/Components/Tasks/index.ts b/packages/app/src/Components/Tasks/index.ts index 476f3fb8..a09c10d4 100644 --- a/packages/app/src/Components/Tasks/index.ts +++ b/packages/app/src/Components/Tasks/index.ts @@ -1,4 +1,4 @@ -import { MetadataCache } from "@snort/system"; +import { CachedMetadata } from "@snort/system"; import { LoginSession } from "@/Utils/Login"; @@ -8,7 +8,7 @@ export interface UITask { /** * Run checks to determine if this Task should be triggered for this user */ - check(user: MetadataCache, session: LoginSession): boolean; + check(user: CachedMetadata, session: LoginSession): boolean; mute(): void; load(cb: () => void): void; render(): JSX.Element; @@ -26,7 +26,7 @@ export abstract class BaseUITask implements UITask { abstract id: string; noBaseStyle = false; - abstract check(user: MetadataCache, session: LoginSession): boolean; + abstract check(user: CachedMetadata, session: LoginSession): boolean; abstract render(): JSX.Element; constructor() { diff --git a/packages/app/src/Components/Textarea/Textarea.tsx b/packages/app/src/Components/Textarea/Textarea.tsx index 749237ad..43f1a131 100644 --- a/packages/app/src/Components/Textarea/Textarea.tsx +++ b/packages/app/src/Components/Textarea/Textarea.tsx @@ -1,7 +1,7 @@ import "@webscopeio/react-textarea-autocomplete/style.css"; import "./Textarea.css"; -import { MetadataCache, NostrPrefix } from "@snort/system"; +import { CachedMetadata, NostrPrefix } from "@snort/system"; import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import { useIntl } from "react-intl"; import TextareaAutosize from "react-textarea-autosize"; @@ -28,7 +28,7 @@ const EmojiItem = ({ entity: { name, char } }: { entity: EmojiItemProps }) => { ); }; -const UserItem = (metadata: MetadataCache) => { +const UserItem = (metadata: CachedMetadata) => { const { pubkey, display_name, nip05, ...rest } = metadata; return (
@@ -84,7 +84,7 @@ const Textarea = (props: TextareaProps) => { "@": { afterWhitespace: true, dataProvider: userDataProvider, - component: (props: { entity: MetadataCache }) => , + component: (props: { entity: CachedMetadata }) => , output: (item: { pubkey: string }) => `@${hexToBech32(NostrPrefix.PublicKey, item.pubkey)}`, }, }} diff --git a/packages/app/src/Components/User/ProfileLink.tsx b/packages/app/src/Components/User/ProfileLink.tsx index 42ad992d..98feee1c 100644 --- a/packages/app/src/Components/User/ProfileLink.tsx +++ b/packages/app/src/Components/User/ProfileLink.tsx @@ -1,4 +1,4 @@ -import { MetadataCache, NostrLink, NostrPrefix, UserMetadata } from "@snort/system"; +import { CachedMetadata, NostrLink, NostrPrefix, UserMetadata } from "@snort/system"; import { SnortContext } from "@snort/system-react"; import { ReactNode, useContext } from "react"; import { Link, LinkProps } from "react-router-dom"; @@ -13,7 +13,7 @@ export function ProfileLink({ ...others }: { pubkey: string; - user?: UserMetadata | MetadataCache; + user?: UserMetadata | CachedMetadata; explicitLink?: string; children?: ReactNode; } & Omit) { diff --git a/packages/app/src/Components/User/UserWebsiteLink.tsx b/packages/app/src/Components/User/UserWebsiteLink.tsx index e35d1e5c..8d3173ed 100644 --- a/packages/app/src/Components/User/UserWebsiteLink.tsx +++ b/packages/app/src/Components/User/UserWebsiteLink.tsx @@ -1,10 +1,10 @@ import "./UserWebsiteLink.css"; -import { MetadataCache, UserMetadata } from "@snort/system"; +import { CachedMetadata, UserMetadata } from "@snort/system"; import Icon from "@/Components/Icons/Icon"; -export function UserWebsiteLink({ user }: { user?: MetadataCache | UserMetadata }) { +export function UserWebsiteLink({ user }: { user?: CachedMetadata | UserMetadata }) { const website_url = user?.website && !user.website.startsWith("http") ? "https://" + user.website : user?.website || ""; diff --git a/packages/app/src/Db/FuzzySearch.ts b/packages/app/src/Db/FuzzySearch.ts index c40f2154..0210cfac 100644 --- a/packages/app/src/Db/FuzzySearch.ts +++ b/packages/app/src/Db/FuzzySearch.ts @@ -1,4 +1,5 @@ import Fuse from "fuse.js"; +import {CachedMetadata} from "@snort/system"; export type FuzzySearchResult = { pubkey: string; @@ -19,23 +20,43 @@ export const addEventToFuzzySearch = ev => { if (ev.kind !== 0) { return; } - const existing = profileTimestamps.get(ev.pubkey); - if (existing) { - if (existing > ev.created_at) { - return; + requestAnimationFrame(() => { + const existing = profileTimestamps.get(ev.pubkey); + if (existing) { + if (existing > ev.created_at) { + return; + } + fuzzySearch.remove(doc => doc.pubkey === ev.pubkey); } - fuzzySearch.remove(doc => doc.pubkey === ev.pubkey); - } - profileTimestamps.set(ev.pubkey, ev.created_at); - try { - const data = JSON.parse(ev.content); - if (ev.pubkey && (data.name || data.display_name || data.nip05)) { - data.pubkey = ev.pubkey; - fuzzySearch.add(data); + profileTimestamps.set(ev.pubkey, ev.created_at); + try { + const data = JSON.parse(ev.content); + if (ev.pubkey && (data.name || data.display_name || data.nip05)) { + data.pubkey = ev.pubkey; + fuzzySearch.add(data); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); - } + }); }; +export const addCachedMetadataToFuzzySearch = (profile: CachedMetadata) => { + // TODO add profiles from Cache + requestAnimationFrame(() => { + const existing = profileTimestamps.get(profile.pubkey); + if (existing) { + if (existing > profile.created) { + return; + } + fuzzySearch.remove(doc => doc.pubkey === profile.pubkey); + } + profileTimestamps.set(profile.pubkey, profile.created); + if (profile.pubkey && (profile.name || profile.display_name || profile.nip05)) { + fuzzySearch.add(profile); + console.log('added profile to fuzzy search', profile); + } + }); +} + export default fuzzySearch; diff --git a/packages/app/src/Pages/Messages/ChatParticipant.tsx b/packages/app/src/Pages/Messages/ChatParticipant.tsx index d09535c2..226a1e89 100644 --- a/packages/app/src/Pages/Messages/ChatParticipant.tsx +++ b/packages/app/src/Pages/Messages/ChatParticipant.tsx @@ -1,4 +1,4 @@ -import { MetadataCache } from "@snort/system"; +import { CachedMetadata } from "@snort/system"; import { ChatParticipant } from "@/chat"; import NoteToSelf from "@/Components/User/NoteToSelf"; @@ -10,5 +10,5 @@ export function ChatParticipantProfile({ participant }: { participant: ChatParti if (participant.id === publicKey) { return ; } - return ; + return ; } diff --git a/packages/app/src/Pages/NetworkGraph.tsx b/packages/app/src/Pages/NetworkGraph.tsx index 1728cbe1..20645daa 100644 --- a/packages/app/src/Pages/NetworkGraph.tsx +++ b/packages/app/src/Pages/NetworkGraph.tsx @@ -1,4 +1,4 @@ -import { MetadataCache, socialGraphInstance, STR, UID } from "@snort/system"; +import { CachedMetadata, socialGraphInstance, STR, UID } from "@snort/system"; import { SnortContext } from "@snort/system-react"; import { useContext, useEffect, useState } from "react"; import { NodeObject } from "react-force-graph-3d"; @@ -12,7 +12,7 @@ import { defaultAvatar } from "../Utils"; interface GraphNode { id: UID; - profile?: MetadataCache; + profile?: CachedMetadata; distance: number; val: number; inboundCount: number; diff --git a/packages/app/src/Pages/Profile/ProfilePage.tsx b/packages/app/src/Pages/Profile/ProfilePage.tsx index 96e32ef3..6c4efe93 100644 --- a/packages/app/src/Pages/Profile/ProfilePage.tsx +++ b/packages/app/src/Pages/Profile/ProfilePage.tsx @@ -4,7 +4,7 @@ import { fetchNip05Pubkey, LNURL } from "@snort/shared"; import { encodeTLVEntries, EventKind, - MetadataCache, + CachedMetadata, NostrPrefix, TLVEntryType, tryParseNostrLink, @@ -59,13 +59,13 @@ import { ZapTarget } from "@/Utils/Zapper"; interface ProfilePageProps { id?: string; - state?: MetadataCache; + state?: CachedMetadata; } export default function ProfilePage({ id: propId, state }: ProfilePageProps) { const params = useParams(); const location = useLocation(); - const profileState = (location.state as MetadataCache | undefined) || state; + const profileState = (location.state as CachedMetadata | undefined) || state; const navigate = useNavigate(); const [id, setId] = useState(profileState?.pubkey); const [relays, setRelays] = useState>(); diff --git a/packages/app/src/Utils/Notifications.ts b/packages/app/src/Utils/Notifications.ts index 16f7cbe5..30999541 100644 --- a/packages/app/src/Utils/Notifications.ts +++ b/packages/app/src/Utils/Notifications.ts @@ -1,6 +1,6 @@ import { base64 } from "@scure/base"; import { removeUndefined, unwrap } from "@snort/shared"; -import { EventKind, EventPublisher, MetadataCache, TaggedNostrEvent } from "@snort/system"; +import { EventKind, EventPublisher, CachedMetadata, TaggedNostrEvent } from "@snort/system"; import { UserCache } from "@/Cache"; import SnortApi from "@/External/SnortApi"; @@ -38,7 +38,7 @@ export async function makeNotification(ev: TaggedNostrEvent): Promise { diff --git a/packages/app/src/Utils/index.ts b/packages/app/src/Utils/index.ts index 3db6487d..812d6e21 100644 --- a/packages/app/src/Utils/index.ts +++ b/packages/app/src/Utils/index.ts @@ -9,7 +9,7 @@ import { encodeTLV, EventKind, HexKey, - MetadataCache, + CachedMetadata, NostrEvent, NostrLink, NostrPrefix, @@ -214,8 +214,8 @@ export function getLatestByPubkey(events: TaggedNostrEvent[]): Map { - const deduped = profiles.reduce((results: Map, ev) => { +export function getLatestProfileByPubkey(profiles: CachedMetadata[]): Map { + const deduped = profiles.reduce((results: Map, ev) => { if (!results.has(ev.pubkey)) { const latest = getNewestProfile(profiles.filter(a => a.pubkey === ev.pubkey)); if (latest) { @@ -223,7 +223,7 @@ export function getLatestProfileByPubkey(profiles: MetadataCache[]): Map()); + }, new Map()); return deduped; } @@ -255,7 +255,7 @@ export function getNewest(rawNotes: readonly TaggedNostrEvent[]) { } } -export function getNewestProfile(rawNotes: MetadataCache[]) { +export function getNewestProfile(rawNotes: CachedMetadata[]) { const notes = [...rawNotes]; notes.sort((a, b) => b.created - a.created); if (notes.length > 0) { @@ -279,7 +279,7 @@ export function tagFilterOfTextRepost(note: TaggedNostrEvent, id?: u256): (tag: tag[0] === "e" && tag[3] === "mention" && note.content === `#[${i}]` && (id ? tag[1] === id : true); } -export function groupByPubkey(acc: Record, user: MetadataCache) { +export function groupByPubkey(acc: Record, user: CachedMetadata) { return { ...acc, [user.pubkey]: user }; } diff --git a/packages/app/src/system.ts b/packages/app/src/system.ts index e8552673..8552a47f 100644 --- a/packages/app/src/system.ts +++ b/packages/app/src/system.ts @@ -2,7 +2,7 @@ import { removeUndefined, throwIfOffline } from "@snort/shared"; import { mapEventToProfile, NostrEvent, NostrSystem, ProfileLoaderService, socialGraphInstance } from "@snort/system"; import { RelayMetrics, SystemDb, UserCache, UserRelays } from "@/Cache"; -import { addEventToFuzzySearch } from "@/Db/FuzzySearch"; +import { addEventToFuzzySearch, addProfileToFuzzySearch } from "@/Db/FuzzySearch"; import { LoginStore } from "@/Utils/Login"; import { hasWasm, WasmOptimizer } from "@/Utils/wasm"; diff --git a/packages/system-react/src/useUserProfile.ts b/packages/system-react/src/useUserProfile.ts index 95dd0a6c..4410e6e6 100644 --- a/packages/system-react/src/useUserProfile.ts +++ b/packages/system-react/src/useUserProfile.ts @@ -1,13 +1,13 @@ import { useContext, useSyncExternalStore } from "react"; -import { HexKey, MetadataCache } from "@snort/system"; +import { HexKey, CachedMetadata } from "@snort/system"; import { SnortContext } from "./context"; /** * Gets a profile from cache or requests it from the relays */ -export function useUserProfile(pubKey?: HexKey): MetadataCache | undefined { +export function useUserProfile(pubKey?: HexKey): CachedMetadata | undefined { const system = useContext(SnortContext); - return useSyncExternalStore( + return useSyncExternalStore( h => { if (pubKey) { system.ProfileLoader.TrackKeys(pubKey); diff --git a/packages/system-web/src/index.ts b/packages/system-web/src/index.ts index 1f3366d2..9e53455a 100644 --- a/packages/system-web/src/index.ts +++ b/packages/system-web/src/index.ts @@ -1,4 +1,4 @@ -import { NostrEvent, MetadataCache, RelayMetrics, UsersRelays } from "@snort/system"; +import { NostrEvent, CachedMetadata, RelayMetrics, UsersRelays } from "@snort/system"; import Dexie, { Table } from "dexie"; const NAME = "snort-system"; @@ -13,7 +13,7 @@ const STORES = { export class SnortSystemDb extends Dexie { ready = false; - users!: Table; + users!: Table; relayMetrics!: Table; userRelays!: Table; events!: Table; diff --git a/packages/system/src/cache/index.ts b/packages/system/src/cache/index.ts index e1315b75..f7e327cb 100644 --- a/packages/system/src/cache/index.ts +++ b/packages/system/src/cache/index.ts @@ -1,7 +1,7 @@ import { FullRelaySettings, HexKey, NostrEvent, UserMetadata } from ".."; import { hexToBech32, unixNowMs, DexieTableLike } from "@snort/shared"; -export interface MetadataCache extends UserMetadata { +export interface CachedMetadata extends UserMetadata { /** * When the object was saved in cache */ @@ -58,7 +58,7 @@ export function mapEventToProfile(ev: NostrEvent) { npub: hexToBech32("npub", ev.pubkey), created: ev.created_at, loaded: unixNowMs(), - } as MetadataCache; + } as CachedMetadata; // sanitize non-string/number for (const [k, v] of Object.entries(ret)) { @@ -73,7 +73,7 @@ export function mapEventToProfile(ev: NostrEvent) { } export interface SnortSystemDb { - users: DexieTableLike; + users: DexieTableLike; relayMetrics: DexieTableLike; userRelays: DexieTableLike; events: DexieTableLike; diff --git a/packages/system/src/cache/user-metadata.ts b/packages/system/src/cache/user-metadata.ts index b3485b17..0895697c 100644 --- a/packages/system/src/cache/user-metadata.ts +++ b/packages/system/src/cache/user-metadata.ts @@ -1,17 +1,17 @@ -import { MetadataCache } from "."; +import { CachedMetadata } from "."; import { fetchNip05Pubkey, FeedCache, LNURL, DexieTableLike } from "@snort/shared"; -export class UserProfileCache extends FeedCache { +export class UserProfileCache extends FeedCache { #zapperQueue: Array<{ pubkey: string; lnurl: string }> = []; #nip5Queue: Array<{ pubkey: string; nip05: string }> = []; - constructor(table?: DexieTableLike) { + constructor(table?: DexieTableLike) { super("UserCache", table); this.#processZapperQueue(); this.#processNip5Queue(); } - key(of: MetadataCache): string { + key(of: CachedMetadata): string { return of.pubkey; } @@ -23,7 +23,7 @@ export class UserProfileCache extends FeedCache { } } - async search(q: string): Promise> { + async search(q: string): Promise> { if (this.table) { // on-disk cache will always have more data return ( @@ -41,7 +41,7 @@ export class UserProfileCache extends FeedCache { } else { return [...this.cache.values()] .filter(user => { - const profile = user as MetadataCache; + const profile = user as CachedMetadata; return ( profile.name?.includes(q) || profile.npub?.includes(q) || @@ -58,7 +58,7 @@ export class UserProfileCache extends FeedCache { * @param m Profile metadata * @returns */ - override async update(m: MetadataCache) { + override async update(m: CachedMetadata) { const updateType = await super.update(m); if (updateType !== "refresh") { const lnurl = m.lud16 ?? m.lud06; @@ -78,7 +78,7 @@ export class UserProfileCache extends FeedCache { return updateType; } - takeSnapshot(): MetadataCache[] { + takeSnapshot(): CachedMetadata[] { return [...this.cache.values()]; } diff --git a/packages/system/src/nostr-system.ts b/packages/system/src/nostr-system.ts index 7e5272ef..8ad13e36 100644 --- a/packages/system/src/nostr-system.ts +++ b/packages/system/src/nostr-system.ts @@ -9,7 +9,7 @@ import { NoteStore } from "./note-collection"; import { BuiltRawReqFilter, RequestBuilder } from "./request-builder"; import { RelayMetricHandler } from "./relay-metric-handler"; import { - MetadataCache, + CachedMetadata, ProfileLoaderService, RelayMetrics, SystemInterface, @@ -38,7 +38,7 @@ export interface NostrSystemEvents { export interface NostrsystemProps { relayCache?: FeedCache; - profileCache?: FeedCache; + profileCache?: FeedCache; relayMetrics?: FeedCache; eventsCache?: FeedCache; optimizer?: Optimizer; @@ -62,7 +62,7 @@ export class NostrSystem extends EventEmitter implements Syst /** * Storage class for user profiles */ - #profileCache: FeedCache; + #profileCache: FeedCache; /** * Storage class for relay metrics (connects/disconnects) diff --git a/packages/system/src/profile-cache.ts b/packages/system/src/profile-cache.ts index 84d2e511..377d61ac 100644 --- a/packages/system/src/profile-cache.ts +++ b/packages/system/src/profile-cache.ts @@ -1,16 +1,16 @@ import { unixNowMs } from "@snort/shared"; import { EventKind, TaggedNostrEvent, RequestBuilder } from "."; import { ProfileCacheExpire } from "./const"; -import { mapEventToProfile, MetadataCache } from "./cache"; +import { mapEventToProfile, CachedMetadata } from "./cache"; import { v4 as uuid } from "uuid"; import { BackgroundLoader } from "./background-loader"; -export class ProfileLoaderService extends BackgroundLoader { +export class ProfileLoaderService extends BackgroundLoader { override name(): string { return "ProfileLoaderService"; } - override onEvent(e: Readonly): MetadataCache | undefined { + override onEvent(e: Readonly): CachedMetadata | undefined { return mapEventToProfile(e); } @@ -30,11 +30,11 @@ export class ProfileLoaderService extends BackgroundLoader { return sub; } - protected override makePlaceholder(key: string): MetadataCache | undefined { + protected override makePlaceholder(key: string): CachedMetadata | undefined { return { pubkey: key, loaded: unixNowMs() - ProfileCacheExpire + 30_000, created: 0, - } as MetadataCache; + } as CachedMetadata; } }