Something went wrong. Try again.
Central platform for European atproto.<cc> country community websites
Something went wrong. Try again.
4.8 kB · 117 lines
Astro
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118---import Base from '../../layouts/Base.astro';import CalendarEventCard from '../CalendarEventCard.astro';import LinkedEventCard from '../LinkedEventCard.astro';import PostCard from '../PostCard.astro';import { useTranslations, type Lang } from '../../i18n/ui';import { linkedEvents } from '../../data/linked-events';import { pastMeetupPosts } from '../../data/meetups';import { getCalendarEvents } from '../../lib/atproto/events';import { getScope } from '../../config/scopes';import { getPosts } from '../../lib/atproto/client';
import type { CalendarEvent } from '../../lib/atproto/events';import type { LinkedEvent } from '../../data/linked-events';
const { lang } = Astro.props as { lang: Lang };const t = useTranslations(lang);// Calendar events are country-tagged (see events-account.ts): a national site shows events tagged// with its country, the .eu aggregate shows all. Past meetups are NL photos, so they stay on .nl and// the aggregate only. Other national sites keep the generic organizer guides regardless.const scope = getScope(import.meta.env.PUBLIC_SITE_SCOPE);const cc = scope.aggregate || scope.countries === 'all' ? null : scope.countries[0];const allEvents = await getCalendarEvents();const events = scope.aggregate ? allEvents : allEvents.filter((e) => cc != null && e.countries.includes(cc));// Ecosystem link cards are country-tagged too: a national site shows only its own, the aggregate all.const scopedLinkedEvents = scope.aggregate ? linkedEvents : linkedEvents.filter((e) => cc != null && e.countries.includes(cc));const showMeetups = scope.aggregate || cc === 'nl';const meetups = showMeetups ? await getPosts(pastMeetupPosts) : [];
// Merge our own calendar events (full cards) with ecosystem events (stripped link cards)// into single Upcoming / Past lists, sorted chronologically.interface Row { sort: number; upcoming: boolean; cal?: CalendarEvent; linked?: LinkedEvent;}const rows: Row[] = [ ...events.map((e) => ({ sort: Date.parse(e.startsAt), cal: e, upcoming: e.upcoming })), ...scopedLinkedEvents.map((e) => ({ sort: Date.parse(e.startsAt), linked: e, upcoming: e.when === 'upcoming' })),];const upcomingRows = rows.filter((r) => r.upcoming).sort((a, b) => a.sort - b.sort);const pastRows = rows.filter((r) => !r.upcoming).sort((a, b) => b.sort - a.sort);---
<Base lang={lang} title={t('nav.events')}> <section class="hero"> <h1 class="display page-title">{t('nav.events')}</h1> <p>{t('events.subtitle')}</p> </section>
{ upcomingRows.length > 0 && ( <section class="section"> <h2>{t('events.heading')}</h2> <div class="events-grid"> {upcomingRows.map((r) => r.cal ? ( <CalendarEventCard event={r.cal} lang={lang} label={t('events.label.upcoming')} /> ) : ( <LinkedEventCard event={r.linked!} lang={lang} label={t('events.label.upcoming')} /> ), )} </div> </section> ) }
{ pastRows.length > 0 && ( <section class="section"> <h2>{t('events.past.events')}</h2> <div class="events-grid"> {pastRows.map((r) => r.cal ? ( <CalendarEventCard event={r.cal} lang={lang} label={t('events.label.past')} /> ) : ( <LinkedEventCard event={r.linked!} lang={lang} label={t('events.label.past')} /> ), )} </div> </section> ) }
{ meetups.length > 0 && ( <section class="section"> <h2>{t('events.past.h')}</h2> <div class="feed meetups-feed">{meetups.map((p) => <PostCard post={p} />)}</div> </section> ) }
{/* The organizer guides now live in the EU-wide handbook at docs.atproto.eu (one source for every country), so these cards deep-link out there rather than to local /events/* pages. */} <section class="section"> <h2>{t('events.guides.h')}</h2> <p class="events-note">{t('events.guides.note')}</p> <div class="guide-list"> <a class="guide-card" href="https://docs.atproto.eu/blueprints/hackathon/"> <span class="guide-title">{t('events.guides.hackathon')}</span> <span class="guide-desc">{t('events.guides.hackathon.d')}</span> </a> <a class="guide-card" href="https://docs.atproto.eu/blueprints/meetup/"> <span class="guide-title">{t('events.guides.meetup')}</span> <span class="guide-desc">{t('events.guides.meetup.d')}</span> </a> <a class="guide-card" href="https://docs.atproto.eu/blueprints/unconference/"> <span class="guide-title">{t('events.guides.unconference')}</span> <span class="guide-desc">{t('events.guides.unconference.d')}</span> </a> </div> </section></Base>