import type { NDKEvent } from "@nostr-dev-kit/ndk"; import { nip19 } from "nostr-tools"; import { getMatchingTags } from "./utils/nostrUtils.ts"; export function neventEncode(event: NDKEvent, relays: string[]) { return nip19.neventEncode({ id: event.id, kind: event.kind, relays, author: event.pubkey, }); } export function naddrEncode(event: NDKEvent, relays: string[]) { const dTag = getMatchingTags(event, "d")[0]?.[1]; if (!dTag) { throw new Error("Event does not have a d tag"); } return nip19.naddrEncode({ identifier: dTag, pubkey: event.pubkey, kind: event.kind || 0, relays, }); } export function nprofileEncode(pubkey: string, relays: string[]) { return nip19.nprofileEncode({ pubkey, relays }); } export function formatDate(unixtimestamp: number) { const months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; const date = new Date(unixtimestamp * 1000); const day = date.getDate(); const month = months[date.getMonth()]; const year = date.getFullYear(); const formattedDate = `${day} ${month} ${year}`; return formattedDate; } let serial = 0; export function next(): number { serial++; return serial; } export function scrollTabIntoView(el: string | HTMLElement, wait: boolean) { function scrollTab() { const element = typeof el === "string" ? document.querySelector(`[id^="wikitab-v0-${el}"]`) : el; if (!element) return; element.scrollIntoView({ behavior: "smooth", inline: "start", }); } if (wait) { setTimeout(() => { scrollTab(); }, 1); } else { scrollTab(); } } export function isElementInViewport(el: string | HTMLElement) { const element = typeof el === "string" ? document.querySelector(`[id^="wikitab-v0-${el}"]`) : el; if (!element) return; const rect = element.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (globalThis.innerHeight || document.documentElement.clientHeight) && rect.right <= (globalThis.innerWidth || document.documentElement.clientWidth) ); } /** * Removes `kind: 30040` index events that don't comply with the NKBIP-01 specification. * @param events A set of events. * @returns The filtered set of events. */ export function filterValidIndexEvents(events: Set): Set { // The filter object supports only limited parameters, so we need to filter out events that // don't respect NKBIP-01. events.forEach((event) => { // Index events have no content, and they must have `title`, `d`, and `e` tags. if ( (event.content != null && event.content.length > 0) || getMatchingTags(event, "title").length === 0 || getMatchingTags(event, "d").length === 0 || (getMatchingTags(event, "a").length === 0 && getMatchingTags(event, "e").length === 0) ) { events.delete(event); } }); console.debug(`Filtered index events: ${events.size} events remaining.`); return events; } /** * Async version of Array.findIndex() that runs sequentially. * Returns the index of the first element that satisfies the provided testing function. * @param array The array to search * @param predicate The async testing function * @returns A promise that resolves to the index of the first matching element, or -1 if none found */ export async function findIndexAsync( array: T[], predicate: (element: T, index: number, array: T[]) => Promise, ): Promise { for (let i = 0; i < array.length; i++) { if (await predicate(array[i], i, array)) { return i; } } return -1; } // Extend Array prototype with findIndexAsync declare global { interface Array { findIndexAsync( predicate: (element: T, index: number, array: T[]) => Promise, ): Promise; } } Array.prototype.findIndexAsync = function ( this: T[], predicate: (element: T, index: number, array: T[]) => Promise, ): Promise { return findIndexAsync(this, predicate); }; /** * Creates a debounced function that delays invoking func until after wait milliseconds have elapsed * since the last time the debounced function was invoked. * @param func The function to debounce * @param wait The number of milliseconds to delay * @returns A debounced version of the function */ export function debounce unknown>( func: T, wait: number, ): (...args: Parameters) => void { let timeout: ReturnType | undefined; return function executedFunction(...args: Parameters) { const later = () => { timeout = undefined; func(...args); }; if (timeout) { clearTimeout(timeout); } timeout = setTimeout(later, wait); }; }