Files
BelgianBitcoinEmbassy/frontend/lib/meetupsData.ts
T
bbeandCursor 2ef68222bf feat: resolve live Nostr references in blog posts and add embeds
Support naddr/nevent/note slugs for unindexed posts, cache naddr lookups,
render Nostr embeds in markdown, and add a consistency check for events mirrors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 03:57:32 +02:00

72 lines
2.2 KiB
TypeScript

/**
* Single source of truth for meetup data used by the public /events page, the
* /events.md mirror, and the /llms.txt summary line. Centralizing the fetch +
* upcoming/past partition guarantees those three can never disagree on the count.
*
* Fetched with `no-store` so every render reflects the live backend — this is
* what keeps crawlers (which don't run JS) and llms.txt consumers from seeing a
* stale or build-time-empty list.
*/
import { apiUrl } from "./api-base";
import { getMeetupStartUtc } from "./meetupEventTime";
export interface Meetup {
id: string;
title: string;
date: string;
time?: string;
location?: string;
description?: string;
status?: string;
organizer?: { name?: string; slug?: string } | null;
[key: string]: unknown;
}
/** Fetch all publicly-visible meetups from the backend, live (uncached). */
export async function fetchMeetupsLive(): Promise<Meetup[]> {
try {
const res = await fetch(apiUrl("/meetups"), { cache: "no-store" });
if (!res.ok) return [];
const data = await res.json();
return Array.isArray(data) ? (data as Meetup[]) : [];
} catch {
return [];
}
}
export interface PartitionedMeetups {
upcoming: Meetup[];
past: Meetup[];
}
/**
* Split meetups into upcoming (start >= now, soonest first) and past (start <
* now, most recent first). Meetups with an unparseable date are dropped.
*/
export function partitionMeetups(
meetups: Meetup[],
now: Date = new Date(),
): PartitionedMeetups {
const upcoming: Meetup[] = [];
const past: Meetup[] = [];
for (const m of meetups) {
const start = getMeetupStartUtc(m.date, m.time || "00:00");
if (Number.isNaN(start.getTime())) continue;
if (start >= now) upcoming.push(m);
else past.push(m);
}
const startMs = (m: Meetup) =>
getMeetupStartUtc(m.date, m.time || "00:00").getTime();
upcoming.sort((a, b) => startMs(a) - startMs(b));
past.sort((a, b) => startMs(b) - startMs(a));
return { upcoming, past };
}
/** Number of upcoming meetups — the single value llms.txt and events.md share. */
export function countUpcoming(meetups: Meetup[], now: Date = new Date()): number {
return partitionMeetups(meetups, now).upcoming.length;
}