forked from
leaflet.pub/leaflet
a tool for shared writing and social publishing
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};