a tool for shared writing and social publishing
at debug/datetime 136 lines 3.8 kB view raw
1"use client"; 2 3import React from "react"; 4import { useUIState } from "src/useUIState"; 5import { useSearchParams } from "next/navigation"; 6 7import { focusBlock } from "src/utils/focusBlock"; 8import { elementId } from "src/utils/elementId"; 9 10import { Replicache } from "replicache"; 11import { Fact, ReplicacheMutators, useEntity } from "src/replicache"; 12 13import { scanIndex } from "src/replicache/utils"; 14import { CardThemeProvider } from "../ThemeManager/ThemeProvider"; 15import { scrollIntoViewIfNeeded } from "src/utils/scrollIntoViewIfNeeded"; 16import { useCardBorderHidden } from "./useCardBorderHidden"; 17import { BookendSpacer, SandwichSpacer } from "components/LeafletLayout"; 18import { LeafletSidebar } from "app/[leaflet_id]/Sidebar"; 19import { Page } from "./Page"; 20 21export function Pages(props: { rootPage: string }) { 22 let rootPage = useEntity(props.rootPage, "root/page")[0]; 23 let pages = useUIState((s) => s.openPages); 24 let params = useSearchParams(); 25 let queryRoot = params.get("page"); 26 let firstPage = queryRoot || rootPage?.data.value || props.rootPage; 27 let cardBorderHidden = useCardBorderHidden(rootPage.id); 28 let firstPageIsCanvas = useEntity(firstPage, "page/type"); 29 let fullPageScroll = 30 !!cardBorderHidden && pages.length === 0 && !firstPageIsCanvas; 31 32 return ( 33 <> 34 <LeafletSidebar /> 35 {!fullPageScroll && ( 36 <BookendSpacer 37 onClick={(e) => { 38 e.currentTarget === e.target && blurPage(); 39 }} 40 /> 41 )} 42 43 <Page entityID={firstPage} first fullPageScroll={fullPageScroll} /> 44 {pages.map((page) => ( 45 <React.Fragment key={page}> 46 <SandwichSpacer 47 onClick={(e) => { 48 e.currentTarget === e.target && blurPage(); 49 }} 50 /> 51 <Page entityID={page} fullPageScroll={false} /> 52 </React.Fragment> 53 ))} 54 {!fullPageScroll && ( 55 <BookendSpacer 56 onClick={(e) => { 57 e.currentTarget === e.target && blurPage(); 58 }} 59 /> 60 )} 61 </> 62 ); 63} 64 65export async function focusPage( 66 pageID: string, 67 rep: Replicache<ReplicacheMutators>, 68 focusFirstBlock?: "focusFirstBlock", 69) { 70 // if this page is already focused, 71 let focusedBlock = useUIState.getState().focusedEntity; 72 // else set this page as focused 73 useUIState.setState(() => ({ 74 focusedEntity: { 75 entityType: "page", 76 entityID: pageID, 77 }, 78 })); 79 80 setTimeout(async () => { 81 //scroll to page 82 83 scrollIntoViewIfNeeded( 84 document.getElementById(elementId.page(pageID).container), 85 false, 86 "smooth", 87 ); 88 89 // if we asked that the function focus the first block, focus the first block 90 if (focusFirstBlock === "focusFirstBlock") { 91 let firstBlock = await rep.query(async (tx) => { 92 let type = await scanIndex(tx).eav(pageID, "page/type"); 93 let blocks = await scanIndex(tx).eav( 94 pageID, 95 type[0]?.data.value === "canvas" ? "canvas/block" : "card/block", 96 ); 97 98 let firstBlock = blocks[0]; 99 100 if (!firstBlock) { 101 return null; 102 } 103 104 let blockType = ( 105 await tx 106 .scan< 107 Fact<"block/type"> 108 >({ indexName: "eav", prefix: `${firstBlock.data.value}-block/type` }) 109 .toArray() 110 )[0]; 111 112 if (!blockType) return null; 113 114 return { 115 value: firstBlock.data.value, 116 type: blockType.data.value, 117 parent: firstBlock.entity, 118 position: firstBlock.data.position, 119 }; 120 }); 121 122 if (firstBlock) { 123 setTimeout(() => { 124 focusBlock(firstBlock, { type: "start" }); 125 }, 500); 126 } 127 } 128 }, 50); 129} 130 131export const blurPage = () => { 132 useUIState.setState(() => ({ 133 focusedEntity: null, 134 selectedBlocks: [], 135 })); 136};