your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { Alert, Button, Input, Modal, Subheading } from '@foxui/core';
3 import type { CreationModalComponentProps } from '../types';
4
5 const EVENT_COLLECTION = 'community.lexicon.calendar.event';
6
7 let { item = $bindable(), oncreate, oncancel }: CreationModalComponentProps = $props();
8
9 let isValidating = $state(false);
10 let errorMessage = $state('');
11 let eventUrl = $state('');
12
13 function parseEventUrl(url: string): { did: string; rkey: string } | null {
14 // Match smokesignal.events URLs: https://smokesignal.events/{did}/{rkey}
15 const smokesignalMatch = url.match(/^https?:\/\/smokesignal\.events\/(did:[^/]+)\/([^/?#]+)/);
16 if (smokesignalMatch) {
17 return { did: smokesignalMatch[1], rkey: smokesignalMatch[2] };
18 }
19
20 // Match AT URIs: at://{did}/community.lexicon.calendar.event/{rkey}
21 const atUriMatch = url.match(/^at:\/\/(did:[^/]+)\/([^/]+)\/([^/?#]+)/);
22 if (atUriMatch && atUriMatch[2] === EVENT_COLLECTION) {
23 return { did: atUriMatch[1], rkey: atUriMatch[3] };
24 }
25
26 return null;
27 }
28
29 async function validateAndCreate() {
30 errorMessage = '';
31 isValidating = true;
32
33 try {
34 const parsed = parseEventUrl(eventUrl.trim());
35
36 if (!parsed) {
37 throw new Error('Invalid URL format');
38 }
39
40 // Validate the event exists by fetching it
41 const response = await fetch(
42 `https://smokesignal.events/xrpc/community.lexicon.calendar.GetEvent?repository=${encodeURIComponent(parsed.did)}&record_key=${encodeURIComponent(parsed.rkey)}`
43 );
44
45 if (!response.ok) {
46 throw new Error('Event not found');
47 }
48
49 // Store as AT URI
50 item.cardData.uri = `at://${parsed.did}/${EVENT_COLLECTION}/${parsed.rkey}`;
51
52 return true;
53 } catch (err) {
54 errorMessage =
55 err instanceof Error && err.message === 'Event not found'
56 ? "Couldn't find that event. Please check the URL and try again."
57 : 'Invalid URL. Please enter a valid smokesignal.events URL or AT URI.';
58 return false;
59 } finally {
60 isValidating = false;
61 }
62 }
63</script>
64
65<Modal open={true} closeButton={false}>
66 <form
67 onsubmit={async () => {
68 if (await validateAndCreate()) oncreate();
69 }}
70 class="flex flex-col gap-2"
71 >
72 <Subheading>Enter a Smoke Signal event URL</Subheading>
73 <Input
74 bind:value={eventUrl}
75 placeholder="https://smokesignal.events/did:.../..."
76 class="mt-4"
77 />
78
79 {#if errorMessage}
80 <Alert type="error" title="Failed to create event card"><span>{errorMessage}</span></Alert>
81 {/if}
82
83 <p class="text-base-500 dark:text-base-400 mt-2 text-xs">
84 Paste a URL from <a
85 href="https://smokesignal.events"
86 class="text-accent-800 dark:text-accent-300"
87 target="_blank">smokesignal.events</a
88 > or an AT URI for a calendar event.
89 </p>
90
91 <div class="mt-4 flex justify-end gap-2">
92 <Button onclick={oncancel} variant="ghost">Cancel</Button>
93 <Button type="submit" disabled={isValidating || !eventUrl.trim()}
94 >{isValidating ? 'Creating...' : 'Create'}</Button
95 >
96 </div>
97 </form>
98</Modal>