a tool for shared writing and social publishing

refactor interactions into two components

+105 -51
+6 -4
app/[leaflet_id]/publish/PublishPost.tsx
··· 125 125 submit(); 126 126 }} 127 127 > 128 - <div className="container flex flex-col gap-2 sm:p-3 p-4"> 128 + <div className="container flex flex-col gap-3 sm:p-3 p-4"> 129 129 <PublishingTo 130 130 publication_uri={props.publication_uri} 131 131 record={props.record} 132 132 /> 133 - <hr className="border-border-light my-1" /> 133 + <hr className="border-border" /> 134 134 <ShareOptions 135 135 setShareOption={setShareOption} 136 136 shareOption={shareOption} ··· 139 139 editorStateRef={editorStateRef} 140 140 {...props} 141 141 /> 142 - <hr className="border-border-light " /> 143 - <div className="flex flex-col gap-1"> 142 + <hr className="border-border " /> 143 + <div className="flex flex-col gap-2"> 144 144 <h4>Tags</h4> 145 145 <TagSelector 146 146 selectedTags={currentTags} 147 147 setSelectedTags={handleTagsChange} 148 148 /> 149 149 </div> 150 + <hr className="border-border mb-2" /> 151 + 150 152 <div className="flex justify-between"> 151 153 <Link 152 154 className="hover:no-underline! font-bold"
-1
app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx
··· 213 213 <Interactions 214 214 quotesCount={props.quotesCount || 0} 215 215 commentsCount={props.commentsCount || 0} 216 - compact 217 216 showComments={props.preferences.showComments} 218 217 pageId={props.pageId} 219 218 />
+95 -39
app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx
··· 102 102 export const Interactions = (props: { 103 103 quotesCount: number; 104 104 commentsCount: number; 105 - compact?: boolean; 105 + className?: string; 106 + showComments?: boolean; 107 + pageId?: string; 108 + }) => { 109 + const data = useContext(PostPageContext); 110 + const document_uri = data?.uri; 111 + if (!document_uri) 112 + throw new Error("document_uri not available in PostPageContext"); 113 + 114 + let { drawerOpen, drawer, pageId } = useInteractionState(document_uri); 115 + 116 + const handleQuotePrefetch = () => { 117 + if (data?.quotesAndMentions) { 118 + prefetchQuotesData(data.quotesAndMentions); 119 + } 120 + }; 121 + 122 + const tags = (data?.data as any)?.tags as string[] | undefined; 123 + const tagCount = tags?.length || 0; 124 + return ( 125 + <div className={`flex gap-2 text-tertiary text-sm ${props.className}`}> 126 + {tagCount > 0 && <TagPopover tags={tags} tagCount={tagCount} />} 127 + 128 + {props.quotesCount > 0 && ( 129 + <button 130 + className="flex w-fit gap-2 items-center" 131 + onClick={() => { 132 + if (!drawerOpen || drawer !== "quotes") 133 + openInteractionDrawer("quotes", document_uri, props.pageId); 134 + else setInteractionState(document_uri, { drawerOpen: false }); 135 + }} 136 + onMouseEnter={handleQuotePrefetch} 137 + onTouchStart={handleQuotePrefetch} 138 + aria-label="Post quotes" 139 + > 140 + <QuoteTiny aria-hidden /> {props.quotesCount} 141 + </button> 142 + )} 143 + {props.showComments === false ? null : ( 144 + <button 145 + className="flex gap-2 items-center w-fit" 146 + onClick={() => { 147 + if (!drawerOpen || drawer !== "comments" || pageId !== props.pageId) 148 + openInteractionDrawer("comments", document_uri, props.pageId); 149 + else setInteractionState(document_uri, { drawerOpen: false }); 150 + }} 151 + aria-label="Post comments" 152 + > 153 + <CommentTiny aria-hidden /> {props.commentsCount} 154 + </button> 155 + )} 156 + </div> 157 + ); 158 + }; 159 + 160 + export const ExpandedInteractions = (props: { 161 + quotesCount: number; 162 + commentsCount: number; 106 163 className?: string; 107 164 showComments?: boolean; 108 165 pageId?: string; ··· 120 177 } 121 178 }; 122 179 180 + const tags = (data?.data as any)?.tags as string[] | undefined; 181 + const tagCount = tags?.length || 0; 123 182 return ( 124 183 <div 125 - className={`flex gap-2 text-tertiary ${props.compact ? "text-sm" : "px-3 sm:px-4"} ${props.className}`} 184 + className={`gap-2 text-tertiary px-3 sm:px-4 flex flex-col ${props.className}`} 126 185 > 127 - {props.compact && <TagPopover />} 128 - <button 129 - className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`} 130 - onClick={() => { 131 - if (!drawerOpen || drawer !== "quotes") 132 - openInteractionDrawer("quotes", document_uri, props.pageId); 133 - else setInteractionState(document_uri, { drawerOpen: false }); 134 - }} 135 - onMouseEnter={handleQuotePrefetch} 136 - onTouchStart={handleQuotePrefetch} 137 - aria-label="Post quotes" 138 - > 139 - <QuoteTiny aria-hidden /> {props.quotesCount}{" "} 140 - {!props.compact && ( 186 + {tagCount > 0 && ( 187 + <> 188 + <hr className="border-border-light mb-1 " /> 189 + <TagList tags={tags} /> 190 + <hr className="border-border-light mt-1 " /> 191 + </> 192 + )} 193 + 194 + {props.quotesCount > 0 && ( 195 + <button 196 + className="flex w-fit gap-2 items-center px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline" 197 + onClick={() => { 198 + if (!drawerOpen || drawer !== "quotes") 199 + openInteractionDrawer("quotes", document_uri, props.pageId); 200 + else setInteractionState(document_uri, { drawerOpen: false }); 201 + }} 202 + onMouseEnter={handleQuotePrefetch} 203 + onTouchStart={handleQuotePrefetch} 204 + aria-label="Post quotes" 205 + > 206 + <QuoteTiny aria-hidden /> {props.quotesCount}{" "} 141 207 <span 142 208 aria-hidden 143 209 >{`Quote${props.quotesCount === 1 ? "" : "s"}`}</span> 144 - )} 145 - </button> 210 + </button> 211 + )} 146 212 {props.showComments === false ? null : ( 147 213 <button 148 - className={`flex gap-1 items-center ${!props.compact && "px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline"}`} 214 + className="flex gap-2 items-center w-fit px-1 py-0.5 border border-border-light rounded-lg trasparent-outline selected-outline" 149 215 onClick={() => { 150 216 if (!drawerOpen || drawer !== "comments" || pageId !== props.pageId) 151 217 openInteractionDrawer("comments", document_uri, props.pageId); ··· 154 220 aria-label="Post comments" 155 221 > 156 222 <CommentTiny aria-hidden />{" "} 157 - {props.compact ? ( 158 - props.commentsCount 159 - ) : props.commentsCount > 0 ? ( 223 + {props.commentsCount > 0 ? ( 160 224 <span aria-hidden> 161 225 {`${props.commentsCount} Comment${props.commentsCount === 1 ? "" : "s"}`} 162 226 </span> ··· 165 229 )} 166 230 </button> 167 231 )} 168 - {!props.compact && <TagList />} 169 232 </div> 170 233 ); 171 234 }; 172 235 173 - const TagPopover = () => { 174 - const data = useContext(PostPageContext); 175 - const tags = (data?.data as any)?.tags as string[] | undefined; 176 - const tagCount = tags?.length || 0; 177 - 178 - if (tagCount === 0) return null; 179 - 236 + const TagPopover = (props: { 237 + tagCount: number; 238 + tags: string[] | undefined; 239 + }) => { 180 240 return ( 181 241 <Popover 182 242 className="p-2! max-w-xs" 183 243 trigger={ 184 244 <div className="tags flex gap-1 items-center "> 185 - <TagTiny /> {tagCount} 245 + <TagTiny /> {props.tagCount} 186 246 </div> 187 247 } 188 248 > 189 - <TagList className="text-secondary!" /> 249 + <TagList tags={props.tags} className="text-secondary!" /> 190 250 </Popover> 191 251 ); 192 252 }; 193 253 194 - const TagList = (props: { className?: string }) => { 195 - const data = useContext(PostPageContext); 196 - const tags = (data?.data as any)?.tags as string[] | undefined; 197 - 198 - if (!tags || tags.length === 0) return null; 199 - 254 + const TagList = (props: { className?: string; tags: string[] | undefined }) => { 255 + if (!props.tags) return; 200 256 return ( 201 257 <div className="flex gap-1 flex-wrap"> 202 - {tags.map((tag, index) => ( 258 + {props.tags.map((tag, index) => ( 203 259 <Tag name={tag} key={index} className={props.className} /> 204 260 ))} 205 261 </div>
+2 -2
app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx
··· 11 11 import { SubscribeWithBluesky } from "app/lish/Subscribe"; 12 12 import { EditTiny } from "components/Icons/EditTiny"; 13 13 import { 14 + ExpandedInteractions, 14 15 getCommentCount, 15 16 getQuoteCount, 16 17 Interactions, ··· 84 85 did={did} 85 86 prerenderedCodeBlocks={prerenderedCodeBlocks} 86 87 /> 87 - <Interactions 88 + <ExpandedInteractions 88 89 pageId={pageId} 89 90 showComments={preferences.showComments} 90 91 commentsCount={getCommentCount(document, pageId) || 0} ··· 92 93 /> 93 94 {!isSubpage && ( 94 95 <> 95 - <hr className="border-border-light mb-4 mt-4 sm:mx-4 mx-3" /> 96 96 <div className="sm:px-4 px-3"> 97 97 {identity && 98 98 identity.atp_did ===
+2 -4
app/lish/[did]/[publication]/[rkey]/PostFooter.tsx
··· 5 5 import { useIdentityData } from "components/IdentityProvider"; 6 6 import { PubLeafletComment } from "lexicons/api"; 7 7 import { PostPageData } from "./getPostPageData"; 8 - import { Interactions } from "./Interactions/Interactions"; 8 + import { ExpandedInteractions } from "./Interactions/Interactions"; 9 9 import { decodeQuotePosition } from "./quotePosition"; 10 10 11 11 export const PostFooter = (props: { ··· 18 18 return; 19 19 return ( 20 20 <div className="flex flex-col px-3 sm:px-4"> 21 - <hr className="border-border-light mb-4" /> 22 - <Interactions 21 + <ExpandedInteractions 23 22 showComments={props.preferences.showComments} 24 23 quotesCount={ 25 24 props.data.document_mentions_in_bsky.filter((q) => { ··· 36 35 ).length 37 36 } 38 37 /> 39 - <hr className="border-border-light mb-4 mt-4 " /> 40 38 {identity && 41 39 identity.atp_did === 42 40 props.data.documents_in_publications[0]?.publications?.identity_did ? (
-1
app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx
··· 90 90 </div> 91 91 <Interactions 92 92 showComments={props.preferences.showComments} 93 - compact 94 93 quotesCount={getQuoteCount(document) || 0} 95 94 commentsCount={getCommentCount(document) || 0} 96 95 />