tangled
alpha
login
or
join now
witchcraft.systems
/
pds-dash
15
fork
atom
this repo has no description
15
fork
atom
overview
issues
pulls
pipelines
Frontend works
ari.express
10 months ago
f00e0638
6ca0a971
verified
This commit was signed with the committer's
known signature
.
ari.express
SSH Key Fingerprint:
SHA256:j4xpQafvRcIH4rwZqM5aREIogWsCjyYohia7vH0+uZY=
+72
-37
4 changed files
expand all
collapse all
unified
split
deno.lock
package.json
src
App.svelte
lib
pdsfetch.ts
+5
deno.lock
···
8
8
"npm:@tsconfig/svelte@^5.0.4": "5.0.4",
9
9
"npm:moment@^2.30.1": "2.30.1",
10
10
"npm:svelte-check@^4.1.5": "4.1.6_svelte@5.28.1__acorn@8.14.1_typescript@5.7.3",
11
11
+
"npm:svelte-infinite-loading@^1.4.0": "1.4.0",
11
12
"npm:svelte@^5.23.1": "5.28.1_acorn@8.14.1",
12
13
"npm:typescript@~5.7.2": "5.7.3",
13
14
"npm:vite@^6.3.1": "6.3.2_picomatch@4.0.2"
···
415
416
"typescript"
416
417
]
417
418
},
419
419
+
"svelte-infinite-loading@1.4.0": {
420
420
+
"integrity": "sha512-Jo+f/yr/HmZQuIiiKKzAHVFXdAUWHW2RBbrcQTil8JVk1sCm/riy7KTJVzjBgQvHasrFQYKF84zvtc9/Y4lFYg=="
421
421
+
},
418
422
"svelte@5.28.1_acorn@8.14.1": {
419
423
"integrity": "sha512-iOa9WmfNG95lSOSJdMhdjJ4Afok7IRAQYXpbnxhd5EINnXseG0GVa9j6WPght4eX78XfFez45Fi+uRglGKPV/Q==",
420
424
"dependencies": [
···
476
480
"npm:@tsconfig/svelte@^5.0.4",
477
481
"npm:moment@^2.30.1",
478
482
"npm:svelte-check@^4.1.5",
483
483
+
"npm:svelte-infinite-loading@^1.4.0",
479
484
"npm:svelte@^5.23.1",
480
485
"npm:typescript@~5.7.2",
481
486
"npm:vite@^6.3.1"
+2
-1
package.json
···
13
13
"@atcute/bluesky": "^2.0.2",
14
14
"@atcute/client": "^3.0.1",
15
15
"@atcute/identity-resolver": "^0.1.2",
16
16
-
"moment": "^2.30.1"
16
16
+
"moment": "^2.30.1",
17
17
+
"svelte-infinite-loading": "^1.4.0"
17
18
},
18
19
"devDependencies": {
19
20
"@sveltejs/vite-plugin-svelte": "^5.0.3",
+36
-16
src/App.svelte
···
1
1
<script lang="ts">
2
2
import PostComponent from "./lib/PostComponent.svelte";
3
3
import AccountComponent from "./lib/AccountComponent.svelte";
4
4
+
import InfiniteLoading from "svelte-infinite-loading";
4
5
import { getNextPosts, Post, getAllMetadataFromPds } from "./lib/pdsfetch";
5
6
import { Config } from "../config";
6
6
-
const postsPromise = getNextPosts();
7
7
const accountsPromise = getAllMetadataFromPds();
8
8
+
import { onMount } from "svelte";
9
9
+
10
10
+
let posts: Post[] = [];
11
11
+
12
12
+
onMount(() => {
13
13
+
// Fetch initial posts
14
14
+
getNextPosts().then((initialPosts) => {
15
15
+
posts = initialPosts;
16
16
+
});
17
17
+
});
18
18
+
// Infinite loading function
19
19
+
const onInfinite = ({ detail: { loaded, complete } }) => {
20
20
+
getNextPosts().then((newPosts) => {
21
21
+
if (newPosts.length > 0) {
22
22
+
posts = [...posts, ...newPosts];
23
23
+
loaded();
24
24
+
} else {
25
25
+
complete();
26
26
+
}
27
27
+
});
28
28
+
};
8
29
</script>
9
30
10
31
<main>
···
12
33
{#await accountsPromise}
13
34
<p>Loading...</p>
14
35
{:then accountsData}
15
15
-
16
36
<div id="Account">
17
37
<h1 id="Header">ATProto PDS</h1>
18
38
<p>Home to {accountsData.length} accounts</p>
···
27
47
<p>Error: {error.message}</p>
28
48
{/await}
29
49
30
30
-
{#await postsPromise}
31
31
-
<p>Loading...</p>
32
32
-
{:then postsData}
33
33
-
<button on:click={getNextPosts}>
34
34
-
Load more posts
35
35
-
</button>
36
36
-
<div id="Feed">
37
37
-
<div id="spacer"></div>
38
38
-
{#each postsData as postObject}
39
39
-
<PostComponent post={postObject as Post} />
40
40
-
{/each}
41
41
-
<div id="spacer"></div>
42
42
-
</div>
43
43
-
{/await}
50
50
+
<div id="Feed">
51
51
+
<div id="spacer"></div>
52
52
+
{#each posts as postObject}
53
53
+
<PostComponent post={postObject as Post} />
54
54
+
{/each}
55
55
+
<InfiniteLoading on:infinite={onInfinite}
56
56
+
id="infiniteLoading"
57
57
+
distance={0}
58
58
+
threshold={0}
59
59
+
useWindow={false}
60
60
+
forceUseWindow={false}
61
61
+
/>
62
62
+
<div id="spacer"></div>
63
63
+
</div>
44
64
</div>
45
65
</main>
46
66
+29
-20
src/lib/pdsfetch.ts
···
26
26
}
27
27
28
28
let accountsMetadata: AccountMetadata[] = [];
29
29
-
// a chronologically sorted list of posts for all users, that will be shown by svelte
30
30
-
// getNextPosts will populate this list with additional posts as needed
31
31
-
let posts: Post[] = [];
29
29
+
32
30
interface atUriObject {
33
31
repo: string;
34
32
collection: string;
···
259
257
return postDate >= cutoffDate;
260
258
});
261
259
if (filtered.length > 0) {
262
262
-
postAcc.account.currentCursor = filtered[filtered.length - 1].cid;
260
260
+
postAcc.account.currentCursor = processAtUri(filtered[filtered.length - 1].uri).rkey;
263
261
}
264
262
return {
265
263
posts: filtered,
···
298
296
const cutoffDate = getCutoffDate(recordsFiltered);
299
297
const recordsCutoff = filterPostsByDate(recordsFiltered, cutoffDate);
300
298
// update the accountMetadata with the new cursor
301
301
-
accountsMetadata = recordsCutoff.map((postAcc) => postAcc.account);
299
299
+
accountsMetadata = accountsMetadata.map((account) => {
300
300
+
const postAcc = recordsCutoff.find(
301
301
+
(postAcc) => postAcc.account.did == account.did,
302
302
+
);
303
303
+
if (postAcc) {
304
304
+
account.currentCursor = postAcc.account.currentCursor;
305
305
+
}
306
306
+
return account;
307
307
+
}
308
308
+
);
302
309
// throw the records in a big single array
303
310
let records = recordsCutoff.flatMap((postAcc) => postAcc.posts);
304
311
// sort the records by timestamp
···
310
317
(b.value as AppBskyFeedPost.Record).createdAt,
311
318
).getTime();
312
319
return bDate - aDate;
313
313
-
}
314
314
-
);
320
320
+
});
315
321
// filter out posts that are in the future
316
322
if (!Config.SHOW_FUTURE_POSTS) {
317
323
const now = Date.now();
···
323
329
});
324
330
}
325
331
// append the new posts to the existing posts
326
326
-
posts = posts.concat(
327
327
-
records.map((record) => {
328
328
-
const account = accountsMetadata.find(
329
329
-
(account) => account.did == processAtUri(record.uri).repo,
332
332
+
333
333
+
const newPosts = records.map((record) => {
334
334
+
const account = accountsMetadata.find(
335
335
+
(account) => account.did == processAtUri(record.uri).repo,
336
336
+
);
337
337
+
if (!account) {
338
338
+
throw new Error(
339
339
+
`Account with DID ${processAtUri(record.uri).repo} not found`,
330
340
);
331
331
-
if (!account) {
332
332
-
throw new Error(`Account with DID ${processAtUri(record.uri).repo} not found`);
333
333
-
}
334
334
-
return new Post(record, account);
335
335
-
}),
336
336
-
);
337
337
-
console.log("Fetched posts:", posts);
338
338
-
return posts;
341
341
+
}
342
342
+
return new Post(record, account);
343
343
+
});
344
344
+
console.log("Fetched posts:", newPosts);
345
345
+
console.log("Metadata:", accountsMetadata);
346
346
+
return newPosts;
339
347
};
340
348
341
349
const fetchPostsForUser = async (did: At.Did, cursor: string | null) => {
342
350
try {
351
351
+
console.log("Fetching posts for user:", did, "with Cursor: ", cursor);
343
352
const { data } = await rpc.get("com.atproto.repo.listRecords", {
344
353
params: {
345
354
repo: did as At.Identifier,
···
388
397
389
398
// return posts.slice(0, Config.MAX_POSTS);
390
399
// };
391
391
-
export { getAllMetadataFromPds, getNextPosts, Post, posts };
400
400
+
export { getAllMetadataFromPds, getNextPosts, Post };
392
401
export type { AccountMetadata };