seperate hashtags

This commit is contained in:
Anton Tranelis 2025-07-04 15:07:46 +02:00
parent 46705b2162
commit 28dd16dfeb
3 changed files with 71 additions and 16 deletions

40
lib/package-lock.json generated
View File

@ -57,9 +57,11 @@
"react-toastify": "^9.1.3",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.10",
"unified": "^11.0.5",
@ -7244,6 +7246,29 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-to-html": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
"integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
"@types/unist": "^3.0.0",
"ccount": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-whitespace": "^3.0.0",
"html-void-elements": "^3.0.0",
"mdast-util-to-hast": "^13.0.0",
"property-information": "^7.0.0",
"space-separated-tokens": "^2.0.0",
"stringify-entities": "^4.0.0",
"zwitch": "^2.0.4"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-to-jsx-runtime": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
@ -12009,6 +12034,21 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/rehype-stringify": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz",
"integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
"hast-util-to-html": "^9.0.0",
"unified": "^11.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-breaks": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz",

View File

@ -145,9 +145,11 @@
"react-toastify": "^9.1.3",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.10",
"unified": "^11.0.5",

View File

@ -6,12 +6,14 @@ import remarkBreaks from 'remark-breaks'
import remarkGfm from 'remark-gfm'
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
import { useTags } from '#components/Map/hooks/useTags'
import { useGetItemTags, useTags } from '#components/Map/hooks/useTags'
import { decodeTag } from '#utils/FormatTags'
import type { Item } from '#types/Item'
export const TextPreview = ({ item }: { item: Item }) => {
const getItemTags = useGetItemTags()
if (!item.text) return null
// Text auf ~100 Zeichen stutzen (inkl. Ellipse „…“)
const previewRaw = truncate(item.text, { limit: 100, ellipsis: true }) as string
@ -23,22 +25,21 @@ export const TextPreview = ({ item }: { item: Item }) => {
return (
<div className='markdown'>
<Markdown
remarkPlugins={[remarkBreaks, remarkGfm]}
rehypePlugins={[rehypeRaw]}
components={{ span: Span }}
>
{withExtraHashes}
<Markdown remarkPlugins={[remarkBreaks, remarkGfm]} rehypePlugins={[rehypeRaw]}>
{removeMentionSpans(removeHashtags(withExtraHashes))}
</Markdown>
{getItemTags(item).map((tag) => (
<HashTag tag={tag} key={tag} />
))}
</div>
)
}
export const HashTag = ({ tag }: { tag: string }) => {
export const HashTag = ({ tag }: { tag: Tag }) => {
const tags = useTags()
const t = tags.find((t) => t.name === tag.slice(1))
const t = tags.find((t) => t.name.toLocaleLowerCase() === tag.name.toLocaleLowerCase())
const addFilterTag = useAddFilterTag()
if (!t) return <span>{tag}</span>
if (!t) return null
return (
<a
className='hashtag'
@ -49,14 +50,26 @@ export const HashTag = ({ tag }: { tag: string }) => {
addFilterTag(t)
}}
>
{decodeTag(tag)}
{`#${decodeTag(tag.name)} `}
</a>
)
}
export const Span = (node) => {
if (node['data-type'] === 'mention') {
return <HashTag tag={node.children} />
}
return <span {...node}>{node.children}</span>
function removeMentionSpans(html) {
return html.replace(
/<span\b(?=[^>]*\bdata-type="mention")(?=[^>]*\bclass="mention")[^>]*>[\s\S]*?<\/span>/gi,
'',
)
}
function removeHashtags(str) {
return str
// 1. Hashtags entfernen, außer sie stehen am Zeilenanfang als Markdown-Heading
.replace(
/(^|\s)(?!#{1,6}\s)(#[A-Za-z0-9_]+)\b/g,
'$1'
)
// 3. Anfangs/Ende trimmen
.trim()
}