diff --git a/deno.lock b/deno.lock
index 1e4f356..df8c920 100644
--- a/deno.lock
+++ b/deno.lock
@@ -3,6 +3,7 @@
"specifiers": {
"npm:@atcute/bluesky@^2.0.2": "2.0.2_@atcute+client@3.0.1",
"npm:@atcute/client@^3.0.1": "3.0.1",
+ "npm:@atcute/identity-resolver@~0.1.2": "0.1.2_@atcute+identity@0.1.3",
"npm:@sveltejs/vite-plugin-svelte@^5.0.3": "5.0.3_svelte@5.28.1__acorn@8.14.1_vite@6.3.2__picomatch@4.0.2",
"npm:@tsconfig/svelte@^5.0.4": "5.0.4",
"npm:svelte-check@^4.1.5": "4.1.6_svelte@5.28.1__acorn@8.14.1_typescript@5.7.3",
@@ -27,6 +28,29 @@
"@atcute/client@3.0.1": {
"integrity": "sha512-j51SuQYQj5jeKrx1DCXx+Q3fpVvO0JYGnKnJAdDSlesSLjPXjvnx1abC+hkuro58KRHHJvRA6P1MC0pmJsWfcg=="
},
+ "@atcute/identity-resolver@0.1.2_@atcute+identity@0.1.3": {
+ "integrity": "sha512-fP2VbHD04kVcCdNi/Kszo6jFzqM7Pg3p33oGhfp2zVkwFKaVBlwCaFRWEga/Xvu/IDLwNdASGWnLqoA34SFeSg==",
+ "dependencies": [
+ "@atcute/identity",
+ "@atcute/util-fetch",
+ "@badrap/valita"
+ ]
+ },
+ "@atcute/identity@0.1.3": {
+ "integrity": "sha512-ndlD8nypHt8G00wixbozKdSNL0O8HTzBjFGEXeAcBUCXSZPBjRWbqtgyJxhgUWnr7swgxgw1mSbZwRB5b7xCiQ==",
+ "dependencies": [
+ "@badrap/valita"
+ ]
+ },
+ "@atcute/util-fetch@1.0.1": {
+ "integrity": "sha512-Clc0E/5ufyGBVfYBUwWNlHONlZCoblSr4Ho50l1LhmRPGB1Wu/AQ9Sz+rsBg7fdaW/auve8ulmwhRhnX2cGRow==",
+ "dependencies": [
+ "@badrap/valita"
+ ]
+ },
+ "@badrap/valita@0.4.4": {
+ "integrity": "sha512-GEhUCk9c4XbNxi+0YZHZsV4fYNd6HejfWuN4Ti4c02DauX+LyX5WY1Y3WfyZ8Pxxl0zqhs+MLtW98cMh86vv6g=="
+ },
"@esbuild/aix-ppc64@0.25.2": {
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="
},
@@ -443,6 +467,7 @@
"dependencies": [
"npm:@atcute/bluesky@^2.0.2",
"npm:@atcute/client@^3.0.1",
+ "npm:@atcute/identity-resolver@~0.1.2",
"npm:@sveltejs/vite-plugin-svelte@^5.0.3",
"npm:@tsconfig/svelte@^5.0.4",
"npm:svelte-check@^4.1.5",
diff --git a/package.json b/package.json
index 36d1491..59269d2 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,8 @@
},
"dependencies": {
"@atcute/bluesky": "^2.0.2",
- "@atcute/client": "^3.0.1"
+ "@atcute/client": "^3.0.1",
+ "@atcute/identity-resolver": "^0.1.2"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.3",
diff --git a/src/lib/PostComponent.svelte b/src/lib/PostComponent.svelte
index bdbd72e..3af49c0 100644
--- a/src/lib/PostComponent.svelte
+++ b/src/lib/PostComponent.svelte
@@ -13,13 +13,24 @@
alt="avatar of {post.displayName}"
/>
{/if}
-
+
- {#if post.replyingDid}
-
replying to: {post.replyingDid}
- {/if}
-
{post.text}
+
{post.text}
+ {#if post.replyingUri}
+
replying to {post.replyingUri.repo}
+ {/if}
+ {#if post.quotingUri}
+
quoting {post.quotingUri.repo}
+ {/if}
{#if post.imagesCid}
{#each post.imagesCid as imageLink}
diff --git a/src/lib/pdsfetch.ts b/src/lib/pdsfetch.ts
index 748fee6..33a4345 100644
--- a/src/lib/pdsfetch.ts
+++ b/src/lib/pdsfetch.ts
@@ -2,9 +2,16 @@ import { simpleFetchHandler, XRPC } from "@atcute/client";
import "@atcute/bluesky/lexicons";
import type {
AppBskyActorDefs,
+ AppBskyActorProfile,
AppBskyFeedPost,
+ At,
ComAtprotoRepoListRecords,
} from "@atcute/client/lexicons";
+import {
+ CompositeDidDocumentResolver,
+ PlcDidDocumentResolver,
+ WebDidDocumentResolver,
+} from "@atcute/identity-resolver";
import { Config } from "../../config";
// import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons";
// import { AppBskyFeedPost } from "@atcute/client/lexicons";
@@ -13,17 +20,24 @@ import { Config } from "../../config";
interface AccountMetadata {
did: string;
displayName: string;
+ handle: string;
avatarCid: string | null;
}
+interface atUriObject {
+ repo: string;
+ collection: string;
+ rkey: string;
+}
class Post {
authorDid: string;
authorAvatarCid: string | null;
+ authorHandle: string;
displayName: string;
text: string;
timestamp: number;
timenotstamp: string;
- quotingDid: string | null;
- replyingDid: string | null;
+ quotingUri: atUriObject | null;
+ replyingUri: atUriObject | null;
imagesCid: string[] | null;
videosLinkCid: string | null;
@@ -33,17 +47,18 @@ class Post {
) {
this.authorDid = account.did;
this.authorAvatarCid = account.avatarCid;
+ this.authorHandle = account.handle;
this.displayName = account.displayName;
const post = record.value as AppBskyFeedPost.Record;
this.timenotstamp = post.createdAt;
this.text = post.text;
this.timestamp = Date.parse(post.createdAt);
if (post.reply) {
- this.replyingDid = didFromATuri(post.reply.parent.uri).repo;
+ this.replyingUri = processAtUri(post.reply.parent.uri);
} else {
- this.replyingDid = null;
+ this.replyingUri = null;
}
- this.quotingDid = null;
+ this.quotingUri = null;
this.imagesCid = null;
this.videosLinkCid = null;
switch (post.embed?.$type) {
@@ -56,10 +71,10 @@ class Post {
this.videosLinkCid = post.embed.video.ref.$link;
break;
case "app.bsky.embed.record":
- this.quotingDid = didFromATuri(post.embed.record.uri).repo;
+ this.quotingUri = processAtUri(post.embed.record.uri);
break;
case "app.bsky.embed.recordWithMedia":
- this.quotingDid = didFromATuri(post.embed.record.record.uri).repo;
+ this.quotingUri = processAtUri(post.embed.record.record.uri);
switch (post.embed.media.$type) {
case "app.bsky.embed.images":
this.imagesCid = post.embed.media.images.map((imageRecord) =>
@@ -77,7 +92,7 @@ class Post {
}
}
-const didFromATuri = (aturi: string) => {
+const processAtUri = (aturi: string): atUriObject => {
const parts = aturi.split("/");
return {
repo: parts[2],
@@ -108,9 +123,11 @@ const getAccountMetadata = async (did: `did:${string}:${string}`) => {
rkey: "self",
},
});
- const value = data.value as AppBskyActorDefs.ProfileView;
+ const value = data.value as AppBskyActorProfile.Record;
+ const handle = await blueskyHandleFromDid(did);
const account: AccountMetadata = {
did: did,
+ handle: handle,
displayName: value.displayName || "",
avatarCid: null,
};
@@ -143,7 +160,7 @@ const fetchPosts = async (did: string) => {
try {
const { data } = await rpc.get("com.atproto.repo.listRecords", {
params: {
- repo: did,
+ repo: did as At.Identifier,
collection: "app.bsky.feed.post",
limit: 5,
},
@@ -163,6 +180,39 @@ const fetchPosts = async (did: string) => {
}
};
+const identityResolve = async (did: At.Did) => {
+ const resolver = new CompositeDidDocumentResolver({
+ methods: {
+ plc: new PlcDidDocumentResolver(),
+ web: new WebDidDocumentResolver(),
+ },
+ });
+
+ if (did.startsWith("did:plc:") || did.startsWith("did:web:")) {
+ const doc = await resolver.resolve(
+ did as `did:plc:${string}` | `did:web:${string}`,
+ );
+ return doc;
+ } else {
+ throw new Error(`Unsupported DID type: ${did}`);
+ }
+};
+
+const blueskyHandleFromDid = async (did: At.Did) => {
+ const doc = await identityResolve(did);
+ if (doc.alsoKnownAs) {
+ const handleAtUri = doc.alsoKnownAs.find((url) => url.startsWith("at://"));
+ const handle = handleAtUri?.split("/")[2];
+ if (!handle) {
+ return "Handle not found";
+ } else {
+ return handle;
+ }
+ } else {
+ return "Handle not found";
+ }
+};
+
const fetchAllPosts = async () => {
const users: AccountMetadata[] = await getAllMetadataFromPds();
const postRecords = await Promise.all(
@@ -186,5 +236,11 @@ const fetchAllPosts = async () => {
return posts;
};
+const testApiCall = async () => {
+ const { data } = await rpc.get("com.atproto.sync.listRepos", {
+ params: {},
+ });
+ console.log(data);
+};
export { fetchAllPosts, getAllMetadataFromPds, Post };
export type { AccountMetadata };