tangled
alpha
login
or
join now
flo-bit.dev
/
blento
21
fork
atom
your personal website on atproto - mirror
blento.app
21
fork
atom
overview
issues
pulls
pipelines
add subscribe button, show upcoming or past events
Florian
2 weeks ago
2d0f550d
6bf9d2b4
+55
-16
1 changed file
expand all
collapse all
unified
split
src
routes
[[actor=actor]]
events
+page.svelte
+55
-16
src/routes/[[actor=actor]]/events/+page.svelte
···
2
import type { EventData } from '$lib/cards/social/EventCard';
3
import { getCDNImageBlobUrl } from '$lib/atproto';
4
import { user } from '$lib/atproto/auth.svelte';
5
-
import { Avatar as FoxAvatar, Badge, Button } from '@foxui/core';
0
6
import Avatar from 'svelte-boring-avatars';
7
import * as TID from '@atcute/tid';
8
import { goto } from '$app/navigation';
···
78
}
79
80
let isOwner = $derived(user.isLoggedIn && user.did === did);
0
0
0
0
0
0
0
0
0
0
81
</script>
82
83
<svelte:head>
···
96
<div class="mb-8 flex items-start justify-between">
97
<div>
98
<h1 class="text-base-900 dark:text-base-50 mb-2 text-2xl font-bold sm:text-3xl">
99
-
Upcoming events
100
</h1>
101
<div class="mt-4 flex items-center gap-2">
102
<span class="text-base-500 dark:text-base-400 text-sm">Hosted by</span>
···
111
</a>
112
</div>
113
</div>
114
-
{#if isOwner}
0
0
0
0
0
0
0
0
0
0
0
0
0
115
<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
125
>
126
-
{/if}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
127
</div>
128
129
-
{#if events.length === 0}
130
-
<p class="text-base-500 dark:text-base-400 py-12 text-center">No events found.</p>
0
0
131
{:else}
132
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
133
-
{#each events as event (event.rkey)}
134
{@const thumbnail = getThumbnail(event)}
135
{@const location = getLocationString(event.locations)}
136
{@const rkey = event.rkey}
···
2
import type { EventData } from '$lib/cards/social/EventCard';
3
import { getCDNImageBlobUrl } from '$lib/atproto';
4
import { user } from '$lib/atproto/auth.svelte';
5
+
import { Avatar as FoxAvatar, Badge, Button, toast } from '@foxui/core';
6
+
import { page } from '$app/state';
7
import Avatar from 'svelte-boring-avatars';
8
import * as TID from '@atcute/tid';
9
import { goto } from '$app/navigation';
···
79
}
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
+
);
92
</script>
93
94
<svelte:head>
···
107
<div class="mb-8 flex items-start justify-between">
108
<div>
109
<h1 class="text-base-900 dark:text-base-50 mb-2 text-2xl font-bold sm:text-3xl">
110
+
{showPast ? 'Past' : 'Upcoming'} events
111
</h1>
112
<div class="mt-4 flex items-center gap-2">
113
<span class="text-base-500 dark:text-base-400 text-sm">Hosted by</span>
···
122
</a>
123
</div>
124
</div>
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}
139
<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
0
0
0
146
>
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
+
>
164
</div>
165
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>
170
{:else}
171
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
172
+
{#each filteredEvents as event (event.rkey)}
173
{@const thumbnail = getThumbnail(event)}
174
{@const location = getLocationString(event.locations)}
175
{@const rkey = event.rkey}