You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
116 lines
3.9 KiB
116 lines
3.9 KiB
import { normalizeToDTag, parseAdvancedSearch } from '@/lib/search-parser' |
|
import type { TSearchParams } from '@/types' |
|
import { nip19 } from 'nostr-tools' |
|
|
|
export const ALEXANDRIA_NEXT_EVENTS_BASE = 'https://next-alexandria.gitcitadel.eu/events' |
|
|
|
const NIP05_STANDALONE = /^\s*[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\s*$/ |
|
|
|
/** `id` query for Alexandria: hex for note/nevent; full bech32 for naddr. */ |
|
function nip19ToAlexandriaIdValue(bech32Input: string): string | null { |
|
try { |
|
let id = bech32Input.trim() |
|
if (id.startsWith('nostr:')) id = id.slice(6) |
|
const decoded = nip19.decode(id) |
|
if (decoded.type === 'note' || decoded.type === 'nevent') { |
|
const d = decoded.data as string | { id: string } |
|
const hex = typeof d === 'string' ? d : d.id |
|
return hex ? hex.toLowerCase() : null |
|
} |
|
if (decoded.type === 'naddr') { |
|
return id |
|
} |
|
return null |
|
} catch { |
|
return null |
|
} |
|
} |
|
|
|
/** |
|
* Maps a free-form “notes” / NIP-50 style query to Alexandria `/events` query params |
|
* (`d`, `t`, `n`, `q`, `id`) per https://next-alexandria.gitcitadel.eu/events. |
|
*/ |
|
export function buildAlexandriaEventsSearchUrlFromNotesQuery(query: string): string | null { |
|
const q = query.trim() |
|
if (!q) return null |
|
|
|
const lower = q.toLowerCase() |
|
|
|
if (lower.startsWith('d:')) { |
|
const inner = q.slice(2).trim() |
|
const d = normalizeToDTag(inner) || inner.toLowerCase().replace(/\s+/g, '-').replace(/^-+|-+$/g, '') |
|
if (!d) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?d=${encodeURIComponent(d)}` |
|
} |
|
|
|
if (lower.startsWith('t:')) { |
|
const inner = q.slice(2).trim().toLowerCase() |
|
if (!inner) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?t=${encodeURIComponent(inner)}` |
|
} |
|
|
|
if (lower.startsWith('n:')) { |
|
const inner = q.slice(2).trim() |
|
if (!inner) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?n=${encodeURIComponent(inner)}` |
|
} |
|
|
|
if (q.startsWith('#')) { |
|
const inner = q.slice(1).trim().toLowerCase() |
|
if (!inner) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?t=${encodeURIComponent(inner)}` |
|
} |
|
|
|
if (/^[0-9a-f]{64}$/i.test(q)) { |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?id=${encodeURIComponent(q.toLowerCase())}` |
|
} |
|
|
|
const nip19Id = nip19ToAlexandriaIdValue(q) |
|
if (nip19Id) { |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?id=${encodeURIComponent(nip19Id)}` |
|
} |
|
|
|
if (NIP05_STANDALONE.test(q)) { |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?q=${encodeURIComponent(q.trim())}` |
|
} |
|
|
|
const adv = parseAdvancedSearch(q) |
|
if (adv.hashtag) { |
|
const raw = Array.isArray(adv.hashtag) ? adv.hashtag[0] : adv.hashtag |
|
const tag = raw?.toString().trim().toLowerCase() |
|
if (tag) return `${ALEXANDRIA_NEXT_EVENTS_BASE}?t=${encodeURIComponent(tag)}` |
|
} |
|
|
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?q=${encodeURIComponent(q)}` |
|
} |
|
|
|
export function buildAlexandriaEventsSearchUrlForTSearchParams(params: TSearchParams): string | null { |
|
if (params.type === 'hashtag') { |
|
const tag = params.search?.trim().toLowerCase() |
|
if (!tag) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?t=${encodeURIComponent(tag)}` |
|
} |
|
if (params.type === 'profiles') { |
|
let n = params.search.trim() |
|
if (n.toLowerCase().startsWith('n:')) n = n.slice(2).trim() |
|
if (!n) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?n=${encodeURIComponent(n)}` |
|
} |
|
if (params.type === 'notes') { |
|
return buildAlexandriaEventsSearchUrlFromNotesQuery(params.search) |
|
} |
|
return null |
|
} |
|
|
|
export function buildAlexandriaEventsUrlForHashtagParam(tag: string): string | null { |
|
const t = tag.trim().toLowerCase() |
|
if (!t) return null |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?t=${encodeURIComponent(t)}` |
|
} |
|
|
|
export function buildAlexandriaEventsUrlForDTagParam(d: string): string | null { |
|
const v = d.trim() |
|
if (!v) return null |
|
const normalized = normalizeToDTag(v) || v |
|
return `${ALEXANDRIA_NEXT_EVENTS_BASE}?d=${encodeURIComponent(normalized)}` |
|
}
|
|
|