a tool for shared writing and social publishing

add published page block preview

+191 -12
+6 -2
app/lish/[did]/[publication]/[rkey]/Interactions/Quotes.tsx
··· 26 26 }) => { 27 27 let data = useContext(PostPageContext); 28 28 const document_uri = data?.uri; 29 - if (!document_uri) throw new Error('document_uri not available in PostPageContext'); 29 + if (!document_uri) 30 + throw new Error("document_uri not available in PostPageContext"); 30 31 31 32 return ( 32 33 <div className="flex flex-col gap-2"> ··· 34 35 Quotes 35 36 <button 36 37 className="text-tertiary" 37 - onClick={() => setInteractionState(document_uri, { drawerOpen: false })} 38 + onClick={() => 39 + setInteractionState(document_uri, { drawerOpen: false }) 40 + } 38 41 > 39 42 <CloseTiny /> 40 43 </button> ··· 125 128 > 126 129 <div className="italic border border-border-light rounded-md px-2 pt-1"> 127 130 <PostContent 131 + pages={[]} 128 132 bskyPostData={[]} 129 133 blocks={content} 130 134 did={props.did}
+20 -7
app/lish/[did]/[publication]/[rkey]/PostContent.tsx
··· 26 26 import { AppBskyFeedDefs } from "@atproto/api"; 27 27 import { PubBlueskyPostBlock } from "./PublishBskyPostBlock"; 28 28 import { openPage } from "./PostPages"; 29 + import { PageLinkBlock } from "components/Blocks/PageLinkBlock"; 30 + import { PublishedPageLinkBlock } from "./PublishedPageBlock"; 29 31 30 32 export function PostContent({ 31 33 blocks, ··· 35 37 prerenderedCodeBlocks, 36 38 bskyPostData, 37 39 pageId, 40 + pages, 38 41 }: { 39 42 blocks: PubLeafletPagesLinearDocument.Block[]; 40 43 pageId?: string; ··· 43 46 className?: string; 44 47 prerenderedCodeBlocks?: Map<string, string>; 45 48 bskyPostData: AppBskyFeedDefs.PostView[]; 49 + pages: PubLeafletPagesLinearDocument.Main[]; 46 50 }) { 47 51 return ( 48 52 <div ··· 53 57 return ( 54 58 <Block 55 59 pageId={pageId} 60 + pages={pages} 56 61 bskyPostData={bskyPostData} 57 62 block={b} 58 63 did={did} ··· 78 83 prerenderedCodeBlocks, 79 84 bskyPostData, 80 85 pageId, 86 + pages, 81 87 }: { 82 88 pageId?: string; 83 89 preview?: boolean; ··· 85 91 block: PubLeafletPagesLinearDocument.Block; 86 92 did: string; 87 93 isList?: boolean; 94 + pages: PubLeafletPagesLinearDocument.Main[]; 88 95 previousBlock?: PubLeafletPagesLinearDocument.Block; 89 96 prerenderedCodeBlocks?: Map<string, string>; 90 97 bskyPostData: AppBskyFeedDefs.PostView[]; ··· 123 130 switch (true) { 124 131 case PubLeafletBlocksPage.isMain(b.block): { 125 132 let id = b.block.id; 133 + let page = pages.find((p) => p.id === id); 134 + if (!page) return; 126 135 return ( 127 - <div 128 - onClick={() => { 129 - openPage(pageId, id); 130 - }} 131 - > 132 - ITS A BLOCK 133 - </div> 136 + <PublishedPageLinkBlock 137 + blocks={page.blocks} 138 + pageId={id} 139 + parentPageId={pageId} 140 + did={did} 141 + bskyPostData={bskyPostData} 142 + /> 134 143 ); 135 144 } 136 145 case PubLeafletBlocksBskyPost.isMain(b.block): { ··· 159 168 <ul className="-ml-px sm:ml-[9px] pb-2"> 160 169 {b.block.children.map((child, i) => ( 161 170 <ListItem 171 + pages={pages} 162 172 bskyPostData={bskyPostData} 163 173 index={[...index, i]} 164 174 item={child} ··· 316 326 317 327 function ListItem(props: { 318 328 index: number[]; 329 + pages: PubLeafletPagesLinearDocument.Main[]; 319 330 item: PubLeafletBlocksUnorderedList.ListItem; 320 331 did: string; 321 332 className?: string; ··· 325 336 <ul className="-ml-[7px] sm:ml-[7px]"> 326 337 {props.item.children.map((child, index) => ( 327 338 <ListItem 339 + pages={props.pages} 328 340 bskyPostData={props.bskyPostData} 329 341 index={[...props.index, index]} 330 342 item={child} ··· 343 355 /> 344 356 <div className="flex flex-col w-full"> 345 357 <Block 358 + pages={props.pages} 346 359 bskyPostData={props.bskyPostData} 347 360 block={{ block: props.item.content }} 348 361 did={props.did}
+6 -3
app/lish/[did]/[publication]/[rkey]/PostPages.tsx
··· 21 21 import { PageOptionButton } from "components/Pages/PageOptions"; 22 22 import { CloseTiny } from "components/Icons/CloseTiny"; 23 23 import { PageWrapper } from "components/Pages/Page"; 24 + import { Fragment } from "react"; 24 25 export const usePostPageUIState = create(() => ({ 25 26 pages: [] as string[], 26 27 })); ··· 73 74 74 75 let hasPageBackground = !!pubRecord.theme?.showPageBackground; 75 76 let fullPageScroll = !hasPageBackground && !drawerOpen && pages.length === 0; 77 + let record = document.data as PubLeafletDocument.Record; 76 78 return ( 77 79 <> 78 80 {!fullPageScroll && <BookendSpacer />} ··· 88 90 preferences={preferences} 89 91 /> 90 92 <PostContent 93 + pages={record.pages as PubLeafletPagesLinearDocument.Main[]} 91 94 bskyPostData={bskyPostData} 92 95 blocks={blocks} 93 96 did={did} ··· 141 144 )} 142 145 143 146 {pages.map((p) => { 144 - let record = document.data as PubLeafletDocument.Record; 145 147 let page = record.pages.find( 146 148 (page) => (page as PubLeafletPagesLinearDocument.Main).id === p, 147 149 ) as PubLeafletPagesLinearDocument.Main | undefined; 148 150 if (!page) return null; 149 151 return ( 150 - <> 152 + <Fragment key={p}> 151 153 <SandwichSpacer /> 152 154 <PageWrapper 153 155 cardBorderHidden={!hasPageBackground} ··· 161 163 } 162 164 > 163 165 <PostContent 166 + pages={record.pages as PubLeafletPagesLinearDocument.Main[]} 164 167 pageId={page.id} 165 168 bskyPostData={bskyPostData} 166 169 blocks={page.blocks} ··· 168 171 prerenderedCodeBlocks={prerenderedCodeBlocks} 169 172 /> 170 173 </PageWrapper> 171 - </> 174 + </Fragment> 172 175 ); 173 176 })} 174 177 {!fullPageScroll && <BookendSpacer />}
+159
app/lish/[did]/[publication]/[rkey]/PublishedPageBlock.tsx
··· 1 + "use client"; 2 + 3 + import { useEntity, useReplicache } from "src/replicache"; 4 + import { useUIState } from "src/useUIState"; 5 + import { CSSProperties, useContext, useRef } from "react"; 6 + import { useCardBorderHidden } from "components/Pages/useCardBorderHidden"; 7 + import { PostContent } from "./PostContent"; 8 + import { 9 + PubLeafletBlocksHeader, 10 + PubLeafletBlocksText, 11 + PubLeafletPagesLinearDocument, 12 + PubLeafletPublication, 13 + } from "lexicons/api"; 14 + import { AppBskyFeedDefs } from "@atproto/api"; 15 + import { TextBlock } from "./TextBlock"; 16 + import { PostPageContext } from "./PostPageContext"; 17 + import { openPage, usePostPageUIState } from "./PostPages"; 18 + 19 + export function PublishedPageLinkBlock(props: { 20 + blocks: PubLeafletPagesLinearDocument.Block[]; 21 + parentPageId: string | undefined; 22 + pageId: string; 23 + did: string; 24 + preview?: boolean; 25 + className?: string; 26 + prerenderedCodeBlocks?: Map<string, string>; 27 + bskyPostData: AppBskyFeedDefs.PostView[]; 28 + }) { 29 + //switch to use actually state 30 + let isOpen = usePostPageUIState((s) => s.pages.includes(props.pageId)); 31 + return ( 32 + <div 33 + className={`w-full cursor-pointer 34 + pageLinkBlockWrapper relative group/pageLinkBlock 35 + bg-bg-page shadow-sm 36 + flex overflow-clip 37 + block-border 38 + ${isOpen && "!border-tertiary"} 39 + `} 40 + onClick={(e) => { 41 + if (e.isDefaultPrevented()) return; 42 + if (e.shiftKey) return; 43 + e.preventDefault(); 44 + e.stopPropagation(); 45 + 46 + console.log("yoo@"); 47 + 48 + openPage(props.parentPageId, props.pageId); 49 + //open the damn thing somehow 50 + }} 51 + > 52 + <DocLinkBlock {...props} /> 53 + </div> 54 + ); 55 + } 56 + export function DocLinkBlock(props: { 57 + blocks: PubLeafletPagesLinearDocument.Block[]; 58 + pageId?: string; 59 + did: string; 60 + preview?: boolean; 61 + className?: string; 62 + prerenderedCodeBlocks?: Map<string, string>; 63 + bskyPostData: AppBskyFeedDefs.PostView[]; 64 + }) { 65 + let { rep } = useReplicache(); 66 + let [title, description] = props.blocks 67 + .map((b) => b.block) 68 + .filter( 69 + (b) => PubLeafletBlocksText.isMain(b) || PubLeafletBlocksHeader.isMain(b), 70 + ); 71 + 72 + return ( 73 + <div 74 + style={{ "--list-marker-width": "20px" } as CSSProperties} 75 + className={` 76 + w-full h-[104px] 77 + `} 78 + > 79 + <> 80 + <div className="pageLinkBlockContent w-full flex overflow-clip cursor-pointer h-full"> 81 + <div className="my-2 ml-3 grow min-w-0 text-sm bg-transparent overflow-clip "> 82 + {title && ( 83 + <div 84 + className={`pageBlockOne outline-none resize-none align-top flex gap-2 ${title.$type === "pub.leaflet.blocks.header" ? "font-bold text-base" : ""}`} 85 + > 86 + <TextBlock 87 + facets={title.facets} 88 + plaintext={title.plaintext} 89 + index={[]} 90 + preview 91 + /> 92 + </div> 93 + )} 94 + {description && ( 95 + <div 96 + className={`pageBlockLineTwo outline-none resize-none align-top flex gap-2 ${description.$type === "pub.leaflet.blocks.header" ? "font-bold" : ""}`} 97 + > 98 + <TextBlock 99 + facets={description.facets} 100 + plaintext={description.plaintext} 101 + index={[]} 102 + preview 103 + /> 104 + </div> 105 + )} 106 + </div> 107 + {!props.preview && ( 108 + <PagePreview blocks={props.blocks} did={props.did} /> 109 + )} 110 + </div> 111 + </> 112 + </div> 113 + ); 114 + } 115 + 116 + export function PagePreview(props: { 117 + did: string; 118 + blocks: PubLeafletPagesLinearDocument.Block[]; 119 + }) { 120 + let previewRef = useRef<HTMLDivElement | null>(null); 121 + let { rootEntity } = useReplicache(); 122 + let data = useContext(PostPageContext); 123 + let theme = data?.documents_in_publications[0]?.publications 124 + ?.record as PubLeafletPublication.Record; 125 + let pageWidth = `var(--page-width-unitless)`; 126 + let cardBorderHidden = !theme.theme?.showPageBackground; 127 + return ( 128 + <div 129 + ref={previewRef} 130 + className={`pageLinkBlockPreview w-[120px] overflow-clip mx-3 mt-3 -mb-2 border rounded-md shrink-0 border-border-light flex flex-col gap-0.5 rotate-[4deg] origin-center ${cardBorderHidden ? "" : "bg-bg-page"}`} 131 + > 132 + <div 133 + className="absolute top-0 left-0 origin-top-left pointer-events-none " 134 + style={{ 135 + width: `calc(1px * ${pageWidth})`, 136 + height: `calc(100vh - 64px)`, 137 + transform: `scale(calc((120 / ${pageWidth} )))`, 138 + backgroundColor: "rgba(var(--bg-page), var(--bg-page-alpha))", 139 + }} 140 + > 141 + {!cardBorderHidden && ( 142 + <div 143 + className={`pageLinkBlockBackground 144 + absolute top-0 left-0 right-0 bottom-0 145 + pointer-events-none 146 + `} 147 + /> 148 + )} 149 + <PostContent 150 + pages={[]} 151 + did={props.did} 152 + blocks={props.blocks} 153 + preview 154 + bskyPostData={[]} 155 + /> 156 + </div> 157 + </div> 158 + ); 159 + }