your personal website on atproto - mirror
blento.app
1<script lang="ts">
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
6 const EVENT_COLLECTION = 'community.lexicon.calendar.event';
7
8 let { item = $bindable(), oncreate, oncancel }: CreationModalComponentProps = $props();
9
10 let isValidating = $state(false);
11 let errorMessage = $state('');
12 let eventUrl = $state('');
13
14 function parseEventUrl(url: string): { did: string; rkey: string } | null {
15 // Match smokesignal.events URLs: https://smokesignal.events/{did}/{rkey}
16 const smokesignalMatch = url.match(/^https?:\/\/smokesignal\.events\/(did:[^/]+)\/([^/?#]+)/);
17 if (smokesignalMatch) {
18 return { did: smokesignalMatch[1], rkey: smokesignalMatch[2] };
19 }
20
21 // Match AT URIs: at://{did}/community.lexicon.calendar.event/{rkey}
22 const atUriMatch = url.match(/^at:\/\/(did:[^/]+)\/([^/]+)\/([^/?#]+)/);
23 if (atUriMatch && atUriMatch[2] === EVENT_COLLECTION) {
24 return { did: atUriMatch[1], rkey: atUriMatch[3] };
25 }
26
27 return null;
28 }
29
30 async function validateAndCreate() {
31 errorMessage = '';
32 isValidating = true;
33
34 try {
35 const parsed = parseEventUrl(eventUrl.trim());
36
37 if (!parsed) {
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 );
45
46 if (!response.ok) {
47 throw new Error('Event not found');
48 }
49
50 // Store as AT URI
51 item.cardData.uri = `at://${parsed.did}/${EVENT_COLLECTION}/${parsed.rkey}`;
52
53 return true;
54 } catch (err) {
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;
62 }
63 }
64</script>
65
66<Modal open={true} closeButton={false}>
67 <form
68 onsubmit={async () => {
69 if (await validateAndCreate()) oncreate();
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
80 {#if errorMessage}
81 <Alert type="error" title="Failed to create event card"><span>{errorMessage}</span></Alert>
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">
93 <Button onclick={oncancel} variant="ghost">Cancel</Button>
94 <Button type="submit" disabled={isValidating || !eventUrl.trim()}
95 >{isValidating ? 'Creating...' : 'Create'}</Button
96 >
97 </div>
98 </form>
99</Modal>