+14
-19
src/App.svelte
+14
-19
src/App.svelte
···
10
10
let posts: Post[] = [];
11
11
let guestbookPosts: Post[] = [];
12
12
13
-
let hue: number = 1;
14
-
const cycleColors = async () => {
15
-
while (true) {
16
-
hue += 1;
17
-
if (hue > 360) {
18
-
hue = 0;
19
-
}
20
-
document.documentElement.style.setProperty("--primary-h", hue.toString());
21
-
await new Promise((resolve) => setTimeout(resolve, 10));
22
-
}
23
-
}
24
13
let clickCounter = 0;
25
-
const carameldansenfusion = async () => {
14
+
const birdbrain = async () => {
26
15
clickCounter++;
27
16
if (clickCounter >= 10) {
28
17
clickCounter = 0;
29
-
cycleColors();
18
+
document.getElementById('birdbrain').showPopover();
30
19
}
31
20
};
32
21
···
36
25
posts = initialPosts;
37
26
});
38
27
fetchGuestbookPosts().then((gbPosts) => {
39
-
guestbookPosts = gbPosts;
40
28
console.log(gbPosts);
29
+
guestbookPosts = gbPosts;
41
30
});
42
31
});
43
32
// Infinite loading function
···
85
74
█▓█▓▓█ ███▓█
86
75
████ ████
87
76
</pre>
88
-
<h1 onclick={carameldansenfusion} id="header">shimaenaga pds</h1>
77
+
<h1 onclick={birdbrain} id="header">shimaenaga pds</h1>
89
78
<p id="subtitle">a project of <a href="https://veryroundbird.house" target="_blank">veryroundbird.house</a></p>
90
79
{#if Config.SHOW_GUESTBOOK}
91
80
<div id="guestbook">
···
93
82
<div id="guestbookContents" popover>
94
83
<div id="signInfo">You can sign the guestbook <a href="{Config.GUESTBOOK_POST}" target="_blank">here</a>!</div>
95
84
<div id="guestbookPosts">
96
-
{#each guestbookPosts as postObject}
97
-
<div class="guestbookPost">
98
-
</div>
99
-
{/each}
85
+
{#if guestbookPosts.length > 0}
86
+
{#each guestbookPosts as postObject}
87
+
<PostComponent post={postObject as Post} />
88
+
{/each}
89
+
{:else}
90
+
<p class="noGuestbookPosts">No guestbook posts yet!</p>
91
+
{/if}
100
92
</div>
101
93
</div>
102
94
</div>
···
120
112
<InfiniteLoading on:infinite={onInfinite} distance={3000} />
121
113
</div>
122
114
</div>
115
+
<div id="birdbrain" popover>
116
+
<iframe width="560" height="315" src="https://www.youtube.com/embed/0iVlSNpq8i8?si=Ao4bcr_4HIBqsBy2" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
117
+
</div>
123
118
</main>
+1
-2
src/lib/AccountComponent.svelte
+1
-2
src/lib/AccountComponent.svelte
···
1
1
<script lang="ts">
2
2
import type { AccountMetadata } from "./pdsfetch";
3
3
const { account }: { account: AccountMetadata } = $props();
4
-
console.log(account);
5
4
import { Config } from "../../config";
6
5
</script>
7
6
8
-
<button class="accountBtn" type="button" popovertarget="{account.handle.replace('.')}" title="{account.handle}">
7
+
<button class="accountBtn" type="button" popovertarget="{account.handle.replace('.', '')}" title="{account.handle}">
9
8
<div class="accountContainer">
10
9
{#if account.avatarCid}
11
10
<img
+34
-16
src/lib/pdsfetch.ts
+34
-16
src/lib/pdsfetch.ts
···
368
368
const match = uri.match(/profile\/([\w.]+)\/post\/([\w]+)/);
369
369
if (match) {
370
370
const [, did, postId] = match;
371
-
return `at://${did}/app.bsky.feed.post/${postId}` as At.Did;
371
+
try {
372
+
return `at://${did}/app.bsky.feed.post/${postId}`;
373
+
} catch (e) {
374
+
console.error("Invalid Bluesky post URL format", e);
375
+
return null;
376
+
}
372
377
}
373
378
}
374
-
375
-
this.error = "Invalid Bluesky post URL format";
376
-
return null;
377
379
}
378
380
379
381
const fetchGuestbookPosts = async () => {
380
-
try {
381
-
const { data } = await rpc.get("app.bsky.feed.getPostThread", {
382
-
params: {
383
-
uri: convertUri(Config.GUESTBOOK_POST)
384
-
}
385
-
});
386
-
387
-
return data.records as ComAtprotoRepoListRecords.Record[];
388
-
} catch (e) {
389
-
console.error(`Error fetching replies for ${did}. Are you sure this is a Bluesky post?`, e);
390
-
return null;
391
-
}
382
+
const params = new URLSearchParams({ uri: convertUri(Config.GUESTBOOK_POST) });
383
+
const url = `https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?${params.toString()}`;
384
+
385
+
try {
386
+
const response = await fetch(url, {
387
+
method: "GET",
388
+
headers: {
389
+
Accept: "application/json",
390
+
},
391
+
cache: "no-store",
392
+
});
393
+
394
+
if (!response.ok) {
395
+
const errorText = await response.text();
396
+
console.error("Fetch Error: ", errorText);
397
+
throw new Error(`Failed to fetch thread: ${response.statusText}`);
398
+
}
399
+
400
+
const data = await response.json();
401
+
402
+
if (!data.thread || !data.thread.replies) {
403
+
throw new Error("Invalid thread data: Missing expected properties.");
404
+
}
405
+
406
+
return data.thread.replies;
407
+
} catch (e) {
408
+
console.error("Is this the wrong kind of Bluesky object?", e);
409
+
}
392
410
}
393
411
394
412
export { getAllMetadataFromPds, getNextPosts, Post, fetchGuestbookPosts };
+16
-5
themes/birdrights/theme.css
+16
-5
themes/birdrights/theme.css
···
396
396
#guestbookContents {
397
397
width: 100%;
398
398
max-width: 600px;
399
-
border: 1px var(--border-color) solid;
400
-
color: var(--text-color);
401
-
background-color: var(--content-background-color);
402
-
backdrop-filter: blur(5px);
403
399
height: calc(100vh - 20px);
404
400
padding: 10px;
405
-
border-radius: 5px;
406
401
gap: 10px;
407
402
z-index: 10;
408
403
}
···
440
435
#accountsList {
441
436
grid-template-columns: repeat(6, 1fr);
442
437
}
438
+
}
439
+
440
+
/* Popovers */
441
+
[popover] {
442
+
padding: 0;
443
+
max-width: 600px;
444
+
border: 1px var(--border-color) solid;
445
+
color: var(--text-color);
446
+
background-color: var(--content-background-color);
447
+
backdrop-filter: blur(5px);
448
+
border-radius: 5px;
449
+
z-index: 10;
450
+
}
451
+
452
+
[popover] iframe {
453
+
display: block;
443
454
}
444
455
445
456
/* Scrollbars */