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 3d52697..0c3b4a0 100644 --- a/src/lib/PostComponent.svelte +++ b/src/lib/PostComponent.svelte @@ -12,12 +12,12 @@ src="https://pds.witchcraft.systems/xrpc/com.atproto.sync.getBlob?did={post.authorDid}&cid={post.authorAvatarCid}" /> {/if} -

{post.displayName} | {post.timenotstamp}

+

{post.displayName} | {post.authorHandle} | {post.timenotstamp}

{post.text}

- {#if post.replyingDid} -

Replying to: {post.replyingDid}

+ {#if post.replyingUri} +

Replying to: {post.replyingUri.repo}

{/if} {#if post.imagesCid}
diff --git a/src/lib/pdsfetch.ts b/src/lib/pdsfetch.ts index a29e641..f2bbfdb 100644 --- a/src/lib/pdsfetch.ts +++ b/src/lib/pdsfetch.ts @@ -7,25 +7,33 @@ import type { At, ComAtprotoRepoListRecords, } from "@atcute/client/lexicons"; -import type App from "../App.svelte"; -// import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons"; -// import { AppBskyFeedPost } from "@atcute/client/lexicons"; -// import { AppBskyActorDefs } from "@atcute/client/lexicons"; +import { + CompositeDidDocumentResolver, + PlcDidDocumentResolver, + WebDidDocumentResolver, +} from "@atcute/identity-resolver"; 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; @@ -35,17 +43,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) { @@ -58,10 +67,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) => @@ -79,7 +88,7 @@ class Post { } } -const didFromATuri = (aturi: string) => { +const processAtUri = (aturi: string): atUriObject => { const parts = aturi.split("/"); return { repo: parts[2], @@ -110,8 +119,10 @@ const getAccountMetadata = async (did: `did:${string}:${string}`) => { }, }); const value = data.value as AppBskyActorProfile.Record; + const handle = await blueskyHandleFromDid(did); const account: AccountMetadata = { did: did, + handle: handle, displayName: value.displayName || "", avatarCid: null, }; @@ -145,6 +156,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( @@ -167,5 +211,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 };