diff --git a/frontend/apps/site/src/doc-page.tsx b/frontend/apps/site/src/doc-page.tsx index 65dc2d5c8f..61a7b0b25e 100644 --- a/frontend/apps/site/src/doc-page.tsx +++ b/frontend/apps/site/src/doc-page.tsx @@ -1,72 +1,6 @@ -import { - Account, - HMGroup, - HMPublication, - Publication, - PublicationContent, - PublicationHeading, - UnpackedHypermediaId, - createHmDocLink, - createHmId, - groupDocUrl, - unpackHmId, -} from '@mintter/shared' - -import { - ArrowRight, - Button, - Heading, - PageSection, - Share, - SideSection, - SideSectionTitle, - SizableText, - XStack, - YStack, - useMedia, -} from '@mintter/ui' -import {DehydratedState} from '@tanstack/react-query' -import Head from 'next/head' -import {BasicOGMeta, OGImageMeta} from 'src/head' -import {SitePublicationContentProvider} from 'src/site-embeds' -import {WebTipping} from 'src/web-tipping' -import {ErrorPage} from './error-page' -import Footer from './footer' -import {PublicationMetadata} from './publication-metadata' -import {SiteHead} from './site-head' -import {trpc} from './trpc' +import {createHmId} from '@mintter/shared' import {useRouter} from 'next/router' - -export type PublicationPageProps = { - // documentId: string - // version: string | null - // metadata?: boolean - trpcState: DehydratedState -} - -export type PublicationPageData = { - documentId: string - version?: string - publication?: Publication | null - author?: Account | null - editors: Array<Account | string | null> | null -} -let blockBorderRadius = '$3' - -function OpenInAppLink({url}: {url: string}) { - return ( - <Button - onPress={() => window.open(url, '_blank')} - size="$2" - chromeless - icon={Share} - > - <XStack flex={1} alignItems="center"> - <SizableText size="$2">Open in Mintter app</SizableText> - </XStack> - </Button> - ) -} +import {PublicationPage} from './publication-page' export function DocPage() { const router = useRouter() @@ -75,247 +9,3 @@ export function DocPage() { const docId = createHmId('d', docEid) return <PublicationPage documentId={docId} version={versionId} /> } - -export function PublicationPage({ - pathName, - documentId, - version, - contextGroup, -}: { - pathName?: string - documentId: string - version?: string | null - contextGroup?: HMGroup | null -}) { - const media = useMedia() - - const publication = trpc.publication.get.useQuery({ - documentId: documentId, - versionId: version || '', - }) - - const pub = publication.data?.publication - const pubId = pub?.document?.id ? unpackHmId(pub?.document?.id) : null - const pubVersion = pub?.version - const ogImageUrl = - pubId && pubVersion - ? `/api/content-image/${pubId.type}/${pubId.eid}/${pubVersion}/media.png` - : undefined - if (!pub && !publication.isLoading) { - return ( - <ErrorPage - title="Document not found" - description={`Document Id: ${documentId}`} - > - <SizableText color="$color9">version: {version}</SizableText> - </ErrorPage> - ) - } - return ( - <> - <Head> - <meta name="hypermedia-entity-id" content={pub?.document?.id} /> - <meta name="hypermedia-entity-version" content={pub?.version} /> - <meta name="hypermedia-entity-title" content={pub?.document?.title} /> - <BasicOGMeta title={pub?.document?.title} /> - {ogImageUrl && <OGImageMeta url={ogImageUrl} />} - </Head> - <SiteHead pageTitle={pub?.document?.title} /> - <PageSection.Root> - <PublicationContextSidebar - group={contextGroup} - activePathName={pathName || ''} - display={media.gtLg ? 'inherit' : 'none'} - /> - <PageSection.Content paddingBottom={80}> - {pub ? ( - <> - <SitePublicationContentProvider unpackedId={pubId}> - {pub.document?.title ? ( - <PublicationHeading>{pub.document.title}</PublicationHeading> - ) : null} - <PublicationContent - // paddingHorizontal={0} - // $gtMd={{paddingHorizontal: '$3'}} - // $gtLg={{paddingHorizontal: '$3'}} - publication={pub} - /> - </SitePublicationContentProvider> - </> - ) : publication.isLoading ? ( - <PublicationPlaceholder /> - ) : null} - </PageSection.Content> - <PageSection.Side> - <YStack - $gtLg={{ - marginTop: 80, - }} - > - <PublicationMetadata publication={pub} pathName={pathName} /> - <WebTipping - docId={documentId} - editors={pub?.document?.editors || []} - > - <OpenInAppLink url={createHmDocLink(documentId, pub?.version)} /> - </WebTipping> - </YStack> - </PageSection.Side> - - <PublicationContextSidebar - group={contextGroup} - activePathName={pathName || ''} - display={media.gtLg ? 'none' : 'inherit'} - /> - </PageSection.Root> - <Footer /> - </> - ) -} - -function PublicationPlaceholder() { - return ( - <YStack gap="$6"> - <BlockPlaceholder /> - <BlockPlaceholder /> - <BlockPlaceholder /> - <BlockPlaceholder /> - </YStack> - ) -} - -function BlockPlaceholder() { - return ( - <YStack gap="$3"> - <YStack width="90%" height={16} backgroundColor="$color6" /> - <YStack height={16} backgroundColor="$color6" /> - <YStack width="75%" height={16} backgroundColor="$color6" /> - <YStack width="60%" height={16} backgroundColor="$color6" /> - </YStack> - ) -} - -export function useGroupContentUrl( - groupEid: string | undefined, - groupVersion?: string, - pathName?: string, -) { - const siteInfo = trpc.siteInfo.get.useQuery() - if (!groupEid) return null - const rootPathName = pathName === '/' ? '/' : pathName - return siteInfo.data?.groupEid === groupEid - ? rootPathName - : groupDocUrl(groupEid, groupVersion, pathName || '/') -} - -function GroupSidebarContentItem({ - item, - groupVersion, - groupId, - activePathName, -}: { - item: ContentItem - groupVersion: string | undefined - groupId: UnpackedHypermediaId - activePathName: string -}) { - const contentUrl = useGroupContentUrl( - groupId.eid, - groupVersion, - item.pathName, - ) - if (!contentUrl) return null - return ( - <Button - key={item.pathName} - iconAfter={activePathName === item.pathName ? <ArrowRight /> : null} - tag="a" - href={contentUrl} - size="$3" - chromeless - justifyContent="flex-start" - backgroundColor={ - activePathName === item.pathName ? '$backgroundHover' : 'transparent' - } - hoverStyle={{ - backgroundColor: '$backgroundHover', - }} - > - {item?.publication?.document?.title} - </Button> - ) -} - -type ContentItem = { - publication: null | HMPublication - pathName: string - version: string - docId: UnpackedHypermediaId -} - -function GroupSidebarContent({ - group, - activePathName, - content, -}: { - activePathName: string - group?: HMGroup - content?: Array<null | ContentItem> -}) { - const groupId = group?.id ? unpackHmId(group?.id) : null - return ( - <SideSection - $gtLg={{ - marginTop: 80, - }} - > - {groupId?.eid ? ( - <XStack paddingHorizontal="$3"> - <SideSectionTitle>Site Content:</SideSectionTitle> - </XStack> - ) : null} - {content?.map((item) => { - if (!item || !groupId?.eid) return null - return ( - <GroupSidebarContentItem - key={item.pathName} - item={item} - groupId={groupId} - groupVersion={group?.version} - activePathName={activePathName} - /> - ) - })} - </SideSection> - ) -} - -function PublicationContextSidebar({ - group, - activePathName, - display, - ...props -}: { - group?: HMGroup | null - activePathName: string - display: any -}) { - const groupContent = trpc.group.listContent.useQuery( - { - groupId: group?.id || '', - version: group?.version || '', - }, - {enabled: !!group?.id}, - ) - const groupSidebarContent = group ? ( - <GroupSidebarContent - activePathName={activePathName} - group={group} - content={groupContent.data} - /> - ) : null - - return display != 'none' ? ( - <PageSection.Side {...props}>{groupSidebarContent}</PageSection.Side> - ) : null -} diff --git a/frontend/apps/site/src/group-doc-page.tsx b/frontend/apps/site/src/group-doc-page.tsx index 0e18914851..610cbdb2a1 100644 --- a/frontend/apps/site/src/group-doc-page.tsx +++ b/frontend/apps/site/src/group-doc-page.tsx @@ -1,7 +1,7 @@ import {createHmId} from '@mintter/shared' import {Heading, Spinner} from '@mintter/ui' import {useRouter} from 'next/router' -import {PublicationPage} from 'src/doc-page' +import {PublicationPage} from 'src/publication-page' import {trpc} from 'src/trpc' import {GroupPage} from './group-page' diff --git a/frontend/apps/site/src/group-page.tsx b/frontend/apps/site/src/group-page.tsx index 3c346d3026..05f70c4728 100644 --- a/frontend/apps/site/src/group-page.tsx +++ b/frontend/apps/site/src/group-page.tsx @@ -1,5 +1,6 @@ import {Timestamp} from '@bufbuild/protobuf' import { + HMGroup, HMPublication, PublicationContent, UnpackedHypermediaId, @@ -7,7 +8,6 @@ import { formattedDate, unpackHmId, } from '@mintter/shared' -import {HMGroup} from '@mintter/shared/src/json-hm' import { Button, ButtonText, @@ -26,10 +26,11 @@ import {AccountAvatarLink} from './account-row' import Footer from './footer' import {GroupMetadata} from './group-metadata' import {BasicOGMeta, OGImageMeta} from './head' -import {useGroupContentUrl} from './doc-page' +import {useGroupContentUrl} from './publication-page' import {SitePublicationContentProvider} from './site-embeds' import {SiteHead} from './site-head' import {trpc} from './trpc' +import {MainSiteLayout} from './site-layout' export function GroupPage() { const router = useRouter() @@ -59,15 +60,15 @@ export function GroupPage() { enabled: enabledContentQuery, }, ) - console.log('GroupPage', { - displayVersion, - requestedVersion, - groupId, - q: router.query, - content: groupContent.data, - enabledContent: enabledContentQuery, - group: group.data, - }) + // console.log('GroupPage', { + // displayVersion, + // requestedVersion, + // groupId, + // q: router.query, + // content: groupContent.data, + // enabledContent: enabledContentQuery, + // group: group.data, + // }) const loadedGroup = group.data?.group @@ -120,7 +121,7 @@ export function GroupPage() { ? `/api/content-image/g/${groupEid}/${displayVersion}/media.png` : undefined return ( - <YStack flex={1}> + <> <Head> {loadedGroup ? ( <> @@ -138,34 +139,19 @@ export function GroupPage() { </> ) : null} </Head> - <SiteHead - pageTitle={frontPageItem?.publication?.document?.title || undefined} - /> - <PageSection.Root> - <PageSection.Side /> - <PageSection.Content> - <YStack - // paddingHorizontal="$3" - // $gtMd={{paddingHorizontal: '$4'}} - // gap="$2" - // alignItems="baseline" - > - {mainView} - </YStack> - </PageSection.Content> - <PageSection.Side> - <YStack - className="publication-sidenav-sticky" - $gtLg={{ - marginTop: 80, - }} - > - <GroupMetadata group={group.data?.group} groupId={groupId} /> - </YStack> - </PageSection.Side> - </PageSection.Root> - <Footer /> - </YStack> + <MainSiteLayout + head={ + <SiteHead + pageTitle={frontPageItem?.publication?.document?.title || undefined} + /> + } + rightSide={ + <GroupMetadata group={group.data?.group} groupId={groupId} /> + } + > + <YStack>{mainView}</YStack> + </MainSiteLayout> + </> ) } @@ -301,6 +287,7 @@ function ListviewWrapper({children}: {children: ReactNode}) { gap="$2" alignItems="baseline" marginTop="$5" + paddingBottom="$4" > {children} </YStack> diff --git a/frontend/apps/site/src/publication-page.tsx b/frontend/apps/site/src/publication-page.tsx index 72c7d6ee17..0106d07b58 100644 --- a/frontend/apps/site/src/publication-page.tsx +++ b/frontend/apps/site/src/publication-page.tsx @@ -5,7 +5,6 @@ import { Publication, PublicationContent, PublicationHeading, - UnpackedDocId, UnpackedHypermediaId, createHmDocLink, groupDocUrl, @@ -15,8 +14,6 @@ import { import { ArrowRight, Button, - Heading, - PageSection, Share, SideSection, SideSectionTitle, @@ -31,9 +28,9 @@ import {BasicOGMeta, OGImageMeta} from 'src/head' import {SitePublicationContentProvider} from 'src/site-embeds' import {WebTipping} from 'src/web-tipping' import {ErrorPage} from './error-page' -import Footer from './footer' import {PublicationMetadata} from './publication-metadata' import {SiteHead} from './site-head' +import {MainSiteLayout} from './site-layout' import {trpc} from './trpc' export type PublicationPageProps = { @@ -50,24 +47,8 @@ export type PublicationPageData = { author?: Account | null editors: Array<Account | string | null> | null } -let blockBorderRadius = '$3' -function OpenInAppLink({url}: {url: string}) { - return ( - <Button - onPress={() => window.open(url, '_blank')} - size="$2" - chromeless - icon={Share} - > - <XStack flex={1} alignItems="center"> - <SizableText size="$2">Open in Mintter app</SizableText> - </XStack> - </Button> - ) -} - -export default function PublicationPage({ +export function PublicationPage({ pathName, documentId, version, @@ -78,8 +59,6 @@ export default function PublicationPage({ version?: string | null contextGroup?: HMGroup | null }) { - const media = useMedia() - const publication = trpc.publication.get.useQuery({ documentId: documentId, versionId: version || '', @@ -111,64 +90,67 @@ export default function PublicationPage({ <BasicOGMeta title={pub?.document?.title} /> {ogImageUrl && <OGImageMeta url={ogImageUrl} />} </Head> - <SiteHead pageTitle={pub?.document?.title} /> - <PageSection.Root> - <PublicationContextSidebar - group={contextGroup} - activePathName={pathName || ''} - // display={media.gtLg ? 'inherit' : 'none'} - // display="none" - // $gtLg={{display: 'inherit'}} - /> - <PageSection.Content paddingBottom={80}> - {pub ? ( - <> - <SitePublicationContentProvider unpackedId={pubId}> - {pub.document?.title ? ( - <PublicationHeading>{pub.document.title}</PublicationHeading> - ) : null} - <PublicationContent - // paddingHorizontal={0} - // $gtMd={{paddingHorizontal: '$3'}} - // $gtLg={{paddingHorizontal: '$3'}} - publication={pub} + <MainSiteLayout + head={<SiteHead pageTitle={pub?.document?.title} />} + leftSide={ + <PublicationContextSide + group={contextGroup} + activePathName={pathName || ''} + /> + } + rightSide={ + <> + <YStack> + <PublicationMetadata publication={pub} pathName={pathName} /> + <WebTipping + docId={documentId} + editors={pub?.document?.editors || []} + > + <OpenInAppLink + url={createHmDocLink(documentId, pub?.version)} /> - </SitePublicationContentProvider> - </> - ) : publication.isLoading ? ( - <PublicationPlaceholder /> - ) : null} - </PageSection.Content> - <PageSection.Side> - <YStack - $gtLg={{ - marginTop: 80, - }} - > - <PublicationMetadata publication={pub} pathName={pathName} /> - <WebTipping - docId={documentId} - editors={pub?.document?.editors || []} - > - <OpenInAppLink url={createHmDocLink(documentId, pub?.version)} /> - </WebTipping> - </YStack> - </PageSection.Side> - - <PublicationContextSidebar - group={contextGroup} - activePathName={pathName || ''} - // display={media.gtLg ? 'none' : 'inherit'} - // display="inherit" - // $gtLg={{display: 'none'}} - // display="none" - /> - </PageSection.Root> - <Footer /> + </WebTipping> + </YStack> + </> + } + > + {pub ? ( + <> + <SitePublicationContentProvider unpackedId={pubId}> + {pub.document?.title ? ( + <PublicationHeading>{pub.document.title}</PublicationHeading> + ) : null} + <PublicationContent + // paddingHorizontal={0} + // $gtMd={{paddingHorizontal: '$3'}} + // $gtLg={{paddingHorizontal: '$3'}} + publication={pub} + /> + </SitePublicationContentProvider> + </> + ) : publication.isLoading ? ( + <PublicationPlaceholder /> + ) : null} + </MainSiteLayout> </> ) } +function OpenInAppLink({url}: {url: string}) { + return ( + <Button + onPress={() => window.open(url, '_blank')} + size="$2" + chromeless + icon={Share} + > + <XStack flex={1} alignItems="center"> + <SizableText size="$2">Open in Mintter app</SizableText> + </XStack> + </Button> + ) +} + function PublicationPlaceholder() { return ( <YStack gap="$6"> @@ -246,7 +228,7 @@ type ContentItem = { publication: null | HMPublication pathName: string version: string - docId: UnpackedDocId + docId: UnpackedHypermediaId } function GroupSidebarContent({ @@ -260,16 +242,8 @@ function GroupSidebarContent({ }) { const groupId = group?.id ? unpackHmId(group?.id) : null return ( - <SideSection - $gtLg={{ - marginTop: 80, - }} - > - {groupId?.eid ? ( - <XStack paddingHorizontal="$3"> - <SideSectionTitle>Site Content:</SideSectionTitle> - </XStack> - ) : null} + <SideSection> + {groupId?.eid ? <SideSectionTitle>Site Content:</SideSectionTitle> : null} {content?.map((item) => { if (!item || !groupId?.eid) return null return ( @@ -286,15 +260,13 @@ function GroupSidebarContent({ ) } -function PublicationContextSidebar({ +function PublicationContextSide({ group, activePathName, - display, ...props }: { group?: HMGroup | null activePathName: string - display: any }) { const groupContent = trpc.group.listContent.useQuery( { @@ -310,8 +282,6 @@ function PublicationContextSidebar({ content={groupContent.data} /> ) : null + return groupSidebarContent - // return display != 'none' ? ( - // <PageSection.Side {...props}>{groupSidebarContent}</PageSection.Side> - // ) : null } diff --git a/frontend/apps/site/src/site-head.tsx b/frontend/apps/site/src/site-head.tsx index 95d838b6e9..c5f4ac7c95 100644 --- a/frontend/apps/site/src/site-head.tsx +++ b/frontend/apps/site/src/site-head.tsx @@ -12,8 +12,7 @@ export function SiteHead({pageTitle}: {pageTitle?: string}) { : 'Hypermedia Site (coming soon)' return ( <PageSection.Root - // this prevents the head to be stretched as the other content in the page. - flex="none" + // this prevents the head to be stretched as the other content in the page. > <Head> <title>{pageTitle}</title> diff --git a/frontend/apps/site/src/site-layout.tsx b/frontend/apps/site/src/site-layout.tsx new file mode 100644 index 0000000000..e1e5d7189c --- /dev/null +++ b/frontend/apps/site/src/site-layout.tsx @@ -0,0 +1,46 @@ +import {PageSection, View, YStack} from '@mintter/ui' +import {ReactNode} from 'react' +import Footer from './footer' + +export function MainSiteLayout({ + head, + children, + leftSide, + rightSide, +}: { + head: ReactNode + children: ReactNode + leftSide?: ReactNode + rightSide?: ReactNode +}) { + return ( + <YStack flex={1}> + {head} + <PageSection.Root f={1}> + <PageSection.Side> + <YStack + paddingTop={96} + display="none" + // for some reason the layout breaks if this style is applied directly to PageSection.Side + $gtLg={{display: 'flex'}} + > + {leftSide} + </YStack> + </PageSection.Side> + <PageSection.Content>{children}</PageSection.Content> + <PageSection.Side> + <YStack + paddingTop={0} + // for some reason the layout breaks if this style is applied directly to PageSection.Side + $gtLg={{paddingTop: 96}} + ></YStack> + {rightSide} + </PageSection.Side> + </PageSection.Root> + <PageSection.Side display="inherit" $gtLg={{display: 'none'}}> + {leftSide} + </PageSection.Side> + <Footer /> + </YStack> + ) +} diff --git a/frontend/packages/shared/src/hm-documents.ts b/frontend/packages/shared/src/hm-documents.ts index 72a77f4d2e..78fbbe1f93 100644 --- a/frontend/packages/shared/src/hm-documents.ts +++ b/frontend/packages/shared/src/hm-documents.ts @@ -144,6 +144,15 @@ export type HMDocument = { publishTime?: HMTimestamp } +export type HMGroup = { + id?: string + title?: string + description?: string + ownerAccountId?: string + createTime?: HMTimestamp + version?: string +} + export type HMPublication = { document?: HMDocument version?: string diff --git a/frontend/packages/shared/src/json-hm.ts b/frontend/packages/shared/src/json-hm.ts index 432237715e..49d7f8e5ca 100644 --- a/frontend/packages/shared/src/json-hm.ts +++ b/frontend/packages/shared/src/json-hm.ts @@ -1,18 +1,13 @@ import type { Account, - Annotation, - Block, - BlockNode, ChangeInfo, Device, - Document, - Group, + Group_SiteInfo, + HMDocument, HMTimestamp, MttLink, Profile, Publication, - Group_SiteInfo, - HMDocument, } from '@mintter/shared' export type ServerChangeInfo = ChangeInfo @@ -57,16 +52,6 @@ export type HMAccount = { devices?: {[key: string]: HMDevice} } -export type ServerGroup = Group -export type HMGroup = { - id?: string - title?: string - description?: string - ownerAccountId?: string - createTime?: HMTimestamp - version?: string -} - export type ServerLink = MttLink export type HMLink = { target?: { diff --git a/frontend/packages/shared/src/publication-content.tsx b/frontend/packages/shared/src/publication-content.tsx index 55df2d66e1..64416c09f7 100644 --- a/frontend/packages/shared/src/publication-content.tsx +++ b/frontend/packages/shared/src/publication-content.tsx @@ -372,9 +372,12 @@ export function BlockNodeContent({ backgroundColor={isHovering ? '$background' : 'transparent'} borderRadius={layoutUnit / 4} // flexDirection="row-reverse" - $gtMd={{ - right: isEmbed ? layoutUnit * -2.5 : layoutUnit * -2, - }} + $gtMd={ + { + // disabled because it intersects with the sidebar at narrow screen widths: + // right: isEmbed ? layoutUnit * -1.5 : layoutUnit * -1.5, + } + } > {citations?.length ? ( <Button @@ -393,7 +396,7 @@ export function BlockNodeContent({ {onCopyBlock ? ( <Tooltip content="Copy block reference" delay={800}> <Button - size="$1" + size="$2" opacity={isHovering ? 1 : 0} padding={layoutUnit / 4} borderRadius={layoutUnit / 4} diff --git a/frontend/packages/ui/src/page-components.tsx b/frontend/packages/ui/src/page-components.tsx index 780d45e2a6..fb58c716d8 100644 --- a/frontend/packages/ui/src/page-components.tsx +++ b/frontend/packages/ui/src/page-components.tsx @@ -74,7 +74,6 @@ export const SideContainer = styled(YStack, { }) const PageSectionRoot = styled(Stack, { - flex: 1, position: 'relative', flexDirection: 'column', width: '100%', @@ -86,18 +85,7 @@ const PageSectionRoot = styled(Stack, { }) const PageSectionContent = styled(YStack, { - variants: { - show: { - true: { - display: 'inherit', - }, - false: { - display: 'none', - }, - }, - }, alignSelf: 'stretch', - width: '100%', flex: 1, maxWidth: 720, @@ -109,13 +97,6 @@ const PageSectionContent = styled(YStack, { }) const PageSectionSide = styled(YStack, { - variants: { - show: { - true: { - padding: '$2', - }, - }, - }, maxWidth: 720, alignSelf: 'center', width: '100%',