atmo.rsvp
1<script lang="ts">
2 import { Modal, Button, Avatar } from '@foxui/core';
3 import { LinkCard } from '@foxui/social';
4 import PostToBlueskyModal from '$lib/components/PostToBlueskyModal.svelte';
5 import type { EditorAdapter, EditorViewer } from '$lib/components/editor/adapter';
6
7 let {
8 open = $bindable(false),
9 url,
10 title = 'Event created!',
11 shareText,
12 eventName,
13 ogImageUrl,
14 canSetEventComments = false,
15 eventDid,
16 eventRkey,
17 eventDescription,
18 adapter,
19 viewer,
20 onPosted
21 }: {
22 open: boolean;
23 url: string;
24 title?: string;
25 shareText?: string;
26 eventName?: string;
27 ogImageUrl?: string;
28 canSetEventComments?: boolean;
29 eventDid?: string;
30 eventRkey?: string;
31 eventDescription?: string;
32 adapter: EditorAdapter;
33 viewer: EditorViewer;
34 onPosted?: (ref: { uri: string; cid: string; showComments: boolean }) => void;
35 } = $props();
36
37 let copiedUrl = $state(false);
38 let copiedText = $state(false);
39 let showPostModal = $state(false);
40
41 let textBeforeUrl = $derived(shareText ? shareText.replace(url, '').trim() : undefined);
42
43 async function copyUrl() {
44 try {
45 await navigator.clipboard.writeText(url);
46 copiedUrl = true;
47 setTimeout(() => (copiedUrl = false), 2000);
48 } catch {}
49 }
50
51 async function copyText() {
52 if (!shareText) return;
53 try {
54 await navigator.clipboard.writeText(shareText);
55 copiedText = true;
56 setTimeout(() => (copiedText = false), 2000);
57 } catch {}
58 }
59
60 let blueskyButton: HTMLElement | null = $state(null);
61
62 let canPostDirectly = $derived(
63 !!eventDid && !!eventRkey && !!eventName && viewer.isLoggedIn
64 );
65
66 let blueskyIntentUrl = $derived(
67 `https://bsky.app/intent/compose?text=${encodeURIComponent(shareText || url)}`
68 );
69
70 function handleBlueskyClick() {
71 showPostModal = true;
72 }
73
74 function handlePostedFromInner(ref: { uri: string; cid: string; showComments: boolean }) {
75 onPosted?.(ref);
76 open = false;
77 }
78</script>
79
80<Modal
81 bind:open
82 onOpenAutoFocus={(e) => {
83 e.preventDefault();
84 blueskyButton?.focus();
85 }}
86>
87 <div>
88 <h2 class="text-base-900 dark:text-base-50 mb-2 text-xl font-bold">{title}</h2>
89 <p class="text-base-500 dark:text-base-400 mb-4 text-sm">Share it with others!</p>
90
91 <div
92 class="bg-base-200 dark:bg-base-950/30 border-base-400/40 dark:border-base-700 mb-6 overflow-hidden rounded-xl border px-4 py-3 text-left"
93 >
94 {#if viewer.isLoggedIn}
95 <div class="flex items-center gap-2 pb-4">
96 <Avatar src={viewer.avatar} alt="" class="size-6" />
97 <span class="text-base-700 dark:text-base-200 text-sm font-medium"
98 >{viewer.handle ?? viewer.did}</span
99 >
100 </div>
101 {/if}
102 {#if textBeforeUrl}
103 <p class="text-base-700 dark:text-base-200 text-md font-semibold">{textBeforeUrl}</p>
104 {/if}
105 {#if eventName}
106 <LinkCard
107 href={url}
108 meta={{
109 title: eventName,
110 image: ogImageUrl
111 }}
112 class="mb-1"
113 />
114 {/if}
115 </div>
116
117 <div class="flex flex-col gap-2 sm:flex-row">
118 <Button class="flex-1" variant="secondary" onclick={copyUrl}>
119 {copiedUrl ? 'Copied!' : 'Copy link'}
120 </Button>
121 {#if shareText}
122 <Button class="flex-1" variant="secondary" onclick={copyText}>
123 {copiedText ? 'Copied!' : 'Copy text'}
124 </Button>
125 {/if}
126 {#if canPostDirectly}
127 <Button bind:ref={blueskyButton} class="flex-1" onclick={handleBlueskyClick}>
128 Share to Bluesky
129 </Button>
130 {:else}
131 <Button
132 bind:ref={blueskyButton}
133 class="flex-1"
134 href={blueskyIntentUrl}
135 target="_blank"
136 rel="noopener"
137 >
138 Share to Bluesky
139 </Button>
140 {/if}
141 </div>
142 </div>
143</Modal>
144
145{#if canPostDirectly && eventDid && eventRkey && eventName}
146 <PostToBlueskyModal
147 bind:open={showPostModal}
148 {canSetEventComments}
149 {eventDid}
150 {eventRkey}
151 {eventName}
152 eventUrl={url}
153 {eventDescription}
154 {ogImageUrl}
155 initialText={textBeforeUrl ?? eventName}
156 {adapter}
157 {viewer}
158 onPosted={handlePostedFromInner}
159 />
160{/if}