diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index 20073eec..87bef328 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -8,7 +8,7 @@ import { getAuthor, getFollowedPubkeys } from '@/queries.ts'; import { booleanParamSchema, fileSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; import { uploadFile } from '@/utils/upload.ts'; -import { nostrNow } from '@/utils.ts'; +import { dedupeEvents, extractBech32, nostrNow } from '@/utils.ts'; import { createEvent, paginated, parseBody, updateListEvent } from '@/utils/api.ts'; import { lookupAccount } from '@/utils/lookup.ts'; import { renderAccounts, renderEventAccounts, renderStatuses } from '@/views.ts'; @@ -125,30 +125,33 @@ const accountSearchController: AppController = async (c) => { const query = decodeURIComponent(q); const store = await Storages.search(); + const bech32 = extractBech32(query); const [event, events] = await Promise.all([ - lookupAccount(query), + lookupAccount(bech32 ?? query), store.query([{ kinds: [0], search: query, limit }], { signal }), ]); + if (event) { + events.unshift(event); + } + const results = await hydrateEvents({ - events: event ? [event, ...events] : events, + events: dedupeEvents(events), store, signal, }); - if ((results.length < 1) && query.match(/npub1\w+/)) { - const possibleNpub = query; - try { - const npubHex = nip19.decode(possibleNpub); - return c.json([await accountFromPubkey(String(npubHex.data))]); - } catch (e) { - console.log(e); - return c.json([]); - } + const accounts = await Promise.all( + results.map((event) => renderAccount(event)), + ); + + // Render account from pubkey. + const pubkey = bech32ToPubkey(result.data.q); + if (pubkey && !accounts.find((account) => account.id === pubkey)) { + accounts.unshift(await accountFromPubkey(pubkey)); } - const accounts = await Promise.all(results.map((event) => renderAccount(event))); return c.json(accounts); }; diff --git a/src/controllers/api/search.ts b/src/controllers/api/search.ts index 6ae05f1c..3ca2aa3d 100644 --- a/src/controllers/api/search.ts +++ b/src/controllers/api/search.ts @@ -5,7 +5,7 @@ import { z } from 'zod'; import { AppController } from '@/app.ts'; import { booleanParamSchema } from '@/schema.ts'; import { Storages } from '@/storages.ts'; -import { bech32ToPubkey, dedupeEvents } from '@/utils.ts'; +import { bech32ToPubkey, dedupeEvents, extractBech32 } from '@/utils.ts'; import { nip05Cache } from '@/utils/nip05.ts'; import { accountFromPubkey, renderAccount } from '@/views/mastodon/accounts.ts'; import { renderStatus } from '@/views/mastodon/statuses.ts'; @@ -39,7 +39,7 @@ const searchController: AppController = async (c) => { ]); if (event) { - events.push(event); + events.unshift(event); } const results = dedupeEvents(events); @@ -169,30 +169,4 @@ async function getLookupFilters({ q, type, resolve }: SearchQuery, signal: Abort return filters; } -/** Extract a bech32 ID out of a search query string. */ -function extractBech32(value: string): string | undefined { - let bech32: string = value; - - try { - const uri = new URL(value); - switch (uri.protocol) { - // Extract from NIP-19 URI, eg `nostr:npub1q3sle0kvfsehgsuexttt3ugjd8xdklxfwwkh559wxckmzddywnws6cd26p`. - case 'nostr:': - bech32 = uri.pathname; - break; - // Extract from URL, eg `https://njump.me/npub1q3sle0kvfsehgsuexttt3ugjd8xdklxfwwkh559wxckmzddywnws6cd26p`. - case 'http:': - case 'https:': - bech32 = uri.pathname.slice(1); - break; - } - } catch { - // do nothing - } - - if (n.bech32().safeParse(bech32).success) { - return bech32; - } -} - export { searchController }; diff --git a/src/utils.ts b/src/utils.ts index e9213ed1..5abc2360 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,6 +24,32 @@ function bech32ToPubkey(bech32: string): string | undefined { } } +/** Extract a bech32 ID out of a search query string. */ +function extractBech32(value: string): string | undefined { + let bech32: string = value; + + try { + const uri = new URL(value); + switch (uri.protocol) { + // Extract from NIP-19 URI, eg `nostr:npub1q3sle0kvfsehgsuexttt3ugjd8xdklxfwwkh559wxckmzddywnws6cd26p`. + case 'nostr:': + bech32 = uri.pathname; + break; + // Extract from URL, eg `https://njump.me/npub1q3sle0kvfsehgsuexttt3ugjd8xdklxfwwkh559wxckmzddywnws6cd26p`. + case 'http:': + case 'https:': + bech32 = uri.pathname.slice(1); + break; + } + } catch { + // do nothing + } + + if (n.bech32().safeParse(bech32).success) { + return bech32; + } +} + interface Nip05 { /** Localpart of the nip05, eg `alex` in `alex@alexgleason.me`. */ local: string | undefined; @@ -97,6 +123,7 @@ export { bech32ToPubkey, dedupeEvents, eventAge, + extractBech32, findTag, isNostrId, isURL,