your personal website on atproto - mirror
blento.app
1import { parseUri } from '$lib/atproto';
2import type { CardDefinition } from '../../types';
3import CreateEventCardModal from './CreateEventCardModal.svelte';
4import EventCard from './EventCard.svelte';
5
6const EVENT_COLLECTION = 'community.lexicon.calendar.event';
7
8export type EventData = {
9 mode: string;
10 name: string;
11 status: string;
12 startsAt: string;
13 endsAt?: string;
14 description?: string;
15 locations?: Array<{
16 $type: string;
17 address?: {
18 locality?: string;
19 region?: string;
20 country?: string;
21 };
22 }>;
23 media?: Array<{
24 alt?: string;
25 role?: string;
26 content?: {
27 $type: 'blob';
28 ref: {
29 $link: string;
30 };
31 mimeType?: string;
32 };
33 aspect_ratio?: {
34 width: number;
35 height: number;
36 };
37 }>;
38 uris?: Array<{
39 uri: string;
40 name?: string;
41 }>;
42 countGoing?: number;
43 countInterested?: number;
44 url: string;
45};
46
47export const EventCardDefinition = {
48 type: 'event',
49 contentComponent: EventCard,
50 creationModalComponent: CreateEventCardModal,
51 createNew: (card) => {
52 card.w = 4;
53 card.h = 4;
54 card.mobileW = 8;
55 card.mobileH = 6;
56 },
57
58 loadData: async (items) => {
59 const eventDataMap: Record<string, EventData> = {};
60
61 for (const item of items) {
62 const uri = item.cardData?.uri;
63 if (!uri) continue;
64
65 const parsedUri = parseUri(uri);
66 if (!parsedUri || !parsedUri.rkey || !parsedUri.repo) continue;
67
68 try {
69 const response = await fetch(
70 `https://smokesignal.events/xrpc/community.lexicon.calendar.GetEvent?repository=${encodeURIComponent(parsedUri.repo)}&record_key=${encodeURIComponent(parsedUri.rkey)}`
71 );
72
73 if (response.ok) {
74 const data = await response.json();
75 eventDataMap[item.id] = data as EventData;
76 }
77 } catch (error) {
78 console.error('Failed to fetch event data:', error);
79 }
80 }
81
82 return eventDataMap;
83 },
84
85 onUrlHandler: (url, item) => {
86 // Match smokesignal.events URLs: https://smokesignal.events/{did}/{rkey}
87 const smokesignalMatch = url.match(/^https?:\/\/smokesignal\.events\/(did:[^/]+)\/([^/?#]+)/);
88 if (smokesignalMatch) {
89 const [, did, rkey] = smokesignalMatch;
90 item.w = 4;
91 item.h = 4;
92 item.mobileW = 8;
93 item.mobileH = 6;
94 item.cardType = 'event';
95 item.cardData.uri = `at://${did}/${EVENT_COLLECTION}/${rkey}`;
96 return item;
97 }
98
99 // Match AT URIs: at://{did}/community.lexicon.calendar.event/{rkey}
100 const atUriMatch = url.match(/^at:\/\/(did:[^/]+)\/([^/]+)\/([^/?#]+)/);
101 if (atUriMatch) {
102 const [, did, collection, rkey] = atUriMatch;
103 if (collection === EVENT_COLLECTION) {
104 item.w = 4;
105 item.h = 4;
106 item.mobileW = 8;
107 item.mobileH = 6;
108 item.cardType = 'event';
109 item.cardData.uri = `at://${did}/${collection}/${rkey}`;
110 return item;
111 }
112 }
113
114 return null;
115 },
116
117 urlHandlerPriority: 5,
118
119 name: 'Event',
120
121 keywords: ['calendar', 'meetup', 'schedule', 'date', 'rsvp'],
122 groups: ['Social'],
123 icon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" /></svg>`
124} as CardDefinition & { type: 'event' };