···11+import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs";
22+import { useEntitySetContext } from "components/EntitySetProvider";
33+import { useEffect, useState } from "react";
44+import { useEntity } from "src/replicache";
55+import { useUIState } from "src/useUIState";
66+import { elementId } from "src/utils/elementId";
77+import { focusBlock } from "src/utils/focusBlock";
88+import { AppBskyFeedDefs, AppBskyFeedPost, RichText } from "@atproto/api";
99+import { Separator } from "components/Layout";
1010+import { useInitialPageLoad } from "components/InitialPageLoadProvider";
1111+import { BlueskyTiny } from "components/Icons/BlueskyTiny";
1212+import { CommentTiny } from "components/Icons/CommentTiny";
1313+import {
1414+ BlueskyEmbed,
1515+ PostNotAvailable,
1616+} from "components/Blocks/BlueskyPostBlock/BlueskyEmbed";
1717+import { BlueskyRichText } from "components/Blocks/BlueskyPostBlock/BlueskyRichText";
1818+1919+export const PubBlueskyPostBlock = ({ post }: { post: PostView }) => {
2020+ switch (true) {
2121+ case AppBskyFeedDefs.isBlockedPost(post) ||
2222+ AppBskyFeedDefs.isBlockedAuthor(post) ||
2323+ AppBskyFeedDefs.isNotFoundPost(post):
2424+ return (
2525+ <div className={`w-full`}>
2626+ <PostNotAvailable />
2727+ </div>
2828+ );
2929+3030+ case AppBskyFeedDefs.validatePostView(post).success:
3131+ let record = post.record as AppBskyFeedDefs.PostView["record"];
3232+ let facets = record.facets;
3333+3434+ // silliness to get the text and timestamp from the record with proper types
3535+ let text: string | null = null;
3636+ let timestamp: string | undefined = undefined;
3737+ if (AppBskyFeedPost.isRecord(record)) {
3838+ text = (record as AppBskyFeedPost.Record).text;
3939+ timestamp = (record as AppBskyFeedPost.Record).createdAt;
4040+ }
4141+4242+ //getting the url to the post
4343+ let postId = post.uri.split("/")[4];
4444+ let url = `https://bsky.app/profile/${post.author.handle}/post/${postId}`;
4545+4646+ let datetimeFormatted = new Date(
4747+ timestamp ? timestamp : "",
4848+ ).toLocaleString("en-US", {
4949+ month: "short",
5050+ day: "numeric",
5151+ year: "numeric",
5252+ hour: "numeric",
5353+ minute: "numeric",
5454+ hour12: true,
5555+ });
5656+ return (
5757+ <div
5858+ className={`
5959+ block-border
6060+ mb-2
6161+ flex flex-col gap-2 relative w-full overflow-hidden group/blueskyPostBlock sm:p-3 p-2 text-sm text-secondary bg-bg-page
6262+ `}
6363+ >
6464+ {post.author && record && (
6565+ <>
6666+ <div className="bskyAuthor w-full flex items-center gap-2">
6767+ <img
6868+ src={post.author?.avatar}
6969+ alt={`${post.author?.displayName}'s avatar`}
7070+ className="shink-0 w-8 h-8 rounded-full border border-border-light"
7171+ />
7272+ <div className="grow flex flex-col gap-0.5 leading-tight">
7373+ <div className=" font-bold text-secondary">
7474+ {post.author?.displayName}
7575+ </div>
7676+ <a
7777+ className="text-xs text-tertiary hover:underline"
7878+ target="_blank"
7979+ href={`https://bsky.app/profile/${post.author?.handle}`}
8080+ >
8181+ @{post.author?.handle}
8282+ </a>
8383+ </div>
8484+ </div>
8585+8686+ <div className="flex flex-col gap-2 ">
8787+ <div>
8888+ <pre className="whitespace-pre-wrap">
8989+ {BlueskyRichText({
9090+ record: record as AppBskyFeedPost.Record | null,
9191+ })}
9292+ </pre>
9393+ </div>
9494+ {post.embed && (
9595+ <BlueskyEmbed embed={post.embed} postUrl={url} />
9696+ )}
9797+ </div>
9898+ </>
9999+ )}
100100+ <div className="w-full flex gap-2 items-center justify-between">
101101+ <div className="text-xs text-tertiary">{datetimeFormatted}</div>
102102+ <div className="flex gap-2 items-center">
103103+ {post.replyCount && post.replyCount > 0 && (
104104+ <>
105105+ <a
106106+ className="flex items-center gap-1 hover:no-underline"
107107+ target="_blank"
108108+ href={url}
109109+ >
110110+ {post.replyCount}
111111+ <CommentTiny />
112112+ </a>
113113+ <Separator classname="h-4" />
114114+ </>
115115+ )}
116116+117117+ <a className="" target="_blank" href={url}>
118118+ <BlueskyTiny />
119119+ </a>
120120+ </div>
121121+ </div>
122122+ </div>
123123+ );
124124+ }
125125+};
+29-2
app/lish/[did]/[publication]/[rkey]/page.tsx
···22import { AtUri } from "@atproto/syntax";
33import { ids } from "lexicons/api/lexicons";
44import {
55+ PubLeafletBlocksBskyPost,
56 PubLeafletDocument,
67 PubLeafletPagesLinearDocument,
78 PubLeafletPublication,
89} from "lexicons/api";
910import { Metadata } from "next";
1010-import { AtpAgent } from "@atproto/api";
1111+import { AtpAgent, Agent, AtpBaseClient } from "@atproto/api";
1112import { QuoteHandler } from "./QuoteHandler";
1213import { InteractionDrawer } from "./Interactions/InteractionDrawer";
1314import {
···1920import { PostPage } from "./PostPage";
2021import { PageLayout } from "./PageLayout";
2122import { extractCodeBlocks } from "./extractCodeBlocks";
2323+import { getIdentityData } from "actions/getIdentityData";
2424+import { createOauthClient } from "src/atproto-oauth";
22252326export async function generateMetadata(props: {
2427 params: Promise<{ publication: string; did: string; rkey: string }>;
···6164 </p>
6265 </div>
6366 );
6464- let agent = new AtpAgent({ service: "https://public.api.bsky.app" });
6767+ let identity = await getIdentityData();
6868+ let agent;
6969+ if (identity?.atp_did) {
7070+ const oauthClient = await createOauthClient();
7171+ let credentialSession = await oauthClient.restore(identity.atp_did);
7272+ agent = new Agent(credentialSession);
7373+ } else agent = new AtpAgent({ service: "https://public.api.bsky.app" });
6574 let [document, profile] = await Promise.all([
6675 getPostPageData(
6776 AtUri.make(
···8392 </div>
8493 );
8594 let record = document.data as PubLeafletDocument.Record;
9595+ let bskyPosts = record.pages.flatMap((p) => {
9696+ let page = p as PubLeafletPagesLinearDocument.Main;
9797+ return page.blocks?.filter(
9898+ (b) => b.block.$type === ids.PubLeafletBlocksBskyPost,
9999+ );
100100+ });
101101+ let bskyPostData =
102102+ bskyPosts.length > 0
103103+ ? await agent.getPosts({
104104+ uris: bskyPosts
105105+ .map((p) => {
106106+ let block = p?.block as PubLeafletBlocksBskyPost.Main;
107107+ return block.postRef.uri;
108108+ })
109109+ .slice(0, 24),
110110+ })
111111+ : { data: { posts: [] } };
86112 let firstPage = record.pages[0];
87113 let blocks: PubLeafletPagesLinearDocument.Block[] = [];
88114 if (PubLeafletPagesLinearDocument.isMain(firstPage)) {
···128154 pubRecord={pubRecord}
129155 profile={profile.data}
130156 document={document}
157157+ bskyPostData={bskyPostData.data.posts}
131158 did={did}
132159 blocks={blocks}
133160 name={decodeURIComponent((await props.params).publication)}
+2
lexicons/api/index.ts
···88import * as PubLeafletDocument from './types/pub/leaflet/document'
99import * as PubLeafletPublication from './types/pub/leaflet/publication'
1010import * as PubLeafletBlocksBlockquote from './types/pub/leaflet/blocks/blockquote'
1111+import * as PubLeafletBlocksBskyPost from './types/pub/leaflet/blocks/bskyPost'
1112import * as PubLeafletBlocksCode from './types/pub/leaflet/blocks/code'
1213import * as PubLeafletBlocksHeader from './types/pub/leaflet/blocks/header'
1314import * as PubLeafletBlocksHorizontalRule from './types/pub/leaflet/blocks/horizontalRule'
···3940export * as PubLeafletDocument from './types/pub/leaflet/document'
4041export * as PubLeafletPublication from './types/pub/leaflet/publication'
4142export * as PubLeafletBlocksBlockquote from './types/pub/leaflet/blocks/blockquote'
4343+export * as PubLeafletBlocksBskyPost from './types/pub/leaflet/blocks/bskyPost'
4244export * as PubLeafletBlocksCode from './types/pub/leaflet/blocks/code'
4345export * as PubLeafletBlocksHeader from './types/pub/leaflet/blocks/header'
4446export * as PubLeafletBlocksHorizontalRule from './types/pub/leaflet/blocks/horizontalRule'
···1414import type * as PubLeafletBlocksMath from '../blocks/math'
1515import type * as PubLeafletBlocksCode from '../blocks/code'
1616import type * as PubLeafletBlocksHorizontalRule from '../blocks/horizontalRule'
1717+import type * as PubLeafletBlocksBskyPost from '../blocks/bskyPost'
17181819const is$typed = _is$typed,
1920 validate = _validate
···4647 | $Typed<PubLeafletBlocksMath.Main>
4748 | $Typed<PubLeafletBlocksCode.Main>
4849 | $Typed<PubLeafletBlocksHorizontalRule.Main>
5050+ | $Typed<PubLeafletBlocksBskyPost.Main>
4951 | { $type: string }
5052 alignment?:
5153 | 'lex:pub.leaflet.pages.linearDocument#textAlignLeft'