a tool for shared writing and social publishing

add interactions preview for subpages

+106 -32
+8 -2
app/lish/[did]/[publication]/[rkey]/PostPages.tsx
··· 72 72 }, [quote]); 73 73 }; 74 74 75 - export const openPage = (parent: string | undefined, page: string) => { 75 + export const openPage = ( 76 + parent: string | undefined, 77 + page: string, 78 + options?: { scrollIntoView?: boolean } 79 + ) => { 76 80 flushSync(() => { 77 81 usePostPageUIState.setState((state) => { 78 82 let parentPosition = state.pages.findIndex((s) => s == parent); ··· 86 90 }); 87 91 }); 88 92 89 - scrollIntoView(`post-page-${page}`); 93 + if (options?.scrollIntoView !== false) { 94 + scrollIntoView(`post-page-${page}`); 95 + } 90 96 }; 91 97 92 98 export const closePage = (page: string) =>
+98 -30
app/lish/[did]/[publication]/[rkey]/PublishedPageBlock.tsx
··· 8 8 import { 9 9 PubLeafletBlocksHeader, 10 10 PubLeafletBlocksText, 11 + PubLeafletComment, 11 12 PubLeafletPagesLinearDocument, 12 13 PubLeafletPublication, 13 14 } from "lexicons/api"; ··· 15 16 import { TextBlock } from "./TextBlock"; 16 17 import { PostPageContext } from "./PostPageContext"; 17 18 import { openPage, useOpenPages } from "./PostPages"; 19 + import { 20 + openInteractionDrawer, 21 + setInteractionState, 22 + useInteractionState, 23 + } from "./Interactions/Interactions"; 24 + import { CommentTiny } from "components/Icons/CommentTiny"; 25 + import { QuoteTiny } from "components/Icons/QuoteTiny"; 18 26 19 27 export function PublishedPageLinkBlock(props: { 20 28 blocks: PubLeafletPagesLinearDocument.Block[]; ··· 44 52 e.preventDefault(); 45 53 e.stopPropagation(); 46 54 47 - console.log("yoo@"); 48 - 49 55 openPage(props.parentPageId, props.pageId); 50 - //open the damn thing somehow 51 56 }} 52 57 > 53 58 <DocLinkBlock {...props} /> ··· 56 61 } 57 62 export function DocLinkBlock(props: { 58 63 blocks: PubLeafletPagesLinearDocument.Block[]; 59 - pageId?: string; 64 + pageId: string; 65 + parentPageId?: string; 60 66 did: string; 61 67 preview?: boolean; 62 68 className?: string; 63 69 prerenderedCodeBlocks?: Map<string, string>; 64 70 bskyPostData: AppBskyFeedDefs.PostView[]; 65 71 }) { 66 - let { rep } = useReplicache(); 67 72 let [title, description] = props.blocks 68 73 .map((b) => b.block) 69 74 .filter( ··· 79 84 > 80 85 <> 81 86 <div className="pageLinkBlockContent w-full flex overflow-clip cursor-pointer h-full"> 82 - <div className="my-2 ml-3 grow min-w-0 text-sm bg-transparent overflow-clip "> 83 - {title && ( 84 - <div 85 - className={`pageBlockOne outline-none resize-none align-top flex gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold text-base" : ""}`} 86 - > 87 - <TextBlock 88 - facets={title.facets} 89 - plaintext={title.plaintext} 90 - index={[]} 91 - preview 92 - /> 93 - </div> 94 - )} 95 - {description && ( 96 - <div 97 - className={`pageBlockLineTwo outline-none resize-none align-top flex gap-2 ${description.$type === "pub.leaflet.blocks.header" ? "font-bold" : ""}`} 98 - > 99 - <TextBlock 100 - facets={description.facets} 101 - plaintext={description.plaintext} 102 - index={[]} 103 - preview 104 - /> 105 - </div> 106 - )} 87 + <div className="my-2 ml-3 grow min-w-0 text-sm bg-transparent overflow-clip flex flex-col "> 88 + <div className="grow"> 89 + {title && ( 90 + <div 91 + className={`pageBlockOne outline-none resize-none align-top flex gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold text-base" : ""}`} 92 + > 93 + <TextBlock 94 + facets={title.facets} 95 + plaintext={title.plaintext} 96 + index={[]} 97 + preview 98 + /> 99 + </div> 100 + )} 101 + {description && ( 102 + <div 103 + className={`pageBlockLineTwo outline-none resize-none align-top flex gap-2 ${description.$type === "pub.leaflet.blocks.header" ? "font-bold" : ""}`} 104 + > 105 + <TextBlock 106 + facets={description.facets} 107 + plaintext={description.plaintext} 108 + index={[]} 109 + preview 110 + /> 111 + </div> 112 + )} 113 + </div> 114 + 115 + <Interactions 116 + pageId={props.pageId} 117 + parentPageId={props.parentPageId} 118 + /> 107 119 </div> 108 120 {!props.preview && ( 109 121 <PagePreview blocks={props.blocks} did={props.did} /> ··· 158 170 </div> 159 171 ); 160 172 } 173 + 174 + const Interactions = (props: { pageId: string; parentPageId?: string }) => { 175 + const data = useContext(PostPageContext); 176 + const document_uri = data?.uri; 177 + if (!document_uri) 178 + throw new Error("document_uri not available in PostPageContext"); 179 + let comments = data.comments_on_documents.filter( 180 + (c) => (c.record as PubLeafletComment.Record)?.onPage === props.pageId, 181 + ).length; 182 + let quotes = data.document_mentions_in_bsky.filter((q) => 183 + q.link.includes(props.pageId), 184 + ).length; 185 + 186 + let { drawerOpen, drawer, pageId } = useInteractionState(document_uri); 187 + 188 + return ( 189 + <div className={`flex gap-2 text-tertiary text-sm`}> 190 + {quotes > 0 && ( 191 + <button 192 + className={`flex gap-1 items-center`} 193 + onClick={(e) => { 194 + e.preventDefault(); 195 + e.stopPropagation(); 196 + openPage(props.parentPageId, props.pageId, { 197 + scrollIntoView: false, 198 + }); 199 + if (!drawerOpen || drawer !== "quotes") 200 + openInteractionDrawer("quotes", document_uri, props.pageId); 201 + else setInteractionState(document_uri, { drawerOpen: false }); 202 + }} 203 + > 204 + <span className="sr-only">Page quotes</span> 205 + <QuoteTiny aria-hidden /> {quotes}{" "} 206 + </button> 207 + )} 208 + {comments > 0 && ( 209 + <button 210 + className={`flex gap-1 items-center`} 211 + onClick={(e) => { 212 + e.preventDefault(); 213 + e.stopPropagation(); 214 + openPage(props.parentPageId, props.pageId, { 215 + scrollIntoView: false, 216 + }); 217 + if (!drawerOpen || drawer !== "comments" || pageId !== props.pageId) 218 + openInteractionDrawer("comments", document_uri, props.pageId); 219 + else setInteractionState(document_uri, { drawerOpen: false }); 220 + }} 221 + > 222 + <span className="sr-only">Page comments</span> 223 + <CommentTiny aria-hidden /> {comments}{" "} 224 + </button> 225 + )} 226 + </div> 227 + ); 228 + };