diff --git a/docs/Autofill.md b/docs/Autofill.md
--- a/docs/Autofill.md
+++ b/docs/Autofill.md
@@ -11,7 +11,7 @@
- blue.flashes.actor.portfolio
- social.grain.gallery
- xyz.statusphere.status
- - site.standard.publication
+ - site.standard.publication
- fm.teal.alpha.feed.play
- dev.npmx.feed.like
- add bluesky profile card
diff --git a/src/lib/atproto/methods.ts b/src/lib/atproto/methods.ts
--- a/src/lib/atproto/methods.ts
+++ b/src/lib/atproto/methods.ts
@@ -395,7 +395,8 @@
*/
export function getCDNImageBlobUrl({
did,
- blob
+ blob,
+ type = 'webp'
}: {
did?: string;
blob: {
@@ -404,11 +405,12 @@
$link: string;
};
};
+ type?: 'webp' | 'jpeg';
}) {
if (!blob || !did) return;
did ??= user.did;
- return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${blob.ref.$link}@webp`;
+ return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${blob.ref.$link}@${type}`;
}
/**
diff --git a/src/routes/[[actor=actor]]/e/+page.server.ts b/src/routes/[[actor=actor]]/e/+page.server.ts
new file mode 100644
--- /dev/null
+++ b/src/routes/[[actor=actor]]/e/+page.server.ts
@@ -0,0 +1,77 @@
+import { error } from '@sveltejs/kit';
+import type { EventData } from '$lib/cards/social/EventCard';
+import { getBlentoOrBskyProfile, resolveHandle } from '$lib/atproto/methods.js';
+import { isHandle } from '@atcute/lexicons/syntax';
+import { createCache, type CachedProfile } from '$lib/cache';
+import type { ActorIdentifier, Did } from '@atcute/lexicons';
+import { env as publicEnv } from '$env/dynamic/public';
+
+export async function load({ params, platform, request }) {
+ const cache = createCache(platform);
+
+ const customDomain = request.headers.get('X-Custom-Domain')?.toLowerCase();
+
+ let actor: ActorIdentifier | undefined = params.actor;
+
+ if (!actor) {
+ const kv = platform?.env?.CUSTOM_DOMAINS;
+
+ if (kv && customDomain) {
+ try {
+ const did = await kv.get(customDomain);
+
+ if (did) actor = did as ActorIdentifier;
+ } catch (error) {
+ console.error('failed to get custom domain kv', error);
+ }
+ } else {
+ actor = publicEnv.PUBLIC_HANDLE as ActorIdentifier;
+ }
+ } else if (customDomain && params.actor) {
+ actor = undefined;
+ }
+
+ const did = isHandle(actor) ? await resolveHandle({ handle: actor }) : actor;
+
+ if (!did) {
+ throw error(404, 'Events not found');
+ }
+
+ try {
+ const [eventsResponse, hostProfile] = await Promise.all([
+ fetch(
+ `https://smokesignal.events/xrpc/community.lexicon.calendar.searchEvents?repository=${encodeURIComponent(did)}&query=upcoming`
+ ),
+ cache
+ ? cache.getProfile(did as Did).catch(() => null)
+ : getBlentoOrBskyProfile({ did: did as Did })
+ .then(
+ (p): CachedProfile => ({
+ did: p.did as string,
+ handle: p.handle as string,
+ displayName: p.displayName as string | undefined,
+ avatar: p.avatar as string | undefined,
+ hasBlento: p.hasBlento,
+ url: p.url
+ })
+ )
+ .catch(() => null)
+ ]);
+
+ if (!eventsResponse.ok) {
+ throw error(404, 'Events not found');
+ }
+
+ const data: { results: EventData[] } = await eventsResponse.json();
+ const events = data.results;
+
+ return {
+ events,
+ did,
+ hostProfile: hostProfile ?? null
+ };
+ } catch (e) {
+ if (e && typeof e === 'object' && 'status' in e) throw e;
+ throw error(404, 'Events not found');
+ }
+}
diff --git a/src/routes/[[actor=actor]]/e/+page.svelte b/src/routes/[[actor=actor]]/e/+page.svelte
new file mode 100644
--- /dev/null
+++ b/src/routes/[[actor=actor]]/e/+page.svelte
@@ -0,0 +1,170 @@
+
+
+
Hosted by {hostName}
+No events found.
+ {:else} ++ {formatDate(event.startsAt)} · {formatTime(event.startsAt)} +
+ ++ {event.countGoing} going +
+ {/if} +