atmo.rsvp
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

improve embed

Florian 59bb9a53 6f46e794

+48 -33
+1 -1
src/lib/atproto/port.ts
··· 1 1 // Dev server port — generated by setup-dev, shared by vite, oauth, and tunnel. 2 2 // Each project gets a unique random port (5200–7200) so multiple projects can run simultaneously. 3 - export const DEV_PORT = 5183; 3 + export const DEV_PORT = 5454;
+47 -32
src/routes/embed/p/[actor]/e/[rkey]/+page.svelte
··· 1 1 <script lang="ts"> 2 - import { onMount, untrack } from 'svelte'; 2 + import { untrack } from 'svelte'; 3 3 import { resolve } from '$app/paths'; 4 4 import Avatar from 'svelte-boring-avatars'; 5 + import { notifyContrailOfUpdate } from '$lib/contrail'; 5 6 6 7 interface AtmoEmbedSDK { 7 8 getParams(): { base: string; accent: string; dark: boolean; did: string | null }; ··· 59 60 startDate.getDate() === endDate.getDate() 60 61 ); 61 62 62 - onMount(() => { 63 - if (window.AtmoEmbed) { 64 - const { base, accent, dark } = window.AtmoEmbed.getParams(); 65 - const html = document.documentElement; 66 - if (base) html.classList.add(base); 67 - if (accent) html.classList.add(accent); 68 - if (dark) html.classList.add('dark'); 69 - } 70 - }); 71 63 72 64 async function submitRsvp(status: 'going' | 'interested') { 73 65 if (!window.AtmoEmbed || !data.viewerDid) return; ··· 90 82 if (result?.uri) { 91 83 const parts = result.uri.split('/'); 92 84 rsvpRkey = parts[parts.length - 1]; 85 + notifyContrailOfUpdate(result.uri); 93 86 } 94 87 } catch (e) { 95 88 console.error('RSVP failed:', e); ··· 99 92 } 100 93 101 94 async function cancelRsvp() { 102 - if (!window.AtmoEmbed || !rsvpRkey) return; 95 + if (!window.AtmoEmbed || !rsvpRkey || !data.viewerDid) return; 103 96 submitting = true; 97 + const rsvpUri = `at://${data.viewerDid}/community.lexicon.calendar.rsvp/${rsvpRkey}`; 104 98 try { 105 99 await window.AtmoEmbed.deleteRecord({ 106 100 collection: 'community.lexicon.calendar.rsvp', 107 101 rkey: rsvpRkey 108 102 }); 103 + notifyContrailOfUpdate(rsvpUri); 109 104 rsvpStatus = null; 110 105 rsvpRkey = null; 111 106 } catch (e) { ··· 117 112 </script> 118 113 119 114 <svelte:head> 115 + <!-- Apply theme classes before paint to avoid flash --> 116 + <script> 117 + var p = new URLSearchParams(location.search); 118 + var h = document.documentElement; 119 + var b = p.get('base'); if (b) h.classList.add(b); 120 + var a = p.get('accent'); if (a) h.classList.add(a); 121 + if (p.get('dark') === '1') h.classList.add('dark'); 122 + </script> 120 123 <script src="https://atmo.social/embed-sdk.js"></script> 121 124 </svelte:head> 122 125 123 - <div class="@container bg-base-50 dark:bg-base-900 text-base-900 dark:text-base-50 flex h-full items-center gap-4 overflow-hidden p-3 @sm:gap-5 @sm:p-4 @lg:gap-8 @lg:p-6"> 126 + <div class="@container bg-base-200 dark:bg-base-950/50 text-base-900 dark:text-base-50 flex h-full items-center gap-4 overflow-hidden p-3 @sm:gap-5 @sm:p-4 @lg:gap-8 @lg:p-6"> 124 127 <!-- Thumbnail --> 125 128 <a href={eventUrl} target="_blank" rel="noopener noreferrer" class="shrink-0"> 126 129 <div class="aspect-square h-[calc(100cqh-1.5rem)] max-h-40 overflow-hidden rounded-xl @sm:h-[calc(100cqh-2rem)] @sm:rounded-2xl @lg:h-[calc(100cqh-3rem)] @lg:max-h-56"> ··· 162 165 </a> 163 166 164 167 <!-- RSVP --> 165 - <div class="mt-0.5 @sm:mt-1 @lg:mt-2"> 166 - {#if !data.viewerDid} 167 - <p class="text-base-400 dark:text-base-500 text-[11px] @sm:text-sm @lg:text-base">Log in to RSVP</p> 168 - {:else if rsvpStatus === 'going'} 169 - <div class="flex items-center justify-between rounded-md border border-green-200 bg-green-50 px-2 py-1 @sm:rounded-lg @sm:px-3 @sm:py-2 @lg:px-4 @lg:py-2.5 dark:border-green-900/50 dark:bg-green-900/20"> 170 - <div class="flex items-center gap-1 @sm:gap-2"> 171 - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3 @sm:size-4 @lg:size-5 text-green-600 dark:text-green-400"> 172 - <path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z" clip-rule="evenodd" /> 168 + {#if data.viewerDid} 169 + <div class="border-base-300 dark:border-base-800 bg-base-100 dark:bg-base-900/50 mt-2 flex flex-col justify-center rounded-lg border px-2 py-2 @sm:mt-3 @sm:rounded-xl @sm:px-3 @sm:py-3 @lg:mt-4 @lg:rounded-2xl @lg:px-4 @lg:py-4"> 170 + {#if rsvpStatus === 'going'} 171 + <div class="flex items-center justify-between"> 172 + <div class="flex items-center gap-1.5 @sm:gap-2.5 @lg:gap-3"> 173 + <div class="flex size-5 items-center justify-center rounded-full bg-green-100 @sm:size-6 @lg:size-8 dark:bg-green-900/30"> 174 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3 @sm:size-3.5 @lg:size-4 text-green-600 dark:text-green-400"> 175 + <path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 0 1 1.05-.143Z" clip-rule="evenodd" /> 176 + </svg> 177 + </div> 178 + <span class="text-base-900 dark:text-base-50 text-[11px] font-semibold @sm:text-sm @lg:text-base">You're Going</span> 179 + </div> 180 + <button onclick={cancelRsvp} disabled={submitting} class="text-base-400 hover:text-base-600 dark:text-base-500 dark:hover:text-base-300 cursor-pointer transition-colors"> 181 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3.5 @sm:size-4 @lg:size-5"> 182 + <path d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z" /> 173 183 </svg> 174 - <span class="text-[11px] @sm:text-sm @lg:text-base font-semibold text-green-700 dark:text-green-300">You're Going!</span> 175 - </div> 176 - <button onclick={cancelRsvp} disabled={submitting} class="cursor-pointer text-[11px] @sm:text-sm @lg:text-base text-green-600/60 transition-colors hover:text-green-700 dark:text-green-400/60 dark:hover:text-green-300">Remove</button> 184 + </button> 177 185 </div> 178 186 {:else if rsvpStatus === 'interested'} 179 - <div class="flex items-center justify-between rounded-md border border-amber-200 bg-amber-50 px-2 py-1 @sm:rounded-lg @sm:px-3 @sm:py-2 @lg:px-4 @lg:py-2.5 dark:border-amber-900/50 dark:bg-amber-900/20"> 180 - <div class="flex items-center gap-1 @sm:gap-2"> 181 - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3 @sm:size-4 @lg:size-5 text-amber-600 dark:text-amber-400"> 182 - <path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" /> 187 + <div class="flex items-center justify-between"> 188 + <div class="flex items-center gap-1.5 @sm:gap-2.5 @lg:gap-3"> 189 + <div class="flex size-5 items-center justify-center rounded-full bg-amber-100 @sm:size-6 @lg:size-8 dark:bg-amber-900/30"> 190 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3 @sm:size-3.5 @lg:size-4 text-amber-600 dark:text-amber-400"> 191 + <path fill-rule="evenodd" d="M10.868 2.884c-.321-.772-1.415-.772-1.736 0l-1.83 4.401-4.753.381c-.833.067-1.171 1.107-.536 1.651l3.62 3.102-1.106 4.637c-.194.813.691 1.456 1.405 1.02L10 15.591l4.069 2.485c.713.436 1.598-.207 1.404-1.02l-1.106-4.637 3.62-3.102c.635-.544.297-1.584-.536-1.65l-4.752-.382-1.831-4.401Z" clip-rule="evenodd" /> 192 + </svg> 193 + </div> 194 + <span class="text-base-900 dark:text-base-50 text-[11px] font-semibold @sm:text-sm @lg:text-base">You're Interested</span> 195 + </div> 196 + <button onclick={cancelRsvp} disabled={submitting} class="text-base-400 hover:text-base-600 dark:text-base-500 dark:hover:text-base-300 cursor-pointer transition-colors"> 197 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3.5 @sm:size-4 @lg:size-5"> 198 + <path d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z" /> 183 199 </svg> 184 - <span class="text-[11px] @sm:text-sm @lg:text-base font-semibold text-amber-700 dark:text-amber-300">You're Interested</span> 185 - </div> 186 - <button onclick={cancelRsvp} disabled={submitting} class="cursor-pointer text-[11px] @sm:text-sm @lg:text-base text-amber-600/60 transition-colors hover:text-amber-700 dark:text-amber-400/60 dark:hover:text-amber-300">Remove</button> 200 + </button> 187 201 </div> 188 202 {:else} 189 203 <div class="flex gap-1.5 @sm:gap-2 @lg:gap-3"> 190 204 <button 191 205 onclick={() => submitRsvp('going')} 192 206 disabled={submitting} 193 - class="bg-accent-600 hover:bg-accent-700 cursor-pointer rounded-md px-2.5 py-1 text-[11px] @sm:rounded-lg @sm:px-4 @sm:py-1.5 @sm:text-sm @lg:px-5 @lg:py-2 @lg:text-base font-medium text-white transition-colors disabled:opacity-50" 207 + class="bg-accent-600 hover:bg-accent-700 flex-1 cursor-pointer rounded-md px-2.5 py-1 text-[11px] font-medium text-white transition-colors disabled:opacity-50 @sm:rounded-lg @sm:px-4 @sm:py-1.5 @sm:text-sm @lg:px-5 @lg:py-2 @lg:text-base" 194 208 >Going</button> 195 209 <button 196 210 onclick={() => submitRsvp('interested')} 197 211 disabled={submitting} 198 - class="bg-base-200 dark:bg-base-800 hover:bg-base-300 dark:hover:bg-base-700 text-base-700 dark:text-base-300 cursor-pointer rounded-md px-2.5 py-1 text-[11px] @sm:rounded-lg @sm:px-4 @sm:py-1.5 @sm:text-sm @lg:px-5 @lg:py-2 @lg:text-base font-medium transition-colors disabled:opacity-50" 212 + class="bg-base-300 dark:bg-base-800 hover:bg-base-400 dark:hover:bg-base-700 text-base-700 dark:text-base-300 flex-1 cursor-pointer rounded-md px-2.5 py-1 text-[11px] font-medium transition-colors disabled:opacity-50 @sm:rounded-lg @sm:px-4 @sm:py-1.5 @sm:text-sm @lg:px-5 @lg:py-2 @lg:text-base" 199 213 >Interested</button> 200 214 </div> 201 215 {/if} 202 216 </div> 217 + {/if} 203 218 </div> 204 219 </div>