+21
-5
src/App.svelte
+21
-5
src/App.svelte
···
1
1
<script lang="ts">
2
2
import PostComponent from "./lib/PostComponent.svelte";
3
-
import { fetchAllPosts, Post } from "./lib/pdsfetch";
3
+
import AccountComponent from "./lib/AccountComponent.svelte";
4
+
import { fetchAllPosts, Post, getAllMetadataFromPds } from "./lib/pdsfetch";
4
5
const postsPromise = fetchAllPosts();
6
+
const accountsPromise = getAllMetadataFromPds();
5
7
</script>
6
8
7
9
<main>
10
+
<h1>Welcome to the Feed</h1>
11
+
{#await accountsPromise}
12
+
<p>Loading...</p>
13
+
{:then accountsData}
14
+
<div id="Account">
15
+
{#each accountsData as accountObject}
16
+
<AccountComponent account={accountObject} />
17
+
{/each}
18
+
</div>
19
+
{:catch error}
20
+
<p>Error: {error.message}</p>
21
+
{/await}
22
+
8
23
{#await postsPromise}
9
24
<p>Loading...</p>
10
25
{:then postsData}
11
-
{#each postsData as postObject}
12
-
<PostComponent post={postObject as Post} />
13
-
{/each}
26
+
<div id="Feed">
27
+
{#each postsData as postObject}
28
+
<PostComponent post={postObject as Post} />
29
+
{/each}
30
+
</div>
14
31
{/await}
15
32
</main>
16
33
17
34
<style>
18
-
19
35
</style>
+27
src/lib/AccountComponent.svelte
+27
src/lib/AccountComponent.svelte
···
1
+
<script lang="ts">
2
+
import type { AccountMetadata } from "./pdsfetch";
3
+
const { account }: { account: AccountMetadata } = $props();
4
+
</script>
5
+
<div id="accountContainer">
6
+
{#if account.avatarCid}
7
+
<img
8
+
id="avatar"
9
+
alt="avatar of {account.displayName}"
10
+
src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={account.did}&cid={account.avatarCid}"
11
+
/>
12
+
{/if}
13
+
<p>{account.displayName}</p>
14
+
</div>
15
+
<style>
16
+
#accountContainer {
17
+
display: column;
18
+
text-align: start;
19
+
border: 2px solid black;
20
+
padding: 4%;
21
+
}
22
+
#avatar {
23
+
width: 50px;
24
+
height: 50px;
25
+
border-radius: 50%;
26
+
}
27
+
</style>
+11
-7
src/lib/PostComponent.svelte
+11
-7
src/lib/PostComponent.svelte
···
8
8
{#if post.authorAvatarCid}
9
9
<img
10
10
id="avatar"
11
+
alt="avatar of {post.displayName}"
11
12
src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={post.authorAvatarCid}"
12
13
/>
13
14
{/if}
···
18
19
{#if post.replyingDid}
19
20
<p>Replying to: {post.replyingDid}</p>
20
21
{/if}
21
-
{#if post.imagesLinksCid}
22
-
{#each post.imagesLinksCid as imageLink}
23
-
<img
24
-
id="embedImages"
25
-
src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={imageLink}"
26
-
/>
27
-
{/each}
22
+
{#if post.imagesCid}
23
+
<div id="imagesContainer">
24
+
{#each post.imagesCid as imageLink}
25
+
<img
26
+
id="embedImages"
27
+
alt="Post Image"
28
+
src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={imageLink}"
29
+
/>
30
+
{/each}
31
+
</div>
28
32
{/if}
29
33
{#if post.videosLinkCid}
30
34
<video
+26
-9
src/lib/pdsfetch.ts
+26
-9
src/lib/pdsfetch.ts
···
1
1
import { simpleFetchHandler, XRPC } from "@atcute/client";
2
2
import "@atcute/bluesky/lexicons";
3
+
import type {
4
+
AppBskyActorDefs,
5
+
AppBskyFeedPost,
6
+
ComAtprotoRepoListRecords,
7
+
} from "@atcute/client/lexicons";
3
8
// import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons";
4
9
// import { AppBskyFeedPost } from "@atcute/client/lexicons";
5
10
// import { AppBskyActorDefs } from "@atcute/client/lexicons";
···
12
17
class Post {
13
18
authorDid: string;
14
19
authorAvatarCid: string | null;
15
-
displayName : string;
20
+
displayName: string;
16
21
text: string;
17
22
timestamp: number;
18
23
timenotstamp: string;
19
24
quotingDid: string | null;
20
25
replyingDid: string | null;
21
-
imagesLinksCid: string[] | null;
26
+
imagesCid: string[] | null;
22
27
videosLinkCid: string | null;
23
28
24
-
constructor(record: ComAtprotoRepoListRecords.Record, account : AccountMetadata) {
29
+
constructor(
30
+
record: ComAtprotoRepoListRecords.Record,
31
+
account: AccountMetadata,
32
+
) {
25
33
this.authorDid = account.did;
26
34
this.authorAvatarCid = account.avatarCid;
27
35
this.displayName = account.displayName;
···
35
43
this.replyingDid = null;
36
44
}
37
45
this.quotingDid = null;
38
-
this.imagesLinksCid = null;
46
+
this.imagesCid = null;
39
47
this.videosLinkCid = null;
40
48
switch (post.embed?.$type) {
41
49
case "app.bsky.embed.images":
42
-
this.imagesLinksCid = post.embed.images.map((imageRecord) =>
50
+
this.imagesCid = post.embed.images.map((imageRecord: any) =>
43
51
imageRecord.image.ref.$link
44
52
);
45
53
break;
···
53
61
this.quotingDid = didFromATuri(post.embed.record.record.uri).repo;
54
62
switch (post.embed.media.$type) {
55
63
case "app.bsky.embed.images":
56
-
this.imagesLinksCid = post.embed.media.images.map((imageRecord) =>
64
+
this.imagesCid = post.embed.media.images.map((imageRecord) =>
57
65
imageRecord.image.ref.$link
58
66
);
59
67
···
141
149
await fetchPosts(metadata.did)
142
150
),
143
151
);
144
-
const posts : Post[] = postRecords.flatMap((userFetch) =>
145
-
userFetch.records.map((record) => new Post(record, users.find((user : AccountMetadata) => user.did == userFetch.did)))
152
+
const posts: Post[] = postRecords.flatMap((userFetch) =>
153
+
userFetch.records.map((record) => {
154
+
const user = users.find((user: AccountMetadata) =>
155
+
user.did == userFetch.did
156
+
);
157
+
if (!user) {
158
+
throw new Error(`User with DID ${userFetch.did} not found`);
159
+
}
160
+
return new Post(record, user);
161
+
})
146
162
);
147
163
posts.sort((a, b) => b.timestamp - a.timestamp);
148
164
return posts;
149
165
};
150
166
151
-
export { fetchAllPosts, Post };
167
+
export { fetchAllPosts, getAllMetadataFromPds, Post };
168
+
export type { AccountMetadata };