forked from Mirrors/elk
feat: clean shared link in the parsed html if conditions are met
This commit is contained in:
parent
0b3d32e821
commit
075a9d115b
5 changed files with 47 additions and 5 deletions
|
@ -5,15 +5,29 @@ const {
|
||||||
status,
|
status,
|
||||||
newer,
|
newer,
|
||||||
withAction = true,
|
withAction = true,
|
||||||
|
cleanSharedLink,
|
||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
status: mastodon.v1.Status | mastodon.v1.StatusEdit
|
status: mastodon.v1.Status | mastodon.v1.StatusEdit
|
||||||
newer?: mastodon.v1.Status
|
newer?: mastodon.v1.Status
|
||||||
withAction?: boolean
|
withAction?: boolean
|
||||||
|
cleanSharedLink?: string | false
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { translation } = useTranslation(status, getLanguageCode())
|
const { translation } = useTranslation(status, getLanguageCode())
|
||||||
|
|
||||||
const emojisObject = useEmojisFallback(() => status.emojis)
|
const emojisObject = useEmojisFallback(() => status.emojis)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* example status raw content
|
||||||
|
*
|
||||||
|
* <p>🔴 trying to code live - come let's talk <span class="h-card"><a href="https://m.webtoo.ls/@elk" class="u-url mention">@<span>elk</span></a></span> and <a href="https://social.ayco.io/tags/opensource" class="mention hashtag" rel="tag">#<span>opensource</span></a> <a href="https://www.twitch.tv/ayoayco" target="_blank" rel="nofollow noopener noreferrer"><span class="invisible">https://www.</span><span class="">twitch.tv/ayoayco</span><span class="invisible"></span></a></p>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* "<p>I say something about the link first</p><p><a href=\"https://ayco.io\" target=\"_blank\" rel=\"nofollow noopener noreferrer\"><span class=\"invisible\">https://</span><span class=\"\">ayco.io</span><span class=\"invisible\"></span></a></p>"
|
||||||
|
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
const vnode = $computed(() => {
|
const vnode = $computed(() => {
|
||||||
if (!status.content)
|
if (!status.content)
|
||||||
return null
|
return null
|
||||||
|
@ -24,7 +38,9 @@ const vnode = $computed(() => {
|
||||||
collapseMentionLink: !!('inReplyToId' in status && status.inReplyToId),
|
collapseMentionLink: !!('inReplyToId' in status && status.inReplyToId),
|
||||||
status: 'id' in status ? status : undefined,
|
status: 'id' in status ? status : undefined,
|
||||||
inReplyToStatus: newer,
|
inReplyToStatus: newer,
|
||||||
|
cleanSharedLink,
|
||||||
})
|
})
|
||||||
|
|
||||||
return vnode
|
return vnode
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -16,6 +16,10 @@ const filter = $computed(() => filterResult?.filter)
|
||||||
|
|
||||||
const filterPhrase = $computed(() => filter?.title)
|
const filterPhrase = $computed(() => filter?.title)
|
||||||
const isFiltered = $computed(() => filterPhrase && (context && context !== 'details' ? filter?.context.includes(context) : false))
|
const isFiltered = $computed(() => filterPhrase && (context && context !== 'details' ? filter?.context.includes(context) : false))
|
||||||
|
|
||||||
|
const cleanSharedLink = !status.poll
|
||||||
|
&& !status.mediaAttachments.length
|
||||||
|
&& status.card?.url
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -34,7 +38,7 @@ const isFiltered = $computed(() => filterPhrase && (context && context !== 'deta
|
||||||
<template v-else-if="status.spoilerText" #spoiler>
|
<template v-else-if="status.spoilerText" #spoiler>
|
||||||
<p>{{ status.spoilerText }}</p>
|
<p>{{ status.spoilerText }}</p>
|
||||||
</template>
|
</template>
|
||||||
<StatusBody v-if="!status.sensitive || status.spoilerText" :status="status" :newer="newer" :with-action="!isDetails" :class="isDetails ? 'text-xl' : ''" />
|
<StatusBody v-if="!status.sensitive || status.spoilerText" :clean-shared-link="cleanSharedLink" :status="status" :newer="newer" :with-action="!isDetails" :class="isDetails ? 'text-xl' : ''" />
|
||||||
<StatusTranslation :status="status" />
|
<StatusTranslation :status="status" />
|
||||||
<StatusPoll v-if="status.poll" :status="status" />
|
<StatusPoll v-if="status.poll" :status="status" />
|
||||||
<StatusMedia
|
<StatusMedia
|
||||||
|
@ -45,6 +49,7 @@ const isFiltered = $computed(() => filterPhrase && (context && context !== 'deta
|
||||||
v-if="status.card"
|
v-if="status.card"
|
||||||
:card="status.card"
|
:card="status.card"
|
||||||
:small-picture-only="status.mediaAttachments?.length > 0"
|
:small-picture-only="status.mediaAttachments?.length > 0"
|
||||||
|
:clean-shared-link="cleanSharedLink"
|
||||||
/>
|
/>
|
||||||
<StatusCard
|
<StatusCard
|
||||||
v-if="status.reblog"
|
v-if="status.reblog"
|
||||||
|
|
|
@ -7,15 +7,16 @@ const props = defineProps<{
|
||||||
smallPictureOnly?: boolean
|
smallPictureOnly?: boolean
|
||||||
/** When it is root card in the list, not appear as a child card */
|
/** When it is root card in the list, not appear as a child card */
|
||||||
root?: boolean
|
root?: boolean
|
||||||
|
/** Defined when the preview card URL matches the last shared link href attribute */
|
||||||
|
cleanSharedLink?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const providerName = $computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname)
|
const providerName = $computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname)
|
||||||
|
|
||||||
const gitHubCards = $(usePreferences('experimentalGitHubCards'))
|
const gitHubCards = $(usePreferences('experimentalGitHubCards'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LazyStatusPreviewGitHub v-if="gitHubCards && providerName === 'GitHub'" :card="card" />
|
<LazyStatusPreviewGitHub v-if="gitHubCards && providerName === 'GitHub'" :card="card" />
|
||||||
<LazyStatusPreviewStackBlitz v-else-if="gitHubCards && providerName === 'stackblitz.com'" :card="card" :small-picture-only="smallPictureOnly" :root="root" />
|
<LazyStatusPreviewStackBlitz v-else-if="gitHubCards && providerName === 'stackblitz.com'" :card="card" :small-picture-only="smallPictureOnly" :root="root" />
|
||||||
<StatusPreviewCardNormal v-else :card="card" :small-picture-only="smallPictureOnly" :root="root" />
|
<StatusPreviewCardNormal v-else :card="card" :clean-shared-link="cleanSharedLink" :small-picture-only="smallPictureOnly" :root="root" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -7,6 +7,8 @@ const props = defineProps<{
|
||||||
smallPictureOnly?: boolean
|
smallPictureOnly?: boolean
|
||||||
/** When it is root card in the list, not appear as a child card */
|
/** When it is root card in the list, not appear as a child card */
|
||||||
root?: boolean
|
root?: boolean
|
||||||
|
/** Defined when the preview card URL matches the last shared link href attribute */
|
||||||
|
cleanSharedLink?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// mastodon's default max og image width
|
// mastodon's default max og image width
|
||||||
|
@ -19,7 +21,7 @@ const isSquare = $computed(() => (
|
||||||
|| Number(props.card.width || 0) < ogImageWidth
|
|| Number(props.card.width || 0) < ogImageWidth
|
||||||
|| Number(props.card.height || 0) < ogImageWidth / 2
|
|| Number(props.card.height || 0) < ogImageWidth / 2
|
||||||
))
|
))
|
||||||
const providerName = $computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname)
|
const providerName = $computed(() => props.card.providerName ? `${props.card.providerName} · ${props.cleanSharedLink}` : new URL(props.card.url).hostname)
|
||||||
|
|
||||||
// TODO: handle card.type: 'photo' | 'video' | 'rich';
|
// TODO: handle card.type: 'photo' | 'video' | 'rich';
|
||||||
const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
|
const cardTypeIconMap: Record<mastodon.v1.PreviewCardType, string> = {
|
||||||
|
|
|
@ -16,6 +16,7 @@ export interface ContentParseOptions {
|
||||||
collapseMentionLink?: boolean
|
collapseMentionLink?: boolean
|
||||||
status?: mastodon.v1.Status
|
status?: mastodon.v1.Status
|
||||||
inReplyToStatus?: mastodon.v1.Status
|
inReplyToStatus?: mastodon.v1.Status
|
||||||
|
cleanSharedLink?: string | false // this is defined when conditions for cleaning the link are met
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitizerBasicClasses = filterClasses(/^(h-\S*|p-\S*|u-\S*|dt-\S*|e-\S*|mention|hashtag|ellipsis|invisible)$/u)
|
const sanitizerBasicClasses = filterClasses(/^(h-\S*|p-\S*|u-\S*|dt-\S*|e-\S*|mention|hashtag|ellipsis|invisible)$/u)
|
||||||
|
@ -84,6 +85,7 @@ export function parseMastodonHTML(
|
||||||
mentions,
|
mentions,
|
||||||
status,
|
status,
|
||||||
inReplyToStatus,
|
inReplyToStatus,
|
||||||
|
cleanSharedLink,
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
if (markdown) {
|
if (markdown) {
|
||||||
|
@ -127,7 +129,23 @@ export function parseMastodonHTML(
|
||||||
if (collapseMentionLink)
|
if (collapseMentionLink)
|
||||||
transforms.push(transformCollapseMentions(status, inReplyToStatus))
|
transforms.push(transformCollapseMentions(status, inReplyToStatus))
|
||||||
|
|
||||||
return transformSync(parse(html), transforms)
|
const node = parse(html) as Node
|
||||||
|
|
||||||
|
if (cleanSharedLink) {
|
||||||
|
const filteredNode = node.children.filter(child => !!child.children) // remove invisible spans
|
||||||
|
const filteredLength = filteredNode.length
|
||||||
|
const length = filteredNode[filteredLength - 1].children.length
|
||||||
|
const lastChild = filteredNode[filteredLength - 1].children[length - 1]
|
||||||
|
const sharedHref = lastChild.attributes?.href
|
||||||
|
const match = sharedHref === cleanSharedLink
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const index = length - 1
|
||||||
|
filteredNode[filteredLength - 1].children.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformSync(node, transforms)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue