your personal website on atproto - mirror blento.app

add subscribe button, show upcoming or past events

+55 -16
+55 -16
src/routes/[[actor=actor]]/events/+page.svelte
··· 2 2 import type { EventData } from '$lib/cards/social/EventCard'; 3 3 import { getCDNImageBlobUrl } from '$lib/atproto'; 4 4 import { user } from '$lib/atproto/auth.svelte'; 5 - import { Avatar as FoxAvatar, Badge, Button } from '@foxui/core'; 5 + import { Avatar as FoxAvatar, Badge, Button, toast } from '@foxui/core'; 6 + import { page } from '$app/state'; 6 7 import Avatar from 'svelte-boring-avatars'; 7 8 import * as TID from '@atcute/tid'; 8 9 import { goto } from '$app/navigation'; ··· 78 79 } 79 80 80 81 let isOwner = $derived(user.isLoggedIn && user.did === did); 82 + 83 + let showPast: boolean = $state(false); 84 + let now = $derived(new Date()); 85 + let filteredEvents = $derived( 86 + events.filter((e) => { 87 + const endOrStart = e.endsAt || e.startsAt; 88 + const eventDate = new Date(endOrStart); 89 + return showPast ? eventDate < now : eventDate >= now; 90 + }) 91 + ); 81 92 </script> 82 93 83 94 <svelte:head> ··· 96 107 <div class="mb-8 flex items-start justify-between"> 97 108 <div> 98 109 <h1 class="text-base-900 dark:text-base-50 mb-2 text-2xl font-bold sm:text-3xl"> 99 - Upcoming events 110 + {showPast ? 'Past' : 'Upcoming'} events 100 111 </h1> 101 112 <div class="mt-4 flex items-center gap-2"> 102 113 <span class="text-base-500 dark:text-base-400 text-sm">Hosted by</span> ··· 111 122 </a> 112 123 </div> 113 124 </div> 114 - {#if isOwner} 125 + <div class="flex flex-col items-end gap-2"> 126 + {#if isOwner} 127 + <Button 128 + variant="primary" 129 + onclick={() => { 130 + const rkey = TID.now(); 131 + const handle = 132 + user.profile?.handle && user.profile.handle !== 'handle.invalid' 133 + ? user.profile.handle 134 + : user.did; 135 + goto(`/${handle}/events/${rkey}/edit`); 136 + }}>New event</Button 137 + > 138 + {/if} 115 139 <Button 116 - variant="primary" 117 - onclick={() => { 118 - const rkey = TID.now(); 119 - const handle = 120 - user.profile?.handle && user.profile.handle !== 'handle.invalid' 121 - ? user.profile.handle 122 - : user.did; 123 - goto(`/${handle}/events/${rkey}/edit`); 124 - }}>New event</Button 140 + variant="secondary" 141 + onclick={async () => { 142 + const calendarUrl = `${page.url.origin}${page.url.pathname.replace(/\/$/, '')}/calendar`; 143 + await navigator.clipboard.writeText(calendarUrl); 144 + toast.success('Subscription link copied to clipboard'); 145 + }}>Subscribe</Button 125 146 > 126 - {/if} 147 + </div> 148 + </div> 149 + 150 + <!-- Toggle --> 151 + <div class="mb-6 flex gap-1"> 152 + <button 153 + class="rounded-xl px-3 py-1.5 text-sm font-medium transition-colors {!showPast 154 + ? 'bg-base-200 dark:bg-base-800 text-base-900 dark:text-base-50' 155 + : 'text-base-500 dark:text-base-400 hover:text-base-700 dark:hover:text-base-200 cursor-pointer'}" 156 + onclick={() => (showPast = false)}>Upcoming</button 157 + > 158 + <button 159 + class="rounded-xl px-3 py-1.5 text-sm font-medium transition-colors {showPast 160 + ? 'bg-base-200 dark:bg-base-800 text-base-900 dark:text-base-50' 161 + : 'text-base-500 dark:text-base-400 hover:text-base-700 dark:hover:text-base-200 cursor-pointer'}" 162 + onclick={() => (showPast = true)}>Past</button 163 + > 127 164 </div> 128 165 129 - {#if events.length === 0} 130 - <p class="text-base-500 dark:text-base-400 py-12 text-center">No events found.</p> 166 + {#if filteredEvents.length === 0} 167 + <p class="text-base-500 dark:text-base-400 py-12 text-center"> 168 + No {showPast ? 'past' : 'upcoming'} events. 169 + </p> 131 170 {:else} 132 171 <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"> 133 - {#each events as event (event.rkey)} 172 + {#each filteredEvents as event (event.rkey)} 134 173 {@const thumbnail = getThumbnail(event)} 135 174 {@const location = getLocationString(event.locations)} 136 175 {@const rkey = event.rkey}