tangled
alpha
login
or
join now
flo-bit.dev
/
blento
21
fork
atom
your personal website on atproto - mirror
blento.app
21
fork
atom
overview
issues
pulls
pipelines
event stuff
Florian
3 weeks ago
50224195
043df1e8
+66
-145
10 changed files
expand all
collapse all
unified
split
src
lib
cards
social
EventCard
CreateEventCardModal.svelte
EventCard.svelte
index.ts
routes
[[actor=actor]]
events
+page.server.ts
+page.svelte
[rkey]
+page.server.ts
+page.svelte
EventRsvp.svelte
og.png
+server.ts
EventOgImage.svelte
+13
-13
src/lib/cards/social/EventCard/CreateEventCardModal.svelte
···
2
import { Alert, Button, Input, Subheading } from '@foxui/core';
3
import Modal from '$lib/components/modal/Modal.svelte';
4
import type { CreationModalComponentProps } from '../../types';
0
0
5
6
const EVENT_COLLECTION = 'community.lexicon.calendar.event';
7
···
38
throw new Error('Invalid URL format');
39
}
40
41
-
// Validate the event exists by fetching it
42
-
const response = await fetch(
43
-
`https://smokesignal.events/xrpc/community.lexicon.calendar.GetEvent?repository=${encodeURIComponent(parsed.did)}&record_key=${encodeURIComponent(parsed.rkey)}`
44
-
);
0
0
45
46
-
if (!response.ok) {
47
throw new Error('Event not found');
48
}
49
···
55
errorMessage =
56
err instanceof Error && err.message === 'Event not found'
57
? "Couldn't find that event. Please check the URL and try again."
58
-
: 'Invalid URL. Please enter a valid smokesignal.events URL or AT URI.';
59
return false;
60
} finally {
61
isValidating = false;
···
70
}}
71
class="flex flex-col gap-2"
72
>
73
-
<Subheading>Enter a Smoke Signal event URL</Subheading>
74
<Input
75
bind:value={eventUrl}
76
-
placeholder="https://smokesignal.events/did:.../..."
77
class="mt-4"
78
/>
79
···
82
{/if}
83
84
<p class="text-base-500 dark:text-base-400 mt-2 text-xs">
85
-
Paste a URL from <a
86
-
href="https://smokesignal.events"
87
-
class="text-accent-800 dark:text-accent-300"
88
-
target="_blank">smokesignal.events</a
89
-
> or an AT URI for a calendar event.
90
</p>
91
92
<div class="mt-4 flex justify-end gap-2">
···
2
import { Alert, Button, Input, Subheading } from '@foxui/core';
3
import Modal from '$lib/components/modal/Modal.svelte';
4
import type { CreationModalComponentProps } from '../../types';
5
+
import { getRecord } from '$lib/atproto/methods';
6
+
import type { Did } from '@atcute/lexicons';
7
8
const EVENT_COLLECTION = 'community.lexicon.calendar.event';
9
···
40
throw new Error('Invalid URL format');
41
}
42
43
+
// Validate the event exists by fetching the record directly
44
+
const record = await getRecord({
45
+
did: parsed.did as Did,
46
+
collection: EVENT_COLLECTION,
47
+
rkey: parsed.rkey
48
+
});
49
50
+
if (!record?.value) {
51
throw new Error('Event not found');
52
}
53
···
59
errorMessage =
60
err instanceof Error && err.message === 'Event not found'
61
? "Couldn't find that event. Please check the URL and try again."
62
+
: 'Invalid URL. Please enter a valid event AT URI or smokesignal.events URL.';
63
return false;
64
} finally {
65
isValidating = false;
···
74
}}
75
class="flex flex-col gap-2"
76
>
77
+
<Subheading>Enter an event URL</Subheading>
78
<Input
79
bind:value={eventUrl}
80
+
placeholder="at://did:.../community.lexicon.calendar.event/..."
81
class="mt-4"
82
/>
83
···
86
{/if}
87
88
<p class="text-base-500 dark:text-base-400 mt-2 text-xs">
89
+
Paste an AT URI for a calendar event or a smokesignal.events URL.
0
0
0
0
90
</p>
91
92
<div class="mt-4 flex justify-end gap-2">
+4
-53
src/lib/cards/social/EventCard/EventCard.svelte
···
91
}
92
93
let eventUrl = $derived(() => {
94
-
if (eventData?.url) return eventData.url;
95
if (parsedUri) {
96
-
return `https://smokesignal.events/${parsedUri.repo}/${parsedUri.rkey}`;
97
}
98
return '#';
99
});
···
144
</div>
145
146
{#if isMobile() ? item.mobileW > 4 : item.w > 2}
147
-
<Button href={eventUrl()} target="_blank" rel="noopener noreferrer" class="z-50"
148
-
>View event</Button
149
-
>
150
{/if}
151
</div>
152
···
211
{eventData.description}
212
</p>
213
{/if}
214
-
215
-
{#if (eventData.countGoing !== undefined || eventData.countInterested !== undefined) && ((isMobile() && item.mobileH >= 4) || (!isMobile() && item.h >= 3))}
216
-
<div
217
-
class="text-base-600 dark:text-base-400 accent:text-base-800 flex flex-wrap gap-3 text-xs"
218
-
>
219
-
{#if eventData.countGoing !== undefined}
220
-
<div class="flex items-center gap-1">
221
-
<svg
222
-
xmlns="http://www.w3.org/2000/svg"
223
-
fill="none"
224
-
viewBox="0 0 24 24"
225
-
stroke-width="1.5"
226
-
stroke="currentColor"
227
-
class="size-4"
228
-
>
229
-
<path
230
-
stroke-linecap="round"
231
-
stroke-linejoin="round"
232
-
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
233
-
/>
234
-
</svg>
235
-
<span>{eventData.countGoing} going</span>
236
-
</div>
237
-
{/if}
238
-
{#if eventData.countInterested !== undefined}
239
-
<div class="flex items-center gap-1">
240
-
<svg
241
-
xmlns="http://www.w3.org/2000/svg"
242
-
fill="none"
243
-
viewBox="0 0 24 24"
244
-
stroke-width="1.5"
245
-
stroke="currentColor"
246
-
class="size-4"
247
-
>
248
-
<path
249
-
stroke-linecap="round"
250
-
stroke-linejoin="round"
251
-
d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z"
252
-
/>
253
-
</svg>
254
-
<span>{eventData.countInterested} interested</span>
255
-
</div>
256
-
{/if}
257
-
</div>
258
-
{/if}
259
</div>
260
261
{#if showImage}
···
267
268
<a
269
href={eventUrl()}
270
-
class="absolute inset-0 h-full w-full"
271
target="_blank"
272
-
rel="noopener noreferrer"
273
use:qrOverlay={{
274
context: {
275
title: eventData?.name ?? ''
276
}
277
}}
278
>
279
-
<span class="sr-only">View event on smokesignal.events</span>
280
</a>
281
{:else if isLoaded}
282
<div class="flex h-full w-full items-center justify-center">
···
91
}
92
93
let eventUrl = $derived(() => {
0
94
if (parsedUri) {
95
+
return `/${parsedUri.repo}/events/${parsedUri.rkey}`;
96
}
97
return '#';
98
});
···
143
</div>
144
145
{#if isMobile() ? item.mobileW > 4 : item.w > 2}
146
+
<Button href={eventUrl()} target="_blank" class="z-50">View event</Button>
0
0
147
{/if}
148
</div>
149
···
208
{eventData.description}
209
</p>
210
{/if}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
211
</div>
212
213
{#if showImage}
···
219
220
<a
221
href={eventUrl()}
0
222
target="_blank"
223
+
class="absolute inset-0 h-full w-full"
224
use:qrOverlay={{
225
context: {
226
title: eventData?.name ?? ''
227
}
228
}}
229
>
230
+
<span class="sr-only">View event</span>
231
</a>
232
{:else if isLoaded}
233
<div class="flex h-full w-full items-center justify-center">
+11
-11
src/lib/cards/social/EventCard/index.ts
···
1
-
import { parseUri } from '$lib/atproto';
2
import type { CardDefinition } from '../../types';
3
import CreateEventCardModal from './CreateEventCardModal.svelte';
4
import EventCard from './EventCard.svelte';
0
5
6
const EVENT_COLLECTION = 'community.lexicon.calendar.event';
7
···
39
uri: string;
40
name?: string;
41
}>;
42
-
countGoing?: number;
43
-
countInterested?: number;
44
-
url: string;
45
};
46
47
export const EventCardDefinition = {
···
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
-
);
0
0
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);
···
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' };
···
1
+
import { parseUri, getRecord } from '$lib/atproto';
2
import type { CardDefinition } from '../../types';
3
import CreateEventCardModal from './CreateEventCardModal.svelte';
4
import EventCard from './EventCard.svelte';
5
+
import type { Did } from '@atcute/lexicons';
6
7
const EVENT_COLLECTION = 'community.lexicon.calendar.event';
8
···
40
uri: string;
41
name?: string;
42
}>;
43
+
url?: string;
0
0
44
};
45
46
export const EventCardDefinition = {
···
65
if (!parsedUri || !parsedUri.rkey || !parsedUri.repo) continue;
66
67
try {
68
+
const record = await getRecord({
69
+
did: parsedUri.repo as Did,
70
+
collection: EVENT_COLLECTION,
71
+
rkey: parsedUri.rkey
72
+
});
73
74
+
if (record?.value) {
75
+
eventDataMap[item.id] = record.value as EventData;
0
76
}
77
} catch (error) {
78
console.error('Failed to fetch event data:', error);
···
118
119
name: 'Event',
120
121
+
keywords: ['calendar', 'meetup', 'schedule', 'date', 'rsvp', 'smokesignal'],
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' };
+11
-11
src/routes/[[actor=actor]]/e/+page.server.ts
src/routes/[[actor=actor]]/events/+page.server.ts
···
1
import { error } from '@sveltejs/kit';
2
import type { EventData } from '$lib/cards/social/EventCard';
3
-
import { getBlentoOrBskyProfile } from '$lib/atproto/methods.js';
4
import { createCache, type CachedProfile } from '$lib/cache';
5
import type { Did } from '@atcute/lexicons';
6
import { getActor } from '$lib/actor.js';
···
15
}
16
17
try {
18
-
const [eventsResponse, hostProfile] = await Promise.all([
19
-
fetch(
20
-
`https://smokesignal.events/xrpc/community.lexicon.calendar.searchEvents?repository=${encodeURIComponent(did)}&query=upcoming`
21
-
),
0
0
22
cache
23
? cache.getProfile(did as Did).catch(() => null)
24
: getBlentoOrBskyProfile({ did: did as Did })
···
35
.catch(() => null)
36
]);
37
38
-
if (!eventsResponse.ok) {
39
-
throw error(404, 'Events not found');
40
-
}
41
-
42
-
const data: { results: EventData[] } = await eventsResponse.json();
43
-
const events = data.results;
44
45
return {
46
events,
···
1
import { error } from '@sveltejs/kit';
2
import type { EventData } from '$lib/cards/social/EventCard';
3
+
import { getBlentoOrBskyProfile, listRecords } from '$lib/atproto/methods.js';
4
import { createCache, type CachedProfile } from '$lib/cache';
5
import type { Did } from '@atcute/lexicons';
6
import { getActor } from '$lib/actor.js';
···
15
}
16
17
try {
18
+
const [records, hostProfile] = await Promise.all([
19
+
listRecords({
20
+
did: did as Did,
21
+
collection: 'community.lexicon.calendar.event',
22
+
limit: 100
23
+
}),
24
cache
25
? cache.getProfile(did as Did).catch(() => null)
26
: getBlentoOrBskyProfile({ did: did as Did })
···
37
.catch(() => null)
38
]);
39
40
+
const events = records.map((r) => ({
41
+
...(r.value as EventData),
42
+
rkey: r.uri.split('/').pop() as string
43
+
}));
0
0
44
45
return {
46
events,
+4
-14
src/routes/[[actor=actor]]/e/+page.svelte
src/routes/[[actor=actor]]/events/+page.svelte
···
6
7
let { data } = $props();
8
9
-
let events: EventData[] = $derived(data.events);
10
let did: string = $derived(data.did);
11
let hostProfile = $derived(data.hostProfile);
12
···
74
return { url, alt: media.alt || event.name };
75
}
76
77
-
function getRkey(event: EventData): string {
78
-
return event.url.split('/').pop() || '';
79
-
}
80
-
81
let actorPrefix = $derived(data.hostProfile?.handle ? `/${data.hostProfile.handle}` : `/${did}`);
82
</script>
83
···
98
<h1 class="text-base-900 dark:text-base-50 mb-2 text-2xl font-bold sm:text-3xl">
99
Upcoming events
100
</h1>
101
-
<div class="flex items-center gap-2 mt-4">
102
<span class="text-base-500 dark:text-base-400 text-sm">Hosted by</span>
103
<a
104
href={hostUrl}
···
116
<p class="text-base-500 dark:text-base-400 py-12 text-center">No events found.</p>
117
{:else}
118
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
119
-
{#each events as event (event.url)}
120
{@const thumbnail = getThumbnail(event)}
121
{@const location = getLocationString(event.locations)}
122
-
{@const rkey = getRkey(event)}
123
<a
124
href="{actorPrefix}/e/{rkey}"
125
class="border-base-200 dark:border-base-800 hover:border-base-300 dark:hover:border-base-700 group block overflow-hidden rounded-xl border transition-colors"
···
168
<span class="text-base-500 dark:text-base-400 truncate text-xs">{location}</span>
169
{/if}
170
</div>
171
-
172
-
{#if event.countGoing && event.countGoing > 0}
173
-
<p class="text-base-500 dark:text-base-400 mt-2 text-xs">
174
-
{event.countGoing} going
175
-
</p>
176
-
{/if}
177
</div>
178
</a>
179
{/each}
···
6
7
let { data } = $props();
8
9
+
let events: (EventData & { rkey: string })[] = $derived(data.events);
10
let did: string = $derived(data.did);
11
let hostProfile = $derived(data.hostProfile);
12
···
74
return { url, alt: media.alt || event.name };
75
}
76
0
0
0
0
77
let actorPrefix = $derived(data.hostProfile?.handle ? `/${data.hostProfile.handle}` : `/${did}`);
78
</script>
79
···
94
<h1 class="text-base-900 dark:text-base-50 mb-2 text-2xl font-bold sm:text-3xl">
95
Upcoming events
96
</h1>
97
+
<div class="mt-4 flex items-center gap-2">
98
<span class="text-base-500 dark:text-base-400 text-sm">Hosted by</span>
99
<a
100
href={hostUrl}
···
112
<p class="text-base-500 dark:text-base-400 py-12 text-center">No events found.</p>
113
{:else}
114
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
115
+
{#each events as event (event.rkey)}
116
{@const thumbnail = getThumbnail(event)}
117
{@const location = getLocationString(event.locations)}
118
+
{@const rkey = event.rkey}
119
<a
120
href="{actorPrefix}/e/{rkey}"
121
class="border-base-200 dark:border-base-800 hover:border-base-300 dark:hover:border-base-700 group block overflow-hidden rounded-xl border transition-colors"
···
164
<span class="text-base-500 dark:text-base-400 truncate text-xs">{location}</span>
165
{/if}
166
</div>
0
0
0
0
0
0
167
</div>
168
</a>
169
{/each}
+12
-17
src/routes/[[actor=actor]]/e/[rkey]/+page.server.ts
src/routes/[[actor=actor]]/events/[rkey]/+page.server.ts
···
1
import { error } from '@sveltejs/kit';
2
import type { EventData } from '$lib/cards/social/EventCard';
3
-
import { getBlentoOrBskyProfile, getRecord, resolveHandle } from '$lib/atproto/methods.js';
4
-
import { isHandle } from '@atcute/lexicons/syntax';
5
import { createCache, type CachedProfile } from '$lib/cache';
6
-
import type { ActorIdentifier, Did } from '@atcute/lexicons';
7
-
import { env as publicEnv } from '$env/dynamic/public';
8
import { getActor } from '$lib/actor';
9
10
export async function load({ params, platform, request }) {
···
19
}
20
21
try {
22
-
const [eventResponse, hostProfile, eventRecord] = await Promise.all([
23
-
fetch(
24
-
`https://smokesignal.events/xrpc/community.lexicon.calendar.GetEvent?repository=${encodeURIComponent(did)}&record_key=${encodeURIComponent(rkey)}`
25
-
),
0
0
26
cache
27
? cache.getProfile(did as Did).catch(() => null)
28
: getBlentoOrBskyProfile({ did: did as Did })
···
36
url: p.url
37
})
38
)
39
-
.catch(() => null),
40
-
getRecord({
41
-
did: did as Did,
42
-
collection: 'community.lexicon.calendar.event',
43
-
rkey
44
-
}).catch(() => null)
45
]);
46
47
-
if (!eventResponse.ok) {
48
throw error(404, 'Event not found');
49
}
50
51
-
const eventData: EventData = await eventResponse.json();
52
53
return {
54
eventData,
55
did,
56
rkey,
57
hostProfile: hostProfile ?? null,
58
-
eventCid: (eventRecord?.cid as string) ?? null
59
};
60
} catch (e) {
61
if (e && typeof e === 'object' && 'status' in e) throw e;
···
1
import { error } from '@sveltejs/kit';
2
import type { EventData } from '$lib/cards/social/EventCard';
3
+
import { getBlentoOrBskyProfile, getRecord } from '$lib/atproto/methods.js';
0
4
import { createCache, type CachedProfile } from '$lib/cache';
5
+
import type { Did } from '@atcute/lexicons';
0
6
import { getActor } from '$lib/actor';
7
8
export async function load({ params, platform, request }) {
···
17
}
18
19
try {
20
+
const [eventRecord, hostProfile] = await Promise.all([
21
+
getRecord({
22
+
did: did as Did,
23
+
collection: 'community.lexicon.calendar.event',
24
+
rkey
25
+
}),
26
cache
27
? cache.getProfile(did as Did).catch(() => null)
28
: getBlentoOrBskyProfile({ did: did as Did })
···
36
url: p.url
37
})
38
)
39
+
.catch(() => null)
0
0
0
0
0
40
]);
41
42
+
if (!eventRecord?.value) {
43
throw error(404, 'Event not found');
44
}
45
46
+
const eventData: EventData = eventRecord.value as EventData;
47
48
return {
49
eventData,
50
did,
51
rkey,
52
hostProfile: hostProfile ?? null,
53
+
eventCid: (eventRecord.cid as string) ?? null
54
};
55
} catch (e) {
56
if (e && typeof e === 'object' && 'status' in e) throw e;
+2
-16
src/routes/[[actor=actor]]/e/[rkey]/+page.svelte
src/routes/[[actor=actor]]/events/[rkey]/+page.svelte
···
88
return { url, alt: media.alt || eventData.name };
89
});
90
91
-
let eventUrl = $derived(eventData.url || `https://smokesignal.events/${did}/${rkey}`);
92
let eventUri = $derived(`at://${did}/community.lexicon.calendar.event/${rkey}`);
93
94
let ogImageUrl = $derived(`${page.url.origin}${page.url.pathname}/og.png`);
···
247
</a>
248
</div>
249
250
-
{#if (eventData.countGoing && eventData.countGoing > 0) || (eventData.countInterested && eventData.countInterested > 0)}
251
-
<!-- Counts -->
252
-
<div
253
-
class="text-base-900 dark:text-base-100 order-4 space-y-2.5 text-base font-medium md:order-0 md:col-start-1"
254
-
>
255
-
{#if eventData.countGoing && eventData.countGoing > 0}
256
-
<p>{eventData.countGoing} Going</p>
257
-
{/if}
258
-
{#if eventData.countInterested && eventData.countInterested > 0}
259
-
<p>{eventData.countInterested} Interested</p>
260
-
{/if}
261
-
</div>
262
-
{/if}
263
-
264
{#if eventData.uris && eventData.uris.length > 0}
265
<!-- Links -->
266
<div class="order-5 md:order-0 md:col-start-1">
···
300
301
<!-- View on Smoke Signal link -->
302
<a
303
-
href={eventUrl}
304
target="_blank"
305
rel="noopener noreferrer"
306
class="text-base-500 dark:text-base-400 hover:text-base-700 dark:hover:text-base-200 order-6 inline-flex items-center gap-1.5 text-sm transition-colors md:order-0 md:col-start-2"
···
88
return { url, alt: media.alt || eventData.name };
89
});
90
91
+
let smokesignalUrl = $derived(`https://smokesignal.events/${did}/${rkey}`);
92
let eventUri = $derived(`at://${did}/community.lexicon.calendar.event/${rkey}`);
93
94
let ogImageUrl = $derived(`${page.url.origin}${page.url.pathname}/og.png`);
···
247
</a>
248
</div>
249
0
0
0
0
0
0
0
0
0
0
0
0
0
0
250
{#if eventData.uris && eventData.uris.length > 0}
251
<!-- Links -->
252
<div class="order-5 md:order-0 md:col-start-1">
···
286
287
<!-- View on Smoke Signal link -->
288
<a
289
+
href={smokesignalUrl}
290
target="_blank"
291
rel="noopener noreferrer"
292
class="text-base-500 dark:text-base-400 hover:text-base-700 dark:hover:text-base-200 order-6 inline-flex items-center gap-1.5 text-sm transition-colors md:order-0 md:col-start-2"
src/routes/[[actor=actor]]/e/[rkey]/EventRsvp.svelte
src/routes/[[actor=actor]]/events/[rkey]/EventRsvp.svelte
+9
-9
src/routes/[[actor=actor]]/e/[rkey]/og.png/+server.ts
src/routes/[[actor=actor]]/events/[rkey]/og.png/+server.ts
···
1
-
import { getCDNImageBlobUrl, resolveHandle } from '$lib/atproto/methods.js';
2
-
import { env as publicEnv } from '$env/dynamic/public';
3
4
-
import type { ActorIdentifier } from '@atcute/lexicons';
5
-
import { isHandle } from '@atcute/lexicons/syntax';
6
import type { EventData } from '$lib/cards/social/EventCard';
7
import { ImageResponse } from '@ethercorps/sveltekit-og';
8
import { error } from '@sveltejs/kit';
···
29
let eventData: EventData;
30
31
try {
32
-
const eventResponse = await fetch(
33
-
`https://smokesignal.events/xrpc/community.lexicon.calendar.GetEvent?repository=${encodeURIComponent(did)}&record_key=${encodeURIComponent(rkey)}`
34
-
);
0
0
35
36
-
if (!eventResponse.ok) {
37
throw error(404, 'Event not found');
38
}
39
40
-
eventData = await eventResponse.json();
41
} catch (e) {
42
if (e && typeof e === 'object' && 'status' in e) throw e;
43
throw error(404, 'Event not found');
···
1
+
import { getCDNImageBlobUrl, getRecord } from '$lib/atproto/methods.js';
0
2
3
+
import type { Did } from '@atcute/lexicons';
0
4
import type { EventData } from '$lib/cards/social/EventCard';
5
import { ImageResponse } from '@ethercorps/sveltekit-og';
6
import { error } from '@sveltejs/kit';
···
27
let eventData: EventData;
28
29
try {
30
+
const eventRecord = await getRecord({
31
+
did: did as Did,
32
+
collection: 'community.lexicon.calendar.event',
33
+
rkey
34
+
});
35
36
+
if (!eventRecord?.value) {
37
throw error(404, 'Event not found');
38
}
39
40
+
eventData = eventRecord.value as EventData;
41
} catch (e) {
42
if (e && typeof e === 'object' && 'status' in e) throw e;
43
throw error(404, 'Event not found');
-1
src/routes/[[actor=actor]]/e/[rkey]/og.png/EventOgImage.svelte
src/routes/[[actor=actor]]/events/[rkey]/og.png/EventOgImage.svelte
···
58
</svg>
59
<span class="ml-3 text-2xl text-neutral-300">{dateStr}</span>
60
</div>
61
-
62
</div>
63
</div>
···
58
</svg>
59
<span class="ml-3 text-2xl text-neutral-300">{dateStr}</span>
60
</div>
0
61
</div>
62
</div>