your personal website on atproto - mirror blento.app

small fixes, move rsvp info to constellation

Florian 2a33d4c2 d2ed12fc

+56 -61
+3 -3
src/routes/[[actor=actor]]/events/[rkey]/+page.svelte
··· 155 155 : null 156 156 ); 157 157 158 - let smokesignalUrl = $derived(`https://smokesignal.events/${did}/${rkey}`); 158 + // let smokesignalUrl = $derived(`https://smokesignal.events/${did}/${rkey}`); 159 159 let eventUri = $derived(`at://${did}/community.lexicon.calendar.event/${rkey}`); 160 160 161 161 let ogImageUrl = $derived(`${page.url.origin}${page.url.pathname}/og.png`); ··· 252 252 </div> 253 253 {/if} 254 254 255 - <!-- Date row (Luma-style calendar icon) --> 255 + <!-- Date row --> 256 256 <div class="mb-4 flex items-center gap-4"> 257 257 <div 258 - class="border-base-200 dark:border-base-700 flex size-12 shrink-0 flex-col items-center justify-center overflow-hidden rounded-xl border" 258 + class="border-base-200 dark:border-base-700 bg-base-100 dark:bg-base-950/30 flex size-12 shrink-0 flex-col items-center justify-center overflow-hidden rounded-xl border" 259 259 > 260 260 <span class="text-base-500 dark:text-base-400 text-[9px] leading-none font-semibold"> 261 261 {formatMonth(startDate)}
+8 -6
src/routes/[[actor=actor]]/events/[rkey]/EventAttendees.svelte
··· 83 83 {#if goingCount > 0} 84 84 <button 85 85 type="button" 86 - class="hover:bg-base-100 dark:hover:bg-base-800/50 -mx-2 cursor-pointer rounded-xl px-2 py-2 text-left transition-colors" 86 + class="hover:bg-base-100 dark:hover:bg-base-800/50 -mx-2 block w-full cursor-pointer rounded-xl px-2 py-2 text-left transition-colors" 87 87 onclick={() => openModal('going')} 88 88 > 89 89 <p class="text-base-900 dark:text-base-50 mb-2 text-sm"> ··· 124 124 {#if interestedCount > 0} 125 125 <button 126 126 type="button" 127 - class="hover:bg-base-100 dark:hover:bg-base-800/50 -mx-2 mt-4 cursor-pointer rounded-xl px-2 py-2 text-left transition-colors" 127 + class="hover:bg-base-100 dark:hover:bg-base-800/50 -mx-2 mt-4 block w-full cursor-pointer rounded-xl px-2 py-2 text-left transition-colors" 128 128 onclick={() => openModal('interested')} 129 129 > 130 130 <p class="text-base-900 dark:text-base-50 mb-2 text-sm"> ··· 164 164 </div> 165 165 {/if} 166 166 167 - <Modal bind:open={modalOpen} closeButton onOpenAutoFocus={(e) => e.preventDefault()}> 168 - <p class="text-base-900 dark:text-base-50 text-lg font-semibold"> 167 + <Modal bind:open={modalOpen} closeButton onOpenAutoFocus={(e) => e.preventDefault()} class="p-0"> 168 + <p class="text-base-900 dark:text-base-50 px-4 pt-4 text-lg font-semibold"> 169 169 {modalTitle} 170 170 <span class="text-base-500 dark:text-base-400 text-sm font-normal"> 171 171 ({modalAttendees.length}) 172 172 </span> 173 173 </p> 174 - <div class="mt-3 max-h-80 space-y-1 overflow-y-auto p-2"> 174 + <div 175 + class="dark:bg-base-900/50 bg-base-200/30 mx-4 mb-4 max-h-80 space-y-1 overflow-y-auto rounded-xl p-2" 176 + > 175 177 {#each modalAttendees as person (person.did)} 176 178 <a 177 179 href={person.url} 178 180 target={person.url?.startsWith('/') ? undefined : '_blank'} 179 181 rel={person.url?.startsWith('/') ? undefined : 'noopener noreferrer'} 180 - class="hover:bg-base-100 dark:hover:bg-base-800 flex items-center gap-3 rounded-xl px-2 py-2 transition-colors" 182 + class="hover:bg-base-200 dark:hover:bg-base-900 flex items-center gap-3 rounded-xl px-2 py-2 transition-colors" 181 183 > 182 184 <FoxAvatar 183 185 src={person.avatar}
+38 -48
src/routes/[[actor=actor]]/events/[rkey]/EventRsvp.svelte
··· 1 1 <script lang="ts"> 2 2 import { user } from '$lib/atproto/auth.svelte'; 3 + import { getRecord, putRecord, deleteRecord, createTID } from '$lib/atproto/methods'; 3 4 import { loginModalState } from '$lib/atproto/UI/LoginModal.svelte'; 4 5 import { Avatar, Button } from '@foxui/core'; 6 + import type { Did } from '@atcute/lexicons'; 5 7 6 8 let { 7 9 eventUri, ··· 30 32 31 33 rsvpLoading = true; 32 34 33 - fetch( 34 - `https://smokesignal.events/xrpc/community.lexicon.calendar.getRSVP?identity=${encodeURIComponent(userDid)}&event=${encodeURIComponent(eventUri)}` 35 - ) 35 + const url = `https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinks?subject=${encodeURIComponent(eventUri)}&source=community.lexicon.calendar.rsvp:subject.uri&did=${encodeURIComponent(userDid)}&limit=1`; 36 + 37 + fetch(url) 36 38 .then((res) => { 37 - if (!res.ok) { 38 - rsvpStatus = null; 39 - rsvpRkey = null; 40 - return; 41 - } 39 + if (!res.ok) throw new Error('Failed to fetch backlinks'); 42 40 return res.json(); 43 41 }) 44 - .then((data) => { 45 - if (!data?.record?.status) { 42 + .then(async (data) => { 43 + if (!data?.records?.length) { 46 44 rsvpStatus = null; 47 45 rsvpRkey = null; 48 46 return; 49 47 } 50 - if (data.uri) { 51 - const parts = data.uri.split('/'); 52 - rsvpRkey = parts[parts.length - 1]; 53 - } 54 - const status = data.record.status as string; 55 - if (status.includes('#going')) rsvpStatus = 'going'; 56 - else if (status.includes('#interested')) rsvpStatus = 'interested'; 57 - else if (status.includes('#notgoing')) rsvpStatus = 'notgoing'; 48 + 49 + const backlink = data.records[0]; 50 + rsvpRkey = backlink.rkey; 51 + 52 + const recordData = await getRecord({ 53 + did: backlink.did as Did, 54 + collection: backlink.collection, 55 + rkey: backlink.rkey 56 + }); 57 + 58 + const status = recordData?.value?.status as string; 59 + if (status?.includes('#going')) rsvpStatus = 'going'; 60 + else if (status?.includes('#interested')) rsvpStatus = 'interested'; 61 + else if (status?.includes('#notgoing')) rsvpStatus = 'notgoing'; 58 62 else rsvpStatus = null; 59 63 }) 60 64 .catch(() => { ··· 70 74 if (!user.client || !user.did) return; 71 75 rsvpSubmitting = true; 72 76 try { 73 - if (rsvpRkey) { 74 - await user.client.post('com.atproto.repo.deleteRecord', { 75 - input: { 76 - collection: 'community.lexicon.calendar.rsvp', 77 - repo: user.did, 78 - rkey: rsvpRkey 79 - } 80 - }); 81 - } 77 + const key = rsvpRkey ?? createTID(); 82 78 83 - const response = await user.client.post('com.atproto.repo.createRecord', { 84 - input: { 85 - collection: 'community.lexicon.calendar.rsvp', 86 - repo: user.did, 87 - record: { 88 - $type: 'community.lexicon.calendar.rsvp', 89 - status: `community.lexicon.calendar.rsvp#${status}`, 90 - subject: { 91 - uri: eventUri, 92 - ...(eventCid ? { cid: eventCid } : {}) 93 - }, 94 - createdAt: new Date().toISOString() 95 - } 79 + const response = await putRecord({ 80 + collection: 'community.lexicon.calendar.rsvp', 81 + rkey: key, 82 + record: { 83 + $type: 'community.lexicon.calendar.rsvp', 84 + status: `community.lexicon.calendar.rsvp#${status}`, 85 + subject: { 86 + uri: eventUri, 87 + ...(eventCid ? { cid: eventCid } : {}) 88 + }, 89 + createdAt: new Date().toISOString() 96 90 } 97 91 }); 98 92 99 93 if (response.ok) { 100 94 rsvpStatus = status; 101 - const parts = response.data.uri.split('/'); 102 - rsvpRkey = parts[parts.length - 1]; 95 + rsvpRkey = key; 103 96 onrsvp?.(status); 104 97 } 105 98 } catch (e) { ··· 113 106 if (!user.client || !user.did || !rsvpRkey) return; 114 107 rsvpSubmitting = true; 115 108 try { 116 - await user.client.post('com.atproto.repo.deleteRecord', { 117 - input: { 118 - collection: 'community.lexicon.calendar.rsvp', 119 - repo: user.did, 120 - rkey: rsvpRkey 121 - } 109 + await deleteRecord({ 110 + collection: 'community.lexicon.calendar.rsvp', 111 + rkey: rsvpRkey 122 112 }); 123 113 rsvpStatus = null; 124 114 rsvpRkey = null;
+7 -4
src/routes/[[actor=actor]]/events/[rkey]/api.remote.ts
··· 133 133 }; 134 134 } 135 135 136 + const uniqueGoing = [...new Set(going)]; 137 + const uniqueInterested = [...new Set(interested)]; 138 + 136 139 return { 137 - going: going.map((did) => toAttendeeInfo(did, 'going')), 138 - interested: interested.map((did) => toAttendeeInfo(did, 'interested')), 139 - goingCount: going.length, 140 - interestedCount: interested.length 140 + going: uniqueGoing.map((did) => toAttendeeInfo(did, 'going')), 141 + interested: uniqueInterested.map((did) => toAttendeeInfo(did, 'interested')), 142 + goingCount: uniqueGoing.length, 143 + interestedCount: uniqueInterested.length 141 144 }; 142 145 } 143 146 );