diff --git a/components/NotionPage.tsx b/components/NotionPage.tsx index 51759a9..38cedd4 100644 --- a/components/NotionPage.tsx +++ b/components/NotionPage.tsx @@ -15,12 +15,7 @@ import { Tweet, TwitterContextProvider } from 'react-static-tweets' import { NotionRenderer } from 'react-notion-x' // utils -import { - getBlockTitle, - getBlockIcon, - getPageProperty, - isUrl -} from 'notion-utils' +import { getBlockTitle, getPageProperty } from 'notion-utils' import { mapPageUrl, getCanonicalPageUrl } from 'lib/map-page-url' import { mapImageUrl } from 'lib/map-image-url' import { getPageTweet } from 'lib/get-page-tweet' @@ -134,36 +129,10 @@ export const NotionPage: React.FC = ({ block ) - const socialImageCoverPosition = - (block as PageBlock).format?.page_cover_position ?? - config.defaultPageCoverPosition - const socialImageObjectPosition = socialImageCoverPosition - ? `center ${(1 - socialImageCoverPosition) * 100}%` - : null - - const blockIcon = getBlockIcon(block, recordMap) - const socialAuthorImage = mapImageUrl( - blockIcon && isUrl(blockIcon) ? blockIcon : config.defaultPageIcon, - block - ) - - const socialAuthor = - getPageProperty('Author', block, recordMap) || config.author - const socialDescription = getPageProperty('Description', block, recordMap) || config.description - const timePublished = getPageProperty('Published', block, recordMap) - const datePublished = timePublished ? new Date(timePublished) : undefined - const socialDate = - isBlogPost && datePublished - ? `${datePublished.toLocaleString('en-US', { - month: 'long' - })} ${datePublished.getFullYear()}` - : undefined - const socialDetail = socialDate || site.domain - let pageAside: React.ReactNode = null // only display comments and page actions on blog post pages @@ -187,14 +156,11 @@ export const NotionPage: React.FC = ({ }} > diff --git a/components/PageHead.tsx b/components/PageHead.tsx index ff57369..1f9eeb1 100644 --- a/components/PageHead.tsx +++ b/components/PageHead.tsx @@ -10,34 +10,13 @@ export const PageHead: React.FC< title?: string description?: string image?: string - imageObjectPosition?: string - author?: string - authorImage?: string - detail?: string url?: string } -> = ({ - site, - title, - description, - image, - imageObjectPosition, - author, - authorImage, - detail, - url -}) => { +> = ({ site, title, description, pageId, image, url }) => { title = title ?? site?.name description = description ?? site?.description - const socialImageUrl = getSocialImageUrl({ - title, - image, - imageObjectPosition, - author, - authorImage, - detail - }) + const socialImageUrl = getSocialImageUrl(pageId) || image return ( diff --git a/lib/get-social-image-url.ts b/lib/get-social-image-url.ts index f2ab9d0..3ee20da 100644 --- a/lib/get-social-image-url.ts +++ b/lib/get-social-image-url.ts @@ -1,45 +1,16 @@ import { api, host } from './config' -export function getSocialImageUrl({ - title, - image, - imageObjectPosition, - author, - authorImage, - detail -}: { - title: string - image?: string - imageObjectPosition?: string - author?: string - authorImage?: string - detail?: string -}) { - const url = new URL(api.getSocialImage, host) +export function getSocialImageUrl(pageId: string) { + try { + const url = new URL(api.getSocialImage, host) - if (title) { - url.searchParams.set('title', title) + if (pageId) { + url.searchParams.set('id', pageId) + return url.toString() + } + } catch (err) { + console.warn('error invalid social image url', pageId, err.message) } - if (image) { - url.searchParams.set('image', image) - } - - if (imageObjectPosition) { - url.searchParams.set('imageObjectPosition', imageObjectPosition) - } - - if (author) { - url.searchParams.set('author', author) - } - - if (authorImage) { - url.searchParams.set('authorImage', authorImage) - } - - if (detail) { - url.searchParams.set('detail', detail) - } - - return url.toString() + return null } diff --git a/lib/preview-images.ts b/lib/preview-images.ts index cccbf89..a8b36aa 100644 --- a/lib/preview-images.ts +++ b/lib/preview-images.ts @@ -9,12 +9,6 @@ import { defaultPageIcon, defaultPageCover } from './config' import { db } from './db' import { mapImageUrl } from './map-image-url' -// NOTE: this is just an example of how to pre-compute preview images. -// Depending on how many images you're working with, this can potentially be -// very expensive to recompute, so in production we recommend that you cache -// the preview image results in a key-value database of your choosing. -// If you're not sure where to start, check out https://github.com/jaredwray/keyv - export async function getPreviewImageMap( recordMap: ExtendedRecordMap ): Promise { diff --git a/pages/api/social-image.tsx b/pages/api/social-image.tsx index a3e2d21..ac0cf47 100644 --- a/pages/api/social-image.tsx +++ b/pages/api/social-image.tsx @@ -1,6 +1,20 @@ import React from 'react' import { withOGImage } from 'next-api-og-image' -import { interRegular } from '../../lib/fonts' + +import { + getBlockTitle, + getBlockIcon, + getPageProperty, + isUrl, + parsePageId +} from 'notion-utils' +import { PageBlock } from 'notion-types' + +import { notion } from 'lib/notion' +import { getSiteForDomain } from 'lib/get-site-for-domain' +import { mapImageUrl } from 'lib/map-image-url' +import * as config from 'lib/config' +import { interRegular } from 'lib/fonts' /** * Social image generation via headless chrome. @@ -12,24 +26,69 @@ import { interRegular } from '../../lib/fonts' */ const debugInspectHtml = false -export default withOGImage< - 'query', - | 'title' - | 'image' - | 'author' - | 'authorImage' - | 'detail' - | 'imageObjectPosition' ->({ +export default withOGImage<'query', 'id'>({ template: { - react: ({ - title, - image, - author, - authorImage, - detail, - imageObjectPosition - }) => { + react: async ({ id }) => { + const pageId = parsePageId(id) + + if (!pageId) { + throw new Error('Invalid notion page id') + } + + const site = await getSiteForDomain(config.domain) + const recordMap = await notion.getPage(pageId) + + const keys = Object.keys(recordMap?.block || {}) + const block = recordMap?.block?.[keys[0]]?.value + + if (!block) { + throw new Error('Invalid recordMap for page') + } + + const isBlogPost = + block.type === 'page' && block.parent_table === 'collection' + const title = getBlockTitle(block, recordMap) || site.name + const image = mapImageUrl( + getPageProperty('Social Image', block, recordMap) || + (block as PageBlock).format?.page_cover || + config.defaultPageCover, + block + ) + + const imageCoverPosition = + (block as PageBlock).format?.page_cover_position ?? + config.defaultPageCoverPosition + const imageObjectPosition = imageCoverPosition + ? `center ${(1 - imageCoverPosition) * 100}%` + : null + + const blockIcon = getBlockIcon(block, recordMap) + const authorImage = mapImageUrl( + blockIcon && isUrl(blockIcon) ? blockIcon : config.defaultPageIcon, + block + ) + + const author = + getPageProperty('Author', block, recordMap) || config.author + + // const socialDescription = + // getPageProperty('Description', block, recordMap) || + // config.description + + const timePublished = getPageProperty( + 'Published', + block, + recordMap + ) + const datePublished = timePublished ? new Date(timePublished) : undefined + const date = + isBlogPost && datePublished + ? `${datePublished.toLocaleString('en-US', { + month: 'long' + })} ${datePublished.getFullYear()}` + : undefined + const detail = date || site.domain + return ( @@ -142,7 +201,8 @@ body { } .title { - font-size: 3em; + font-size: 3.2em; + line-height: 1.3; } .metadata {