-
-
Notifications
You must be signed in to change notification settings - Fork 621
fix: support direct image hyperlinks #640
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3f760f1
d603e80
6ebee95
42bc409
ea5d5ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,12 +1,25 @@ | ||||||||||||||||||||
import type React from 'react' | ||||||||||||||||||||
import { type BaseContentBlock, type Block } from 'notion-types' | ||||||||||||||||||||
import { type BaseContentBlock, type Block, type ImageBlock } from 'notion-types' | ||||||||||||||||||||
import { parsePageId } from 'notion-utils' | ||||||||||||||||||||
|
||||||||||||||||||||
import { useNotionContext } from '..' | ||||||||||||||||||||
import { cs } from '../utils' | ||||||||||||||||||||
import { Asset } from './asset' | ||||||||||||||||||||
import { Text } from './text' | ||||||||||||||||||||
|
||||||||||||||||||||
interface PageInfo { | ||||||||||||||||||||
type: 'page' | ||||||||||||||||||||
id: string | ||||||||||||||||||||
url: string | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
interface ExternalInfo { | ||||||||||||||||||||
type: 'external' | ||||||||||||||||||||
url: string | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
type URLInfo = PageInfo | ExternalInfo | null | ||||||||||||||||||||
|
||||||||||||||||||||
const urlStyle = { width: '100%' } | ||||||||||||||||||||
|
||||||||||||||||||||
export function AssetWrapper({ | ||||||||||||||||||||
|
@@ -19,18 +32,11 @@ export function AssetWrapper({ | |||||||||||||||||||
const value = block as BaseContentBlock | ||||||||||||||||||||
const { components, mapPageUrl, rootDomain, zoom } = useNotionContext() | ||||||||||||||||||||
|
||||||||||||||||||||
let isURL = false | ||||||||||||||||||||
if (block.type === 'image') { | ||||||||||||||||||||
const caption: string | undefined = value?.properties?.caption?.[0]?.[0] | ||||||||||||||||||||
if (caption) { | ||||||||||||||||||||
const id = parsePageId(caption, { uuid: true }) | ||||||||||||||||||||
const caption = value.properties?.caption?.[0]?.[0] | ||||||||||||||||||||
const imageHyperlink = (value as ImageBlock).format?.image_hyperlink | ||||||||||||||||||||
const linkToUse = imageHyperlink || caption || '' | ||||||||||||||||||||
|
||||||||||||||||||||
const isPage = caption.charAt(0) === '/' && id | ||||||||||||||||||||
if (isPage || isValidURL(caption)) { | ||||||||||||||||||||
isURL = true | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
const urlInfo = getURLInfo(value, linkToUse) | ||||||||||||||||||||
|
||||||||||||||||||||
const figure = ( | ||||||||||||||||||||
<figure | ||||||||||||||||||||
|
@@ -41,8 +47,8 @@ export function AssetWrapper({ | |||||||||||||||||||
blockId | ||||||||||||||||||||
)} | ||||||||||||||||||||
> | ||||||||||||||||||||
<Asset block={value} zoomable={zoom && !isURL}> | ||||||||||||||||||||
{value?.properties?.caption && !isURL && ( | ||||||||||||||||||||
<Asset block={value} zoomable={zoom && !urlInfo}> | ||||||||||||||||||||
{value?.properties?.caption && ( | ||||||||||||||||||||
<figcaption className='notion-asset-caption'> | ||||||||||||||||||||
<Text value={value.properties.caption} block={block} /> | ||||||||||||||||||||
</figcaption> | ||||||||||||||||||||
Comment on lines
+51
to
54
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The caption is now always displayed, even when it's being used as a link URL. This appears to contradict the PR description which mentions "exposing the link as a caption feels intrusive and affects the user experience." Consider restoring the condition to hide captions when they're used as links: {value?.properties?.caption && (!urlInfo || imageHyperlink) && (
<figcaption className='notion-asset-caption'>
<Text value={value.properties.caption} block={block} />
</figcaption>
)} This would hide the caption when it's being used as a link URL (unless
Suggested change
Spotted by Diamond |
||||||||||||||||||||
|
@@ -52,23 +58,21 @@ export function AssetWrapper({ | |||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
// allows for an image to be a link | ||||||||||||||||||||
if (isURL) { | ||||||||||||||||||||
const caption: string | undefined = value?.properties?.caption?.[0]?.[0] | ||||||||||||||||||||
const id = parsePageId(caption, { uuid: true }) | ||||||||||||||||||||
const isPage = caption?.charAt(0) === '/' && id | ||||||||||||||||||||
const captionHostname = extractHostname(caption) | ||||||||||||||||||||
if (urlInfo?.url) { | ||||||||||||||||||||
const urlHostName = extractHostname(urlInfo.url) | ||||||||||||||||||||
const isExternalLink = | ||||||||||||||||||||
urlHostName && urlHostName !== rootDomain && !caption?.startsWith('/') | ||||||||||||||||||||
|
||||||||||||||||||||
const href = | ||||||||||||||||||||
urlInfo.type === 'page' && urlInfo.id | ||||||||||||||||||||
? mapPageUrl(urlInfo.id) | ||||||||||||||||||||
: urlInfo.url | ||||||||||||||||||||
|
||||||||||||||||||||
return ( | ||||||||||||||||||||
<components.PageLink | ||||||||||||||||||||
style={urlStyle} | ||||||||||||||||||||
href={isPage ? mapPageUrl(id) : caption} | ||||||||||||||||||||
target={ | ||||||||||||||||||||
captionHostname && | ||||||||||||||||||||
captionHostname !== rootDomain && | ||||||||||||||||||||
!caption?.startsWith('/') | ||||||||||||||||||||
? 'blank_' | ||||||||||||||||||||
: null | ||||||||||||||||||||
} | ||||||||||||||||||||
href={href} | ||||||||||||||||||||
target={isExternalLink ? 'blank_' : null} | ||||||||||||||||||||
> | ||||||||||||||||||||
{figure} | ||||||||||||||||||||
</components.PageLink> | ||||||||||||||||||||
|
@@ -77,6 +81,31 @@ export function AssetWrapper({ | |||||||||||||||||||
|
||||||||||||||||||||
return figure | ||||||||||||||||||||
} | ||||||||||||||||||||
function getURLInfo(block: BaseContentBlock, link?: string): URLInfo { | ||||||||||||||||||||
if (!link || block.type !== 'image') { | ||||||||||||||||||||
return null | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
const id = parsePageId(link, { uuid: true }) | ||||||||||||||||||||
const isPage = link.charAt(0) === '/' && id | ||||||||||||||||||||
|
||||||||||||||||||||
if (isPage) { | ||||||||||||||||||||
return { | ||||||||||||||||||||
type: 'page' as const, | ||||||||||||||||||||
id, | ||||||||||||||||||||
url: link | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
if (isValidURL(link)) { | ||||||||||||||||||||
return { | ||||||||||||||||||||
type: 'external' as const, | ||||||||||||||||||||
url: link | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
return null | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
function isValidURL(str: string) { | ||||||||||||||||||||
// TODO: replace this with a more well-tested package | ||||||||||||||||||||
|
@@ -91,7 +120,6 @@ function isValidURL(str: string) { | |||||||||||||||||||
) | ||||||||||||||||||||
return !!pattern.test(str) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
function extractHostname(url?: string) { | ||||||||||||||||||||
try { | ||||||||||||||||||||
const hostname = new URL(url!).hostname | ||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
image_hyperlink
property should be marked as optional since it won't be present on all image blocks. Consider updating the type definition to:This ensures type safety when working with image blocks that don't contain hyperlinks.
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.