"use client"; import React, { JSX, useState } from "react"; import { useUIState } from "src/useUIState"; import { useEntitySetContext } from "../EntitySetProvider"; import { useSearchParams } from "next/navigation"; import { focusBlock } from "src/utils/focusBlock"; import { elementId } from "src/utils/elementId"; import { Replicache } from "replicache"; import { Fact, ReplicacheMutators, useEntity, useReferenceToEntity, useReplicache, } from "src/replicache"; import { Media } from "../Media"; import { DesktopPageFooter } from "../DesktopFooter"; import { ThemePopover } from "../ThemeManager/ThemeSetter"; import { Canvas } from "../Canvas"; import { DraftPostOptions } from "../Blocks/MailboxBlock"; import { Blocks } from "components/Blocks"; import { MenuItem, Menu } from "../Layout"; import { scanIndex } from "src/replicache/utils"; import { PageThemeSetter } from "../ThemeManager/PageThemeSetter"; import { CardThemeProvider } from "../ThemeManager/ThemeProvider"; import { PageShareMenu } from "./PageShareMenu"; import { scrollIntoViewIfNeeded } from "src/utils/scrollIntoViewIfNeeded"; import { useUndoState } from "src/undoManager"; import { CloseTiny } from "components/Icons/CloseTiny"; import { MoreOptionsTiny } from "components/Icons/MoreOptionsTiny"; import { PaintSmall } from "components/Icons/PaintSmall"; import { ShareSmall } from "components/Icons/ShareSmall"; import { PublicationMetadata } from "./PublicationMetadata"; import { useCardBorderHidden } from "./useCardBorderHidden"; import { useLeafletPublicationData } from "components/PageSWRDataProvider"; export function Pages(props: { rootPage: string }) { let rootPage = useEntity(props.rootPage, "root/page")[0]; let pages = useUIState((s) => s.openPages); let params = useSearchParams(); let queryRoot = params.get("page"); let firstPage = queryRoot || rootPage?.data.value || props.rootPage; return ( <>
{pages.map((page) => (
))}
{ e.currentTarget === e.target && blurPage(); }} /> ); } export const LeafletOptions = (props: { entityID: string }) => { return ( <> ); }; function Page(props: { entityID: string; first?: boolean }) { let { rep, rootEntity } = useReplicache(); let isDraft = useReferenceToEntity("mailbox/draft", props.entityID); let isFocused = useUIState((s) => { let focusedElement = s.focusedEntity; let focusedPageID = focusedElement?.entityType === "page" ? focusedElement.entityID : focusedElement?.parent; return focusedPageID === props.entityID; }); let pageType = useEntity(props.entityID, "page/type")?.data.value || "doc"; let cardBorderHidden = useCardBorderHidden(props.entityID); return ( <> {!props.first && (
{ e.currentTarget === e.target && blurPage(); }} /> )}
{ if (e.defaultPrevented) return; if (rep) { if (isFocused) return; focusPage(props.entityID, rep); } }} id={elementId.page(props.entityID).container} style={{ width: pageType === "doc" ? "var(--page-width-units)" : undefined, backgroundColor: cardBorderHidden ? "" : "rgba(var(--bg-page), var(--bg-page-alpha))", }} className={` ${pageType === "canvas" ? "!lg:max-w-[1152px]" : "max-w-(--page-width-units)"} page grow flex flex-col overscroll-y-none overflow-y-auto ${cardBorderHidden ? "border-0 shadow-none! sm:-mt-6 sm:-mb-12 -mt-2 -mb-1 pt-3 " : "border rounded-lg"} ${isFocused ? "shadow-md border-border" : "border-border-light"} `} > {isDraft.length > 0 && (
)}
{isFocused && ( )}
); } const PageContent = (props: { entityID: string }) => { let pageType = useEntity(props.entityID, "page/type")?.data.value || "doc"; if (pageType === "doc") return ; return ; }; const DocContent = (props: { entityID: string }) => { let { rootEntity } = useReplicache(); let isFocused = useUIState((s) => { let focusedElement = s.focusedEntity; let focusedPageID = focusedElement?.entityType === "page" ? focusedElement.entityID : focusedElement?.parent; return focusedPageID === props.entityID; }); let cardBorderHidden = useCardBorderHidden(props.entityID); let rootBackgroundImage = useEntity( rootEntity, "theme/card-background-image", ); let rootBackgroundRepeat = useEntity( rootEntity, "theme/card-background-image-repeat", ); let rootBackgroundOpacity = useEntity( rootEntity, "theme/card-background-image-opacity", ); let cardBackgroundImage = useEntity( props.entityID, "theme/card-background-image", ); let cardBackgroundImageRepeat = useEntity( props.entityID, "theme/card-background-image-repeat", ); let cardBackgroundImageOpacity = useEntity( props.entityID, "theme/card-background-image-opacity", ); let backgroundImage = cardBackgroundImage || rootBackgroundImage; let backgroundImageRepeat = cardBackgroundImage ? cardBackgroundImageRepeat?.data?.value : rootBackgroundRepeat?.data.value; let backgroundImageOpacity = cardBackgroundImage ? cardBackgroundImageOpacity?.data.value : rootBackgroundOpacity?.data.value || 1; return ( <> {!cardBorderHidden ? (
) : null} {/* we handle page bg in this sepate div so that we can apply an opacity the background image without affecting the opacity of the rest of the page */} ); }; const PageOptionButton = ({ children, secondary, cardBorderHidden, className, disabled, ...props }: { children: React.ReactNode; secondary?: boolean; cardBorderHidden: boolean | undefined; className?: string; disabled?: boolean; } & Omit) => { return ( ); }; const PageOptions = (props: { entityID: string; first: boolean | undefined; }) => { let { rootEntity } = useReplicache(); let cardBorderHidden = useCardBorderHidden(props.entityID); return (
{!props.first && ( { useUIState.getState().closePage(props.entityID); }} > )}
); }; const UndoButtons = (props: { cardBorderHidden: boolean | undefined }) => { let undoState = useUndoState(); let { undoManager } = useReplicache(); return ( {undoState.canUndo && (
undoManager.undo()} > undoManager.undo()} disabled={!undoState.canRedo} >
)}
); }; const OptionsMenu = (props: { entityID: string; first: boolean; cardBorderHidden: boolean | undefined; }) => { let [state, setState] = useState<"normal" | "theme" | "share">("normal"); let { permissions } = useEntitySetContext(); if (!permissions.write) return null; let { data: pub, mutate } = useLeafletPublicationData(); if (pub && props.first) return; return ( { if (!open) setState("normal"); }} trigger={ } > {state === "normal" ? ( <> {!props.first && ( { e.preventDefault(); setState("share"); }} > Share Page )} {!pub && ( { e.preventDefault(); setState("theme"); }} > Theme Page )} ) : state === "theme" ? ( ) : state === "share" ? ( ) : null} ); }; export async function focusPage( pageID: string, rep: Replicache, focusFirstBlock?: "focusFirstBlock", ) { // if this page is already focused, let focusedBlock = useUIState.getState().focusedEntity; // else set this page as focused useUIState.setState(() => ({ focusedEntity: { entityType: "page", entityID: pageID, }, })); setTimeout(async () => { //scroll to page scrollIntoViewIfNeeded( document.getElementById(elementId.page(pageID).container), false, "smooth", ); // if we asked that the function focus the first block, focus the first block if (focusFirstBlock === "focusFirstBlock") { let firstBlock = await rep.query(async (tx) => { let type = await scanIndex(tx).eav(pageID, "page/type"); let blocks = await scanIndex(tx).eav( pageID, type[0]?.data.value === "canvas" ? "canvas/block" : "card/block", ); let firstBlock = blocks[0]; if (!firstBlock) { return null; } let blockType = ( await tx .scan< Fact<"block/type"> >({ indexName: "eav", prefix: `${firstBlock.data.value}-block/type` }) .toArray() )[0]; if (!blockType) return null; return { value: firstBlock.data.value, type: blockType.data.value, parent: firstBlock.entity, position: firstBlock.data.position, }; }); if (firstBlock) { setTimeout(() => { focusBlock(firstBlock, { type: "start" }); }, 500); } } }, 50); } const blurPage = () => { useUIState.setState(() => ({ focusedEntity: null, selectedBlocks: [], })); }; const UndoTiny = () => { return ( ); }; const RedoTiny = () => { return ( ); };