feat: format highlight source url
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
|
import { truncateUrl } from '@/lib/url'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
export default function ExternalLink({ url, className }: { url: string; className?: string }) {
|
export default function ExternalLink({ url, className }: { url: string; className?: string }) {
|
||||||
const displayUrl = useMemo(() => getDisplayUrl(url), [url])
|
const displayUrl = useMemo(() => truncateUrl(url), [url])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
@@ -17,29 +18,3 @@ export default function ExternalLink({ url, className }: { url: string; classNam
|
|||||||
</a>
|
</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDisplayUrl = (url: string, maxLength: number = 30) => {
|
|
||||||
try {
|
|
||||||
const urlObj = new URL(url)
|
|
||||||
let domain = urlObj.hostname
|
|
||||||
const path = urlObj.pathname
|
|
||||||
|
|
||||||
if (domain.startsWith('www.')) {
|
|
||||||
domain = domain.slice(4)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path || path === '/') {
|
|
||||||
return domain
|
|
||||||
}
|
|
||||||
|
|
||||||
const displayUrl = domain + path
|
|
||||||
|
|
||||||
if (displayUrl.length > maxLength) {
|
|
||||||
return domain + path.slice(0, maxLength - domain.length - 3) + '...'
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayUrl
|
|
||||||
} catch {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useMemo } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Content from '../Content'
|
import Content from '../Content'
|
||||||
import ContentPreview from '../ContentPreview'
|
import ContentPreview from '../ContentPreview'
|
||||||
|
import ExternalLink from '../ExternalLink'
|
||||||
import UserAvatar from '../UserAvatar'
|
import UserAvatar from '../UserAvatar'
|
||||||
|
|
||||||
export default function Highlight({ event, className }: { event: Event; className?: string }) {
|
export default function Highlight({ event, className }: { event: Event; className?: string }) {
|
||||||
@@ -99,15 +100,10 @@ function HighlightSource({ event }: { event: Event }) {
|
|||||||
return (
|
return (
|
||||||
<div className="truncate text-muted-foreground">
|
<div className="truncate text-muted-foreground">
|
||||||
{t('From')}{' '}
|
{t('From')}{' '}
|
||||||
<a
|
<ExternalLink
|
||||||
href={sourceTag[1]}
|
url={sourceTag[1]}
|
||||||
target="_blank"
|
className="underline italic text-muted-foreground hover:text-foreground"
|
||||||
rel="noopener noreferrer"
|
/>
|
||||||
className="underline text-muted-foreground hover:text-foreground"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{sourceTag[1]}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,3 +137,33 @@ export function isMedia(url: string) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const truncateUrl = (url: string, maxLength: number = 40) => {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(url)
|
||||||
|
let domain = urlObj.hostname
|
||||||
|
let path = urlObj.pathname
|
||||||
|
|
||||||
|
if (domain.startsWith('www.')) {
|
||||||
|
domain = domain.slice(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path || path === '/') {
|
||||||
|
return domain
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.endsWith('/')) {
|
||||||
|
path = path.slice(0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const u = domain + path
|
||||||
|
|
||||||
|
if (u.length > maxLength) {
|
||||||
|
return domain + path.slice(0, maxLength - domain.length - 3) + '...'
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
} catch {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user