refactor(source): text-view (#227)

* refactor text-view

* lint fix
This commit is contained in:
Ulf Gebhardt 2025-06-03 10:09:19 +02:00 committed by GitHub
parent ed9906ae2f
commit c82084576a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 108 additions and 101 deletions

View File

@ -4,7 +4,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { memo } from 'react'
import Markdown from 'react-markdown'
import remarkBreaks from 'remark-breaks'
@ -26,14 +25,12 @@ export const TextView = ({
text,
truncate = false,
rawText,
itemTextField,
}: {
item?: Item
itemId?: string
text?: string
truncate?: boolean
rawText?: string
itemTextField?: string
}) => {
if (item) {
text = item.text
@ -41,8 +38,6 @@ export const TextView = ({
}
const tags = useTags()
const addFilterTag = useAddFilterTag()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const itemTextFieldDummy = itemTextField
let innerText = ''
let replacedText = ''
@ -86,53 +81,15 @@ export const TextView = ({
})
}
const CustomH1 = ({ children }) => <h1 className='tw:text-xl tw:font-bold'>{children}</h1>
const CustomH2 = ({ children }) => <h2 className='tw:text-lg tw:font-bold'>{children}</h2>
const CustomH3 = ({ children }) => <h3 className='tw:text-base tw:font-bold'>{children}</h3>
const CustomH4 = ({ children }) => <h4 className='tw:text-base tw:font-bold'>{children}</h4>
const CustomH5 = ({ children }) => <h5 className='tw:text-sm tw:font-bold'>{children}</h5>
const CustomH6 = ({ children }) => <h6 className='tw:text-sm tw:font-bold'>{children}</h6>
const CustomParagraph = ({ children }) => <p className='tw:my-2!'>{children}</p>
const CustomUnorderdList = ({ children }) => (
<ul className='tw:list-disc tw:list-inside'>{children}</ul>
)
const CustomOrderdList = ({ children }) => (
<ol className='tw:list-decimal tw:list-inside'>{children}</ol>
)
const CustomHorizontalRow = ({ children }) => <hr className='tw:border-current'>{children}</hr>
// eslint-disable-next-line react/prop-types
const CustomImage = ({ alt, src, title }) => (
<img className='tw:max-w-full tw:rounded tw:shadow' src={src} alt={alt} title={title} />
)
const CustomExternalLink = ({ href, children }) => (
<a className='tw:font-bold tw:underline' href={href} target='_blank' rel='noreferrer'>
{' '}
{children}
</a>
)
const CustomHashTagLink = ({
children,
tag,
itemId,
}: {
children: string
tag: Tag
itemId?: string
}) => {
const HashTag = ({ children, tag, itemId }: { children: string; tag: Tag; itemId?: string }) => {
return (
<a
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
className='hashtag'
style={
tag && {
color: tag.color,
}
}
key={tag ? tag.name + itemId : itemId}
onClick={(e) => {
e.stopPropagation()
@ -144,62 +101,48 @@ export const TextView = ({
)
}
// eslint-disable-next-line react/display-name
const MemoizedVideoEmbed = memo(({ url }: { url: string }) => (
<iframe
className='tw:w-full'
src={url}
allow='fullscreen; picture-in-picture'
allowFullScreen
/>
))
const Link = ({ href, children }: { href: string; children: string }) => {
// Youtube
if (href.startsWith('https://www.youtube.com/watch?v=')) {
const videoId = href?.split('v=')[1].split('&')[0]
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`
return (
<iframe src={youtubeEmbedUrl} allow='fullscreen; picture-in-picture' allowFullScreen />
)
}
// Rumble
if (href.startsWith('https://rumble.com/embed/')) {
return <iframe src={href} allow='fullscreen; picture-in-picture' allowFullScreen />
}
// HashTag
if (href.startsWith('#')) {
const tag = tags.find((t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase())
if (tag)
return (
<HashTag tag={tag} itemId={itemId}>
{children}
</HashTag>
)
else return children
}
// Default: Link
return (
<a href={href} target='_blank' rel='noreferrer'>
{children}
</a>
)
}
return (
<Markdown
className={'tw:text-map tw:leading-map tw:text-sm'}
className={'markdown tw:text-map tw:leading-map tw:text-sm'}
remarkPlugins={[remarkBreaks]}
components={{
p: CustomParagraph,
a: ({ href, children }: { href: string; children: string }) => {
const isYouTubeVideo = href?.startsWith('https://www.youtube.com/watch?v=')
const isRumbleVideo = href?.startsWith('https://rumble.com/embed/')
if (isYouTubeVideo) {
const videoId = href?.split('v=')[1].split('&')[0]
const youtubeEmbedUrl = `https://www.youtube-nocookie.com/embed/${videoId}`
return <MemoizedVideoEmbed url={youtubeEmbedUrl}></MemoizedVideoEmbed>
}
if (isRumbleVideo) {
return <MemoizedVideoEmbed url={href}></MemoizedVideoEmbed>
}
if (href?.startsWith('#')) {
const tag = tags.find(
(t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase(),
)
if (tag)
return (
<CustomHashTagLink tag={tag} itemId={itemId}>
{children}
</CustomHashTagLink>
)
else return children
} else {
return <CustomExternalLink href={href}>{children}</CustomExternalLink>
}
},
ul: CustomUnorderdList,
ol: CustomOrderdList,
img: CustomImage,
hr: CustomHorizontalRow,
h1: CustomH1,
h2: CustomH2,
h3: CustomH3,
h4: CustomH4,
h5: CustomH5,
h6: CustomH6,
a: Link,
}}
>
{replacedText}

View File

@ -0,0 +1,63 @@
@import 'tailwindcss' prefix(tw);
@layer markdown {
.markdown h1 {
@apply tw:text-xl;
@apply tw:font-bold;
}
.markdown h2 {
@apply tw:text-lg;
@apply tw:font-bold;
}
.markdown h3, .markdown h4 {
@apply tw:text-base;
@apply tw:font-bold;
}
.markdown h5, .markdown h6 {
@apply tw:text-sm;
@apply tw:font-bold;
}
.markdown p {
@apply tw:my-2!;
}
.markdown ul {
@apply tw:list-disc;
@apply tw:list-inside;
}
.markdown ol {
@apply tw:list-decimal;
@apply tw:list-inside;
}
.markdown hl {
@apply tw:border-current;
}
.markdown img {
@apply tw:max-w-full;
@apply tw:rounded;
@apply tw:shadow;
}
.markdown a {
@apply tw:font-bold;
@apply tw:underline;
}
.markdown .hashtag {
color: #faa;
font-weight: bold;
cursor: pointer;
text-decoration: none;
}
.markdown iframe {
@apply tw:w-full;
}
}

View File

@ -13,3 +13,4 @@ import '#assets/css/misc.css'
import '#assets/css/marker-icons.css'
import '#assets/css/leaflet.css'
import '#assets/css/color-picker.css'
import '#assets/css/markdown.css'