a tool for shared writing and social publishing

use BskyPostContent in the leaflet as well

+38 -193
+13 -113
app/lish/[did]/[publication]/[rkey]/Blocks/PublishBskyPostBlock.tsx
··· 13 13 } from "components/Blocks/BlueskyPostBlock/BlueskyEmbed"; 14 14 import { BlueskyRichText } from "components/Blocks/BlueskyPostBlock/BlueskyRichText"; 15 15 import { openPage } from "../PostPages"; 16 + import { BskyPostContent } from "../BskyPostContent"; 16 17 17 18 export const PubBlueskyPostBlock = (props: { 18 19 post: PostView; ··· 49 50 50 51 //getting the url to the post 51 52 let postId = post.uri.split("/")[4]; 53 + let postView = post as PostView; 54 + 52 55 let url = `https://bsky.app/profile/${post.author.handle}/post/${postId}`; 53 56 54 57 const parent = props.pageId ··· 56 59 : undefined; 57 60 58 61 return ( 59 - <div 60 - onClick={handleOpenThread} 61 - className={` 62 - ${props.className} 63 - block-border 64 - mb-2 65 - flex flex-col gap-2 relative w-full overflow-hidden group/blueskyPostBlock sm:p-3 p-2 text-sm text-secondary bg-bg-page 66 - cursor-pointer hover:border-accent-contrast 67 - `} 68 - > 69 - {post.author && record && ( 70 - <> 71 - <div className="bskyAuthor w-full flex items-center gap-2"> 72 - {post.author.avatar && ( 73 - <img 74 - src={post.author?.avatar} 75 - alt={`${post.author?.displayName}'s avatar`} 76 - className="shink-0 w-8 h-8 rounded-full border border-border-light" 77 - /> 78 - )} 79 - <div className="grow flex flex-col gap-0.5 leading-tight"> 80 - <div className=" font-bold text-secondary"> 81 - {post.author?.displayName} 82 - </div> 83 - <a 84 - className="text-xs text-tertiary hover:underline" 85 - target="_blank" 86 - href={`https://bsky.app/profile/${post.author?.handle}`} 87 - onClick={(e) => e.stopPropagation()} 88 - > 89 - @{post.author?.handle} 90 - </a> 91 - </div> 92 - </div> 93 - 94 - <div className="flex flex-col gap-2 "> 95 - <div> 96 - <pre className="whitespace-pre-wrap"> 97 - {BlueskyRichText({ 98 - record: record as AppBskyFeedPost.Record | null, 99 - })} 100 - </pre> 101 - </div> 102 - {post.embed && ( 103 - <div onClick={(e) => e.stopPropagation()}> 104 - <BlueskyEmbed embed={post.embed} postUrl={url} /> 105 - </div> 106 - )} 107 - </div> 108 - </> 109 - )} 110 - <div className="w-full flex gap-2 items-center justify-between"> 111 - <ClientDate date={timestamp} /> 112 - <div className="flex gap-2 items-center"> 113 - {post.replyCount != null && post.replyCount > 0 && ( 114 - <> 115 - <ThreadLink 116 - postUri={post.uri} 117 - parent={parent} 118 - className="flex items-center gap-1 hover:text-accent-contrast" 119 - onClick={(e) => e.stopPropagation()} 120 - > 121 - {post.replyCount} 122 - <CommentTiny /> 123 - </ThreadLink> 124 - <Separator classname="h-4" /> 125 - </> 126 - )} 127 - {post.quoteCount != null && post.quoteCount > 0 && ( 128 - <> 129 - <QuotesLink 130 - postUri={post.uri} 131 - parent={parent} 132 - className="flex items-center gap-1 hover:text-accent-contrast" 133 - onClick={(e) => e.stopPropagation()} 134 - > 135 - {post.quoteCount} 136 - <QuoteTiny /> 137 - </QuotesLink> 138 - <Separator classname="h-4" /> 139 - </> 140 - )} 141 - 142 - <a 143 - className="" 144 - target="_blank" 145 - href={url} 146 - onClick={(e) => e.stopPropagation()} 147 - > 148 - <BlueskyTiny /> 149 - </a> 150 - </div> 151 - </div> 152 - </div> 62 + <BskyPostContent 63 + post={postView} 64 + parent={undefined} 65 + showBlueskyLink={true} 66 + showEmbed={true} 67 + avatarSize="large" 68 + quoteEnabled 69 + replyEnabled 70 + className="text-sm text-secondary block-border sm:px-3 sm:py-2 px-2 py-1 bg-bg-page mb-2 hover:border-accent-contrast!" 71 + /> 153 72 ); 154 73 } 155 74 }; 156 - 157 - const ClientDate = (props: { date?: string }) => { 158 - let pageLoaded = useHasPageLoaded(); 159 - const formattedDate = useLocalizedDate( 160 - props.date || new Date().toISOString(), 161 - { 162 - month: "short", 163 - day: "numeric", 164 - year: "numeric", 165 - hour: "numeric", 166 - minute: "numeric", 167 - hour12: true, 168 - }, 169 - ); 170 - 171 - if (!pageLoaded) return null; 172 - 173 - return <div className="text-xs text-tertiary">{formattedDate}</div>; 174 - };
+11 -7
app/lish/[did]/[publication]/[rkey]/BskyPostContent.tsx
··· 19 19 20 20 export function BskyPostContent(props: { 21 21 post: PostView; 22 - parent: OpenPage; 22 + parent: OpenPage | undefined; 23 23 avatarSize?: "tiny" | "small" | "medium" | "large" | "giant"; 24 24 className?: string; 25 25 showEmbed?: boolean; ··· 87 87 className={`flex flex-col min-w-0 w-full z-0 ${props.replyLine ? "mt-2" : ""}`} 88 88 > 89 89 <button 90 - className="bskyPostTextContent flex flex-col grow mt-1 text-left" 90 + className={`bskyPostTextContent flex flex-col grow text-left ${props.avatarSize === "small" ? "mt-0.5" : props.avatarSize === "large" ? "mt-2" : "mt-1"}`} 91 91 onClick={() => { 92 92 openPage(parent, { type: "thread", uri: post.uri }); 93 93 }} ··· 99 99 /> 100 100 101 101 <div className={`postContent flex flex-col gap-2 mt-0.5`}> 102 - <div className="text-secondary text-sm"> 102 + <div className="text-secondary"> 103 103 <BlueskyRichText record={record} /> 104 104 </div> 105 105 {showEmbed && post.embed && ( ··· 133 133 <div className="flex gap-3 items-center"> 134 134 {showBlueskyLink && ( 135 135 <> 136 - <a className="text-tertiary" target="_blank" href={url}> 136 + <a 137 + className="text-tertiary hover:text-accent-contrast" 138 + target="_blank" 139 + href={url} 140 + > 137 141 <BlueskyLinkTiny /> 138 142 </a> 139 143 </> ··· 276 280 }) { 277 281 const replyContent = props.post.replyCount != null && 278 282 props.post.replyCount > 0 && ( 279 - <div className="postRepliesCount flex items-center gap-1 text-tertiary text-xs"> 283 + <div className="postRepliesCount flex items-center gap-1 text-xs"> 280 284 <CommentTiny /> 281 285 {props.post.replyCount} 282 286 </div> ··· 284 288 285 289 const quoteContent = props.post.quoteCount != null && 286 290 props.post.quoteCount > 0 && ( 287 - <div className="postQuoteCount flex items-center gap-1 text-tertiary text-xs"> 291 + <div className="postQuoteCount flex items-center gap-1 text-xs"> 288 292 <QuoteTiny /> 289 293 {props.post.quoteCount} 290 294 </div> 291 295 ); 292 296 293 297 return ( 294 - <div className="postCounts flex gap-2 items-center w-full"> 298 + <div className="postCounts flex gap-2 items-center w-full text-tertiary"> 295 299 {replyContent && 296 300 (props.replyEnabled ? ( 297 301 <ThreadLink
+1
app/lish/[did]/[publication]/[rkey]/Interactions/Quotes.tsx
··· 113 113 return ( 114 114 <> 115 115 <Quote 116 + key={q.uri} 116 117 q={q} 117 118 index={index} 118 119 did={props.did}
-10
app/lish/[did]/[publication]/[rkey]/ThreadPage.tsx
··· 326 326 )} 327 327 </div> 328 328 )} 329 - 330 - {hasReplies && props.depth >= 3 && ( 331 - <ThreadLink 332 - postUri={props.post.post.uri} 333 - parent={{ type: "thread", uri: pageUri }} 334 - className="text-left ml-10 text-sm text-accent-contrast hover:underline" 335 - > 336 - View more replies 337 - </ThreadLink> 338 - )} 339 329 </div> 340 330 ); 341 331 };
+13 -63
components/Blocks/BlueskyPostBlock/index.tsx
··· 13 13 import { BlueskyTiny } from "components/Icons/BlueskyTiny"; 14 14 import { CommentTiny } from "components/Icons/CommentTiny"; 15 15 import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 16 + import { BskyPostContent } from "app/lish/[did]/[publication]/[rkey]/BskyPostContent"; 17 + import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 16 18 17 19 export const BlueskyPostBlock = (props: BlockProps & { preview?: boolean }) => { 18 20 let { permissions } = useEntitySetContext(); ··· 76 78 77 79 //getting the url to the post 78 80 let postId = post.post.uri.split("/")[4]; 81 + let postView = post.post as PostView; 79 82 let url = `https://bsky.app/profile/${post.post.author.handle}/post/${postId}`; 80 83 81 84 return ( 82 85 <BlockLayout 83 86 isSelected={!!isSelected} 84 87 hasBackground="page" 85 - className="flex flex-col gap-2 relative overflow-hidden group/blueskyPostBlock text-sm text-secondary" 88 + borderOnHover 89 + className="blueskyPostBlock sm:px-3! sm:py-2! px-2! py-1!" 86 90 > 87 - {post.post.author && record && ( 88 - <> 89 - <div className="bskyAuthor w-full flex items-center gap-2"> 90 - {post.post.author?.avatar ? ( 91 - <img 92 - src={post.post.author?.avatar} 93 - alt={`${post.post.author?.displayName}'s avatar`} 94 - className="shrink-0 w-8 h-8 rounded-full border border-border-light" 95 - /> 96 - ) : ( 97 - <div className="shrink-0 w-8 h-8 rounded-full border border-border-light bg-border"></div> 98 - )} 99 - <div className="grow flex flex-col gap-0.5 leading-tight"> 100 - <div className=" font-bold text-secondary"> 101 - {post.post.author?.displayName} 102 - </div> 103 - <a 104 - className="text-xs text-tertiary hover:underline" 105 - target="_blank" 106 - href={`https://bsky.app/profile/${post.post.author?.handle}`} 107 - > 108 - @{post.post.author?.handle} 109 - </a> 110 - </div> 111 - </div> 112 - 113 - <div className="flex flex-col gap-2 "> 114 - <div> 115 - <pre className="whitespace-pre-wrap"> 116 - {BlueskyRichText({ 117 - record: record as AppBskyFeedPost.Record | null, 118 - })} 119 - </pre> 120 - </div> 121 - {post.post.embed && ( 122 - <BlueskyEmbed embed={post.post.embed} postUrl={url} /> 123 - )} 124 - </div> 125 - </> 126 - )} 127 - <div className="w-full flex gap-2 items-center justify-between"> 128 - {timestamp && <PostDate timestamp={timestamp} />} 129 - <div className="flex gap-2 items-center"> 130 - {post.post.replyCount != null && post.post.replyCount > 0 && ( 131 - <> 132 - <a 133 - className="flex items-center gap-1 hover:no-underline" 134 - target="_blank" 135 - href={url} 136 - > 137 - {post.post.replyCount} 138 - <CommentTiny /> 139 - </a> 140 - <Separator classname="h-4" /> 141 - </> 142 - )} 143 - 144 - <a className="" target="_blank" href={url}> 145 - <BlueskyTiny /> 146 - </a> 147 - </div> 148 - </div> 91 + <BskyPostContent 92 + post={postView} 93 + parent={undefined} 94 + showBlueskyLink={true} 95 + showEmbed={true} 96 + avatarSize="large" 97 + className="text-sm text-secondary " 98 + /> 149 99 </BlockLayout> 150 100 ); 151 101 }