feat: update feed content render

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei 2024-07-26 14:26:37 +08:00
parent cdeb419294
commit 41b3e174ea
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
5 changed files with 118 additions and 39 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path fill="#fff" fill-opacity=".01" d="M24 0v24H0V0z"/><path stroke="#10161F" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16.946 7.054 6.339 17.661M9.409 6.59a15.963 15.963 0 0 1 6.796-.249c.571.1.857.149 1.08.373.225.224.274.51.374 1.08a15.963 15.963 0 0 1-.25 6.796"/></svg>

After

Width:  |  Height:  |  Size: 376 B

View File

@ -1,4 +1,9 @@
import { Tooltip, TooltipContent, TooltipTrigger } from "../tooltip"
import {
Tooltip,
TooltipContent,
TooltipPortal,
TooltipTrigger,
} from "../tooltip"
interface LinkProps {
href: string
@ -10,10 +15,36 @@ interface LinkProps {
export const LinkWithTooltip = (props: LinkProps) => (
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<a href={props.href} title={props.title} target={props.target}>{props.children}</a>
<a href={props.href} title={props.title} target={props.target}>
{props.children}
</a>
</TooltipTrigger>
<TooltipContent align="start" side="bottom">
{props.href}
</TooltipContent>
<TooltipPortal>
<TooltipContent align="start" className="break-all" side="bottom">
{props.href}
</TooltipContent>
</TooltipPortal>
</Tooltip>
)
export const MarkdownLink = (props: LinkProps) => (
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<a
className="follow-link--underline font-semibold text-foreground no-underline"
href={props.href}
title={props.title}
target="_blank"
>
{props.children}
{typeof props.children === "string" && <i className="i-mgc-arrow-right-up-cute-re size-[0.9em] translate-y-[2px] opacity-70" />}
</a>
</TooltipTrigger>
<TooltipPortal>
<TooltipContent align="start" className="break-all" side="bottom">
{props.href}
</TooltipContent>
</TooltipPortal>
</Tooltip>
)

View File

@ -10,32 +10,39 @@ import { useEventCallback } from "usehooks-ts"
import { usePreviewMedia } from "./media/hooks"
const failedList = new Set<string | undefined>()
export type ImageProps =
| (ImgHTMLAttributes<HTMLImageElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "photo"
previewImageUrl?: string
})
| (VideoHTMLAttributes<HTMLVideoElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "video"
previewImageUrl?: string
})
const MediaImpl: FC<ImageProps> = ({
type BaseProps = {
mediaContainerClassName?: string
}
export type MediaProps = BaseProps &
(
| (ImgHTMLAttributes<HTMLImageElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "photo"
previewImageUrl?: string
})
| (VideoHTMLAttributes<HTMLVideoElement> & {
proxy?: {
width: number
height: number
}
disableContextMenu?: boolean
popper?: boolean
type: "video"
previewImageUrl?: string
})
)
const MediaImpl: FC<MediaProps> = ({
className,
proxy,
disableContextMenu,
popper = false,
mediaContainerClassName,
...props
}) => {
const { src, style, type, previewImageUrl, ...rest } = props
@ -89,6 +96,7 @@ const MediaImpl: FC<ImageProps> = ({
!(props.width || props.height) && "size-full",
"bg-stone-100 object-cover",
popper && "cursor-zoom-in",
mediaContainerClassName,
)}
src={imgSrc}
onClick={handleClick}
@ -136,6 +144,7 @@ const MediaImpl: FC<ImageProps> = ({
hidden && "hidden",
!(props.width || props.height) && "size-full",
"relative bg-stone-100 object-cover",
mediaContainerClassName,
)}
onClick={handleClick}
>

View File

@ -1,6 +1,6 @@
import { Checkbox } from "@renderer/components/ui/checkbox"
import { ShikiHighLighter } from "@renderer/components/ui/code-highlighter"
import { LinkWithTooltip } from "@renderer/components/ui/link"
import { MarkdownLink } from "@renderer/components/ui/link"
import { Media } from "@renderer/components/ui/media"
import { toJsxRuntime } from "hast-util-to-jsx-runtime"
import { createElement } from "react"
@ -28,10 +28,7 @@ export const parseHtml = async (
...defaultSchema,
tagNames: renderInlineStyle ?
defaultSchema.tagNames :
[
...defaultSchema.tagNames!,
"video",
],
[...defaultSchema.tagNames!, "video"],
attributes: {
...defaultSchema.attributes,
@ -58,7 +55,7 @@ export const parseHtml = async (
passNode: true,
components: {
a: ({ node, ...props }) =>
createElement(LinkWithTooltip, { ...props } as any),
createElement(MarkdownLink, { ...props } as any),
img: ({ node, ...props }) => {
if (node?.properties.inline) {
return createElement("img", {
@ -66,9 +63,17 @@ export const parseHtml = async (
style: { maxWidth: "100%", display: "inline" },
})
}
return createElement(Media, { ...props, popper: true, type: "photo" })
return createElement(Media, {
...props,
mediaContainerClassName: "w-full rounded",
className: "flex justify-center",
popper: true,
type: "photo",
})
},
video: ({ node, ...props }) => createElement(Media, { ...props, popper: true, type: "video" }),
video: ({ node, ...props }) =>
createElement(Media, { ...props, popper: true, type: "video" }),
p: ({ node, ...props }) => {
if (node?.children) {
for (const item of node.children) {
@ -79,10 +84,11 @@ export const parseHtml = async (
}
return createElement("p", undefined, props.children)
},
hr: ({ node, ...props }) => createElement("hr", {
...props,
className: tw`scale-x-50`,
}),
hr: ({ node, ...props }) =>
createElement("hr", {
...props,
className: tw`scale-x-50`,
}),
input: ({ node, ...props }) => {
if (props.type === "checkbox") {
return createElement(Checkbox, {

View File

@ -229,3 +229,35 @@
rgba(0, 0, 0, 0.067) 0px 2px 5px, rgba(0, 0, 0, 0.067) 0px 1px 1px;
}
}
/* Link */
@layer components {
.follow-link--underline {
color: currentColor;
background-image: linear-gradient(
theme(colors.theme.accent.DEFAULT),
theme(colors.theme.accent.DEFAULT)
);
background-size: 0% 1.5px;
background-repeat: no-repeat;
/* NOTE: this won't work with background images */
transition: all 500ms ease;
@apply border-0 no-underline;
background-position: left 1em;
&:hover {
background-size: 100% 1.5px;
text-shadow: 0.05em 0 theme(colors.background),
-0.05em 0 theme(colors.background);
transition: all 250ms ease;
}
&.no-shadow {
text-shadow: none;
}
}
}