refactor: get html media info implement, fixed #671

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-10-01 20:06:41 +08:00
parent 1ac465cbb9
commit 71749fa505
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
5 changed files with 56 additions and 32 deletions

View File

@ -22,8 +22,7 @@ export const useSourceContentModal = () => {
content: () => <SourceContentView src={src} />,
resizeable: true,
clickOutsideToDismiss: true,
// The number was picked arbitrarily
resizeDefaultSize: { width: 900, height: 700 },
max: true,
})
},
[present],

View File

@ -1,13 +1,13 @@
import { createElement, Fragment, memo, useEffect, useMemo, useState } from "react"
import type { MediaInfo } from "~/lib/parse-html"
import { parseHtml } from "~/lib/parse-html"
import type { RemarkOptions } from "~/lib/parse-markdown"
import { parseMarkdown } from "~/lib/parse-markdown"
import { cn } from "~/lib/utils"
import { useWrappedElementSize } from "~/providers/wrapped-element-provider"
import { MediaContainerWidthProvider } from "../media"
import type { MediaInfoRecord } from "../media"
import { MediaContainerWidthProvider, MediaInfoRecordProvider } from "../media"
import { MarkdownRenderContainerRefContext } from "./context"
export const Markdown: Component<
@ -45,7 +45,7 @@ const HTMLImpl = <A extends keyof JSX.IntrinsicElements = "div">(
accessory?: React.ReactNode
noMedia?: boolean
mediaInfo?: MediaInfo
mediaInfo?: MediaInfoRecord
} & JSX.IntrinsicElements[A] &
Partial<{
renderInlineStyle: boolean
@ -55,7 +55,6 @@ const HTMLImpl = <A extends keyof JSX.IntrinsicElements = "div">(
const [remarkOptions, setRemarkOptions] = useState({
renderInlineStyle,
noMedia,
mediaInfo,
})
const [shouldForceReMountKey, setShouldForceReMountKey] = useState(0)
@ -87,7 +86,9 @@ const HTMLImpl = <A extends keyof JSX.IntrinsicElements = "div">(
return (
<MarkdownRenderContainerRefContext.Provider value={refElement}>
<MediaContainerWidthProvider width={containerWidth}>
{createElement(as, { ...rest, ref: setRefElement }, markdownElement)}
<MediaInfoRecordProvider mediaInfo={mediaInfo}>
{createElement(as, { ...rest, ref: setRefElement }, markdownElement)}
</MediaInfoRecordProvider>
</MediaContainerWidthProvider>
{accessory && <Fragment key={shouldForceReMountKey}>{accessory}</Fragment>}
</MarkdownRenderContainerRefContext.Provider>

View File

@ -52,7 +52,15 @@ const MediaImpl: FC<MediaProps> = ({
thumbnail,
...props
}) => {
const { src, style, type, previewImageUrl, showFallback, blurhash, ...rest } = props
const { src, style, type, previewImageUrl, showFallback, blurhash, height, width, ...rest } =
props
const ctxMediaInfo = useContext(MediaInfoRecordContext)
const ctxHeight = ctxMediaInfo[src!]?.height
const ctxWidth = ctxMediaInfo[src!]?.width
const finalHeight = height || ctxHeight
const finalWidth = width || ctxWidth
const [imgSrc, setImgSrc] = useState(() =>
proxy && src && !failedList.has(src)
@ -130,6 +138,8 @@ const MediaImpl: FC<MediaProps> = ({
case "photo": {
return (
<img
height={finalHeight}
width={finalWidth}
{...(rest as ImgHTMLAttributes<HTMLImageElement>)}
onError={errorHandle}
className={cn(
@ -152,7 +162,7 @@ const MediaImpl: FC<MediaProps> = ({
<span
className={cn(
"center",
!(props.width || props.height) && "size-full",
!(finalWidth || finalHeight) && "size-full",
"relative cursor-card bg-stone-100 object-cover",
mediaContainerClassName,
)}
@ -172,11 +182,11 @@ const MediaImpl: FC<MediaProps> = ({
handleOnLoad,
imgSrc,
mediaContainerClassName,
finalHeight,
finalWidth,
mediaLoadState,
popper,
previewImageSrc,
props.height,
props.width,
rest,
src,
thumbnail,
@ -388,3 +398,21 @@ export const MediaContainerWidthProvider = ({
const useMediaContainerWidth = () => {
return useContext(MediaContainerWidthContext)
}
export type MediaInfoRecord = Record<string, { width?: number; height?: number }>
const MediaInfoRecordContext = createContext<MediaInfoRecord>({})
const noop = {} as const
export const MediaInfoRecordProvider = ({
children,
mediaInfo,
}: {
children: React.ReactNode
mediaInfo?: MediaInfoRecord
}) => {
return (
<MediaInfoRecordContext.Provider value={mediaInfo || noop}>
{children}
</MediaInfoRecordContext.Provider>
)
}

View File

@ -31,14 +31,11 @@ function markInlineImage(node?: Element) {
}
}
export type MediaInfo = Record<string, { width?: number; height?: number }>
export const parseHtml = (
content: string,
options?: Partial<{
renderInlineStyle: boolean
noMedia?: boolean
mediaInfo?: MediaInfo
}>,
) => {
const file = new VFile(content)
@ -99,13 +96,7 @@ export const parseHtml = (
markInlineImage(node)
return createElement(MarkdownLink, { ...props } as any)
},
img: ({ ...props }) => {
return createElement(Img, {
...props,
width: props.src ? options?.mediaInfo?.[props.src]?.width : props.width,
height: props.src ? options?.mediaInfo?.[props.src]?.height : props.height,
})
},
img: Img,
h1: createHeadingRenderer(1),
h2: createHeadingRenderer(2),
@ -216,6 +207,7 @@ const Img: Components["img"] = ({ node, ...props }) => {
...props,
proxy: { height: 0, width: 700 },
}
if (node?.properties.inline) {
return createElement(Media, {
type: "photo",

View File

@ -145,21 +145,25 @@ export const EntryContentRender: Component<{
: undefined,
[readerFontFamily],
)
const mediaInfo = useMemo(
() =>
Object.fromEntries(
(entry?.entries.media ?? data?.entries.media)
?.filter((m) => m.type === "photo")
.map((cur) => [
cur.url,
{
width: cur.width,
height: cur.height,
},
]) ?? [],
),
[entry?.entries.media, data?.entries.media],
)
if (!entry) return null
const content = entry?.entries.content ?? data?.entries.content
const mediaInfo = Object.fromEntries(
(entry.entries.media ?? data?.entries.media)
?.filter((m) => m.type === "photo")
.map((cur) => [
cur.url,
{
width: cur.width,
height: cur.height,
},
]) ?? [],
)
return (
<EntryContentProvider