Resolve raw hex identifiers as event ID or pubkey in search and routing

Detect 64-char hex strings in the search bar and NIP19Page route.
Query relays to check if the hex is an event ID first, then fall back
to treating it as a pubkey if a kind-0 profile is found.
This commit is contained in:
Chad Curtis
2026-02-19 18:17:56 -06:00
parent 8fe07e65cc
commit d58f4bb632
2 changed files with 57 additions and 0 deletions
+10
View File
@@ -5,6 +5,11 @@ import { nip19 } from 'nostr-tools';
*/
const NIP19_PREFIXES = ['npub1', 'nprofile1', 'note1', 'nevent1', 'naddr1'];
/**
* Matches a 64-character lowercase hex string (a raw Nostr event ID or pubkey).
*/
const HEX_64_RE = /^[0-9a-f]{64}$/;
/**
* Checks whether a string looks like a NIP-05 identifier (user@domain.com)
* or a bare domain (e.g. fiatjaf.com). Strips a leading `@` if present.
@@ -46,6 +51,11 @@ export function getNostrIdentifierPath(input: string): string | null {
}
}
// Try raw 64-char hex (event ID or pubkey) — route handles it directly
if (HEX_64_RE.test(value)) {
return `/${value}`;
}
// Try NIP-05
if (looksLikeNip05(value)) {
// Strip leading @ for the URL path
+47
View File
@@ -1,10 +1,14 @@
import { nip19 } from 'nostr-tools';
import { useParams } from 'react-router-dom';
import { useNostr } from '@nostrify/react';
import { useQuery } from '@tanstack/react-query';
import NotFound from './NotFound';
import { ProfilePage } from './ProfilePage';
import { PostDetailPage, AddrPostDetailPage } from './PostDetailPage';
import type { AddressPointer } from 'nostr-tools/nip19';
const HEX_64_RE = /^[0-9a-f]{64}$/;
/**
* Returns true if the identifier looks like a NIP-05 username (contains @).
*/
@@ -12,12 +16,50 @@ function isNip05Like(id: string): boolean {
return id.includes('@');
}
/**
* Resolves a raw 64-char hex string to either an event ID or a pubkey.
* Tries to find an event with that ID first; if none is found, checks
* whether a kind-0 profile exists for it as a pubkey.
*/
function HexIdentifierPage({ hex }: { hex: string }) {
const { nostr } = useNostr();
const { data: resolved, isLoading } = useQuery({
queryKey: ['hex-resolve', hex],
queryFn: async () => {
// Try as event ID first
const events = await nostr.query([{ ids: [hex], limit: 1 }]);
if (events.length > 0) {
return 'event' as const;
}
// Try as pubkey (look for kind 0 profile)
const profiles = await nostr.query([{ kinds: [0], authors: [hex], limit: 1 }]);
if (profiles.length > 0) {
return 'pubkey' as const;
}
return 'event' as const; // default to event — PostDetailPage will handle "not found"
},
staleTime: Infinity,
});
if (isLoading || !resolved) {
return null;
}
if (resolved === 'pubkey') {
return <ProfilePage />;
}
return <PostDetailPage eventId={hex} />;
}
/**
* Universal route handler for `/:param`.
*
* Dispatches based on the shape of the identifier:
* - NIP-19 (`npub1...`, `note1...`, `nevent1...`, `naddr1...`, `nprofile1...`)
* - NIP-05 (`user@domain.com`) → profile
* - Raw 64-char hex → event ID or pubkey (resolved via relay query)
*/
export function NIP19Page() {
const { nip19: identifier } = useParams<{ nip19: string }>();
@@ -31,6 +73,11 @@ export function NIP19Page() {
return <ProfilePage />;
}
// Raw 64-char hex — could be event ID or pubkey, need to resolve
if (HEX_64_RE.test(identifier)) {
return <HexIdentifierPage hex={identifier} />;
}
// Try NIP-19 decoding
let decoded;
try {