very minimalist hashtaglink implementation, also you can middle-click

mentions now.
This commit is contained in:
Henry Jameson 2021-08-23 20:57:21 +03:00
parent c3576211cb
commit 39494439d3
6 changed files with 75 additions and 12 deletions

View file

@ -0,0 +1,36 @@
import { extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
const HashtagLink = {
name: 'HashtagLink',
props: {
url: {
required: true,
type: String
},
content: {
required: true,
type: String
},
tag: {
required: false,
type: String,
default: ''
}
},
methods: {
onClick () {
const tag = this.tag || extractTagFromUrl(this.url)
if (tag) {
const link = this.generateTagLink(tag)
this.$router.push(link)
} else {
window.open(this.url, '_blank')
}
},
generateTagLink (tag) {
return `/tag/${tag}`
}
}
}
export default HashtagLink

View file

@ -0,0 +1,6 @@
.HashtagLink {
position: relative;
white-space: normal;
display: inline-block;
color: var(--link);
}

View file

@ -0,0 +1,19 @@
<template>
<span
class="HashtagLink"
>
<!-- eslint-disable vue/no-v-html -->
<a
:href="url"
class="original"
target="_blank"
@click.prevent="onClick"
v-html="content"
/>
<!-- eslint-enable vue/no-v-html -->
</span>
</template>
<script src="./hashtag_link.js"/>
<style lang="scss" src="./hashtag_link.scss"/>

View file

@ -17,8 +17,9 @@
:style="style" :style="style"
:class="classnames" :class="classnames"
> >
<button <a
class="short button-unstyled" class="short button-unstyled"
:href="url"
@click.prevent="onClick" @click.prevent="onClick"
> >
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
@ -35,7 +36,7 @@
class="you" class="you"
>{{ $t('status.you') }}</span> >{{ $t('status.you') }}</span>
<!-- eslint-enable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
</button> </a>
<span <span
v-if="userName !== userNameFull" v-if="userName !== userNameFull"
class="full popover-default" class="full popover-default"

View file

@ -5,6 +5,7 @@ import { convertHtmlToTree } from 'src/services/html_converter/html_tree_convert
import { convertHtmlToLines } from 'src/services/html_converter/html_line_converter.service.js' import { convertHtmlToLines } from 'src/services/html_converter/html_line_converter.service.js'
import StillImage from 'src/components/still-image/still-image.vue' import StillImage from 'src/components/still-image/still-image.vue'
import MentionsLine, { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.vue' import MentionsLine, { MENTIONS_LIMIT } from 'src/components/mentions_line/mentions_line.vue'
import HashtagLink from 'src/components/hashtag_link/hashtag_link.vue'
import './rich_content.scss' import './rich_content.scss'
@ -83,13 +84,10 @@ export default Vue.component('RichContent', {
const renderHashtag = (attrs, children, encounteredTextReverse) => { const renderHashtag = (attrs, children, encounteredTextReverse) => {
const linkData = getLinkData(attrs, children, tagsIndex++) const linkData = getLinkData(attrs, children, tagsIndex++)
writtenTags.push(linkData) writtenTags.push(linkData)
attrs.target = '_blank'
if (!encounteredTextReverse) { if (!encounteredTextReverse) {
lastTags.push(linkData) lastTags.push(linkData)
} }
return <a {...{ attrs }}> return <HashtagLink {...{ props: linkData }}/>
{ children.map(processItem) }
</a>
} }
const renderMention = (attrs, children) => { const renderMention = (attrs, children) => {
@ -211,7 +209,10 @@ export default Vue.component('RichContent', {
if (!this.handleLinks) break if (!this.handleLinks) break
const attrs = getAttrs(opener) const attrs = getAttrs(opener)
// should only be this // should only be this
if (attrs['class'] && attrs['class'].includes('hashtag')) { if (
(attrs['class'] && attrs['class'].includes('hashtag')) || // Pleroma style
(attrs['rel'] === 'tag') // Mastodon style
) {
return renderHashtag(attrs, children, encounteredTextReverse) return renderHashtag(attrs, children, encounteredTextReverse)
} else { } else {
attrs.target = '_blank' attrs.target = '_blank'
@ -275,7 +276,7 @@ const getLinkData = (attrs, children, index) => {
return { return {
index, index,
url: attrs.href, url: attrs.href,
hashtag: attrs['data-tag'], tag: attrs['data-tag'],
content: flattenDeep(children).join(''), content: flattenDeep(children).join(''),
textContent textContent
} }

View file

@ -300,10 +300,10 @@ describe('RichContent', () => {
'<p>', '<p>',
'<a href="http://macrochan.org/images/N/H/NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg" target="_blank">', '<a href="http://macrochan.org/images/N/H/NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg" target="_blank">',
'NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg</a>', 'NHCMDUXJPPZ6M3Z2CQ6D2EBRSWGE7MZY.jpg</a>',
' <a class="hashtag" data-tag="nou" href="https://shitposter.club/tag/nou" target="_blank">', ' <hashtaglink-stub url="https://shitposter.club/tag/nou" content="#nou" tag="nou">',
'#nou</a>', '</hashtaglink-stub>',
' <a class="hashtag" data-tag="screencap" href="https://shitposter.club/tag/screencap" target="_blank">', ' <hashtaglink-stub url="https://shitposter.club/tag/screencap" content="#screencap" tag="screencap">',
'#screencap</a>', '</hashtaglink-stub>',
' </p>' ' </p>'
].join('') ].join('')