Merge branch 'better-still-emoji' into shigusegubu

* better-still-emoji:
  fix tests, add performance test (skipped, doesn't assert anything), tweak max mentions count
  made the code responsible for showing unwritten mentions actually work
  remove new options for style and separate line, now groups all chained mentions on a mentionsline regardless of placement. fixes spacing
This commit is contained in:
Henry Jameson 2021-08-12 23:01:29 +03:00
commit f96ffe3699
16 changed files with 132 additions and 646 deletions

View file

@ -41,7 +41,7 @@ const MentionLink = {
}, },
computed: { computed: {
user () { user () {
return this.url && this.$store.getters.findUserByUrl(this.url) return this.url && this.$store && this.$store.getters.findUserByUrl(this.url)
}, },
isYou () { isYou () {
// FIXME why user !== currentUser??? // FIXME why user !== currentUser???
@ -65,9 +65,6 @@ const MentionLink = {
highlightClass () { highlightClass () {
if (this.highlight) return highlightClass(this.user) if (this.highlight) return highlightClass(this.user)
}, },
oldStyle () {
return !this.mergedConfig.mentionsNewStyle
},
style () { style () {
if (this.highlight) { if (this.highlight) {
const { const {
@ -83,8 +80,7 @@ const MentionLink = {
return [ return [
{ {
'-you': this.isYou, '-you': this.isYou,
'-highlighted': this.highlight, '-highlighted': this.highlight
'-oldStyle': this.oldStyle
}, },
this.highlightType this.highlightType
] ]

View file

@ -10,10 +10,6 @@
border-radius: 2px; border-radius: 2px;
} }
.original {
margin-right: 0.25em;
}
.full { .full {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
@ -41,8 +37,6 @@
} }
.new { .new {
margin-right: 0.25em;
&.-you { &.-you {
& .shortName, & .shortName,
& .full { & .full {
@ -61,41 +55,6 @@
margin: 0; margin: 0;
} }
&:not(.-oldStyle) {
.short {
padding-left: 0.25em;
padding-right: 0;
padding-top: 0;
padding-bottom: 0;
line-height: 1.5;
font-size: inherit;
.at {
color: var(--faint);
opacity: 0.8;
padding-right: 0.25em;
vertical-align: -20%;
}
}
.you {
padding-right: 0.25em;
}
.userName {
display: inline-block;
color: var(--link);
line-height: inherit;
margin-left: 0;
padding-left: 0.125em;
padding-right: 0.25em;
padding-top: 0;
padding-bottom: 0;
border-top-right-radius: var(--btnRadius);
border-bottom-right-radius: var(--btnRadius);
}
}
&.-striped { &.-striped {
& .userName, & .userName,
& .full { & .full {

View file

@ -18,8 +18,7 @@
:class="classnames" :class="classnames"
> >
<button <button
class="short" class="short button-unstyled"
:class="[{ '-sublime': !highlight }, oldStyle ? 'button-unstyled' : 'button-default']"
@click.prevent="onClick" @click.prevent="onClick"
> >
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->

View file

@ -14,11 +14,8 @@ const MentionsLine = {
MentionLink MentionLink
}, },
computed: { computed: {
oldStyle () {
return !this.mergedConfig.mentionsNewStyle
},
limit () { limit () {
return 6 return 5
}, },
mentionsComputed () { mentionsComputed () {
return this.mentions.slice(0, this.limit) return this.mentions.slice(0, this.limit)
@ -29,16 +26,6 @@ const MentionsLine = {
manyMentions () { manyMentions () {
return this.extraMentions.length > 0 return this.extraMentions.length > 0
}, },
buttonClasses () {
return [
this.oldStyle
? 'button-unstyled'
: 'button-default -sublime',
this.oldStyle
? '-oldStyle'
: '-newStyle'
]
},
...mapGetters(['mergedConfig']) ...mapGetters(['mergedConfig'])
}, },
methods: { methods: {

View file

@ -1,17 +1,10 @@
.MentionsLine { .MentionsLine {
.showMoreLess { .showMoreLess {
white-space: normal; white-space: normal;
&.-newStyle {
line-height: 1.5;
font-size: inherit;
display: inline-block;
padding-top: 0;
padding-bottom: 0;
}
&.-oldStyle {
color: var(--link); color: var(--link);
} }
.mention-link:not(:last-child) {
margin-right: 0.25em;
} }
} }

View file

@ -25,15 +25,13 @@
/> />
</span><button </span><button
v-if="!expanded" v-if="!expanded"
class="showMoreLess" class="button-unstyled showMoreLess"
:class="buttonClasses"
@click="toggleShowMore" @click="toggleShowMore"
> >
{{ $t('status.plus_more', { number: extraMentions.length }) }} {{ $t('status.plus_more', { number: extraMentions.length }) }}
</button><button </button><button
v-if="expanded" v-if="expanded"
class="showMoreLess" class="button-unstyled showMoreLess"
:class="buttonClasses"
@click="toggleShowMore" @click="toggleShowMore"
> >
{{ $t('general.show_less') }} {{ $t('general.show_less') }}

View file

@ -56,25 +56,21 @@ export default Vue.component('RichContent', {
required: false, required: false,
type: Boolean, type: Boolean,
default: false default: false
},
hideMentions: {
required: false,
type: Boolean,
default: false
} }
}, },
// NEVER EVER TOUCH DATA INSIDE RENDER // NEVER EVER TOUCH DATA INSIDE RENDER
render (h) { render (h) {
// Pre-process HTML // Pre-process HTML
const { newHtml: html, lastMentions } = preProcessPerLine(this.html, this.greentext, this.handleLinks) const { newHtml: html } = preProcessPerLine(this.html, this.greentext, this.handleLinks)
const firstMentions = [] // Mentions that appear in the beginning of post body let currentMentions = null // Current chain of mentions, we group all mentions together
// to collapse too many mentions in a row
const lastTags = [] // Tags that appear at the end of post body const lastTags = [] // Tags that appear at the end of post body
const writtenMentions = [] // All mentions that appear in post body const writtenMentions = [] // All mentions that appear in post body
const writtenTags = [] // All tags that appear in post body const writtenTags = [] // All tags that appear in post body
// unique index for vue "tag" property // unique index for vue "tag" property
let mentionIndex = 0 let mentionIndex = 0
let tagsIndex = 0 let tagsIndex = 0
let firstMentionReplaced = false
const renderImage = (tag) => { const renderImage = (tag) => {
return <StillImage return <StillImage
@ -98,41 +94,32 @@ export default Vue.component('RichContent', {
const renderMention = (attrs, children) => { const renderMention = (attrs, children) => {
const linkData = getLinkData(attrs, children, mentionIndex++) const linkData = getLinkData(attrs, children, mentionIndex++)
linkData.notifying = this.attentions.some(a => a.statusnet_profile_url === linkData.url) linkData.notifying = this.attentions.some(a => a.statusnet_profile_url === linkData.url)
if (!linkData.notifying) {
encounteredText = true
}
writtenMentions.push(linkData) writtenMentions.push(linkData)
if (!encounteredText) { if (currentMentions === null) {
firstMentions.push(linkData) currentMentions = []
if (!firstMentionReplaced && !this.hideMentions) { }
firstMentionReplaced = true currentMentions.push(linkData)
return <MentionsLine mentions={ firstMentions } /> if (currentMentions.length === 1) {
return <MentionsLine mentions={ currentMentions } />
} else { } else {
return '' return ''
} }
} else {
return <MentionLink
url={attrs.href}
content={flattenDeep(children).join('')}
/>
}
} }
// We stop treating mentions as "first" ones when we encounter
// non-whitespace text
let encounteredText = false
// Processor to use with html_tree_converter // Processor to use with html_tree_converter
const processItem = (item, index, array, what) => { const processItem = (item, index, array, what) => {
// Handle text nodes - just add emoji // Handle text nodes - just add emoji
if (typeof item === 'string') { if (typeof item === 'string') {
const emptyText = item.trim() === '' const emptyText = item.trim() === ''
if (item.includes('\n')) {
currentMentions = null
}
if (emptyText) { if (emptyText) {
return encounteredText ? item : item.trim() // don't include spaces when processing mentions - we'll include them
} // in MentionsLine
if (!encounteredText) { return currentMentions !== null ? item.trim() : item
item = item.trimStart()
encounteredText = true
} }
currentMentions = null
if (item.includes(':')) { if (item.includes(':')) {
item = ['', processTextForEmoji( item = ['', processTextForEmoji(
item, item,
@ -156,28 +143,25 @@ export default Vue.component('RichContent', {
const Tag = getTagName(opener) const Tag = getTagName(opener)
const attrs = getAttrs(opener) const attrs = getAttrs(opener)
switch (Tag) { switch (Tag) {
case 'span': // Replace last mentions class with mentionsline case 'br':
if (attrs['class'] && attrs['class'].includes('lastMentions')) { currentMentions = null
if (firstMentions.length > 1 && lastMentions.length > 1) {
break break
} else {
return !this.hideMentions ? <MentionsLine mentions={lastMentions} /> : ''
}
} else {
break
}
case 'img': // replace images with StillImage case 'img': // replace images with StillImage
return renderImage(opener) return renderImage(opener)
case 'a': // replace mentions with MentionLink case 'a': // replace mentions with MentionLink
if (!this.handleLinks) break if (!this.handleLinks) break
if (attrs['class'] && attrs['class'].includes('mention')) { if (attrs['class'] && attrs['class'].includes('mention')) {
// Handling mentions here // Handling mentions here
return renderMention(attrs, children, encounteredText) return renderMention(attrs, children)
} else { } else {
// Everything else will be handled in reverse pass // Everything else will be handled in reverse pass
encounteredText = true currentMentions = null
return item // We'll handle it later return item // We'll handle it later
} }
case 'span':
if (this.handleLinks && attrs['class'] && attrs['class'].includes('h-card')) {
return ['', children.map(processItem), '']
}
} }
if (children !== undefined) { if (children !== undefined) {
@ -246,8 +230,6 @@ export default Vue.component('RichContent', {
</span> </span>
const event = { const event = {
firstMentions,
lastMentions,
lastTags, lastTags,
writtenMentions, writtenMentions,
writtenTags writtenTags
@ -284,15 +266,12 @@ export const preProcessPerLine = (html, greentext, handleLinks) => {
const lastMentions = [] const lastMentions = []
const greentextHandle = new Set(['p', 'div']) const greentextHandle = new Set(['p', 'div'])
let nonEmptyIndex = -1
const lines = convertHtmlToLines(html) const lines = convertHtmlToLines(html)
const linesNum = lines.filter(c => c.text).length
const newHtml = lines.reverse().map((item, index, array) => { const newHtml = lines.reverse().map((item, index, array) => {
// Going over each line in reverse to detect last mentions, // Going over each line in reverse to detect last mentions,
// keeping non-text stuff as-is // keeping non-text stuff as-is
if (!item.text) return item if (!item.text) return item
const string = item.text const string = item.text
nonEmptyIndex += 1
// Greentext stuff // Greentext stuff
if ( if (
@ -316,42 +295,19 @@ export const preProcessPerLine = (html, greentext, handleLinks) => {
// Converting that line part into tree // Converting that line part into tree
const tree = convertHtmlToTree(string) const tree = convertHtmlToTree(string)
// If line has loose text, i.e. text outside a mention or a tag
// we won't touch mentions.
let hasLooseText = false
let mentionsNum = 0
const process = (item) => { const process = (item) => {
if (Array.isArray(item)) { if (Array.isArray(item)) {
const [opener, children, closer] = item const [opener, children, closer] = item
const tag = getTagName(opener) const tag = getTagName(opener)
// If we have a link we probably have mentions if (tag === 'span' || tag === 'p') {
if (tag === 'a') {
if (!handleLinks) return [opener, children, closer]
const attrs = getAttrs(opener)
if (attrs['class'] && attrs['class'].includes('mention')) {
// Got mentions
mentionsNum++
return [opener, children, closer]
} else {
// Not a mention? Means we have loose text or whatever
hasLooseText = true
return [opener, children, closer]
}
} else if (tag === 'span' || tag === 'p') {
// For span and p we need to go deeper // For span and p we need to go deeper
return [opener, [...children].map(process), closer] return [opener, [...children].map(process), closer]
} else { } else {
// Everything else equals to a loose text
hasLooseText = true
return [opener, children, closer] return [opener, children, closer]
} }
} }
if (typeof item === 'string') { if (typeof item === 'string') {
if (item.trim() !== '') {
// only meaningful strings are loose text
hasLooseText = true
}
return item return item
} }
} }
@ -359,33 +315,7 @@ export const preProcessPerLine = (html, greentext, handleLinks) => {
// We now processed our tree, now we need to mark line as lastMentions // We now processed our tree, now we need to mark line as lastMentions
const result = [...tree].map(process) const result = [...tree].map(process)
if (
handleLinks && // Do we handle links at all?
mentionsNum > 1 && // Does it have more than one mention?
!hasLooseText && // Don't do anything if it has something besides mentions
nonEmptyIndex === 0 && // Only check last (first since list is reversed) line
nonEmptyIndex !== linesNum - 1 // Don't do anything if there's only one line
) {
let mentionIndex = 0
const process = (item) => {
if (Array.isArray(item)) {
const [opener, children] = item
const tag = getTagName(opener)
if (tag === 'a') {
const attrs = getAttrs(opener)
lastMentions.push(getLinkData(attrs, children, mentionIndex++))
} else if (children) {
children.forEach(process)
}
}
}
result.forEach(process)
// we DO need mentions here so that we conditionally remove them if don't
// have first mentions
return ['<span class="lastMentions">', flattenDeep(result).join(''), '</span>'].join('')
} else {
return flattenDeep(result).join('') return flattenDeep(result).join('')
}
}).reverse().join('') }).reverse().join('')
return { newHtml, lastMentions } return { newHtml, lastMentions }

View file

@ -83,16 +83,6 @@
{{ $t('settings.emoji_reactions_on_timeline') }} {{ $t('settings.emoji_reactions_on_timeline') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="mentionsOwnLine">
{{ $t('settings.mentions_new_place') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="mentionsNewStyle">
{{ $t('settings.mentions_new_style') }}
</BooleanSetting>
</li>
<h3>{{ $t('settings.attachments') }}</h3> <h3>{{ $t('settings.attachments') }}</h3>
<li> <li>
<BooleanSetting path="useContainFit"> <BooleanSetting path="useContainFit">

View file

@ -176,18 +176,18 @@ const Status = {
userId: attn.id userId: attn.id
})) }))
}, },
alsoMentions () {
if (!this.headTailLinks) return []
const set = new Set(this.headTailLinks.writtenMentions.map(m => m.url))
return this.headTailLinks.writtenMentions.filter(mention => {
return !set.has(mention.url)
})
},
mentionsLine () { mentionsLine () {
return this.mentionsOwnLine ? this.mentions : this.alsoMentions const writtenMentions = this.headTailLinks ? this.headTailLinks.writtenMentions : []
}, const set = new Set(writtenMentions.map(_ => _.url))
mentionsOwnLine () { return this.status.attentions.filter(attn => {
return this.mergedConfig.mentionsOwnLine return attn.screen_name !== this.replyToName &&
attn.screen_name !== this.status.user.screen_name &&
!set.has(attn.url)
}).map(attn => ({
url: attn.statusnet_profile_url,
content: attn.screen_name,
userId: attn.id
}))
}, },
hasMentionsLine () { hasMentionsLine () {
return this.mentionsLine.length > 0 return this.mentionsLine.length > 0

View file

@ -306,7 +306,6 @@
:no-heading="noHeading" :no-heading="noHeading"
:highlight="highlight" :highlight="highlight"
:focused="isFocused" :focused="isFocused"
:hide-mentions="mentionsOwnLine && (isReply || true)"
@mediaplay="addMediaPlaying($event)" @mediaplay="addMediaPlaying($event)"
@mediapause="removeMediaPlaying($event)" @mediapause="removeMediaPlaying($event)"
@parseReady="setHeadTailLinks" @parseReady="setHeadTailLinks"

View file

@ -26,8 +26,7 @@ const StatusContent = {
'focused', 'focused',
'noHeading', 'noHeading',
'fullContent', 'fullContent',
'singleLine', 'singleLine'
'hideMentions'
], ],
data () { data () {
return { return {

View file

@ -48,7 +48,6 @@
:html="status.raw_html" :html="status.raw_html"
:emoji="status.emojis" :emoji="status.emojis"
:handle-links="true" :handle-links="true"
:hide-mentions="hideMentions"
:greentext="mergedConfig.greentext" :greentext="mergedConfig.greentext"
:attentions="status.attentions" :attentions="status.attentions"
@parseReady="onParseReady" @parseReady="onParseReady"

View file

@ -31,8 +31,7 @@ const StatusContent = {
'focused', 'focused',
'noHeading', 'noHeading',
'fullContent', 'fullContent',
'singleLine', 'singleLine'
'hideMentions'
], ],
computed: { computed: {
hideAttachments () { hideAttachments () {

View file

@ -8,7 +8,6 @@
:status="status" :status="status"
:compact="compact" :compact="compact"
:single-line="singleLine" :single-line="singleLine"
:hide-mentions="hideMentions"
@parseReady="$emit('parseReady', $event)" @parseReady="$emit('parseReady', $event)"
> >
<div v-if="status.poll && status.poll.options"> <div v-if="status.poll && status.poll.options">

View file

@ -57,8 +57,6 @@ export const defaultState = {
interfaceLanguage: browserLocale, interfaceLanguage: browserLocale,
hideScopeNotice: false, hideScopeNotice: false,
useStreamingApi: false, useStreamingApi: false,
mentionsOwnLine: false,
mentionsNewStyle: false,
sidebarRight: undefined, // instance default sidebarRight: undefined, // instance default
scopeCopy: undefined, // instance default scopeCopy: undefined, // instance default
subjectLineBehavior: undefined, // instance default subjectLineBehavior: undefined, // instance default

View file

@ -8,11 +8,13 @@ const makeMention = (who) => {
attentions.push({ statusnet_profile_url: `https://fake.tld/@${who}` }) attentions.push({ statusnet_profile_url: `https://fake.tld/@${who}` })
return `<span class="h-card"><a class="u-url mention" href="https://fake.tld/@${who}">@<span>${who}</span></a></span>` return `<span class="h-card"><a class="u-url mention" href="https://fake.tld/@${who}">@<span>${who}</span></a></span>`
} }
const stubMention = (who) => `<span class="h-card"><mentionlink-stub url="https://fake.tld/@${who}" content="@<span>${who}</span>"></mentionlink-stub></span>`
const lastMentions = (...data) => `<span class="lastMentions">${data.join('')}</span>`
const p = (...data) => `<p>${data.join('')}</p>` const p = (...data) => `<p>${data.join('')}</p>`
const compwrap = (...data) => `<span class="RichContent">${data.join('')}</span>` const compwrap = (...data) => `<span class="RichContent">${data.join('')}</span>`
const removedMentionSpan = '<span class="h-card"></span>' const mentionsLine = (times) => [
'<mentionsline-stub mentions="',
new Array(times).fill('[object Object]').join(','),
'"></mentionsline-stub>'
].join('')
describe('RichContent', () => { describe('RichContent', () => {
it('renders simple post without exploding', () => { it('renders simple post without exploding', () => {
@ -21,7 +23,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -45,7 +46,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -56,20 +56,15 @@ describe('RichContent', () => {
expect(wrapper.html()).to.eql(compwrap(expected)) expect(wrapper.html()).to.eql(compwrap(expected))
}) })
it('removes mentions from the beginning of post', () => { it('replaces mention with mentionsline', () => {
const html = p( const html = p(
makeMention('John'), makeMention('John'),
' how are you doing thoday?' ' how are you doing today?'
)
const expected = p(
removedMentionSpan,
'how are you doing thoday?'
) )
const wrapper = shallowMount(RichContent, { const wrapper = shallowMount(RichContent, {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -77,70 +72,13 @@ describe('RichContent', () => {
} }
}) })
expect(wrapper.html()).to.eql(compwrap(expected)) expect(wrapper.html()).to.eql(compwrap(p(
mentionsLine(1),
' how are you doing today?'
)))
}) })
it('replaces first mention with mentionsline if hideMentions=false', () => { it('replaces mentions at the end of the hellpost', () => {
const html = p(
makeMention('John'),
' how are you doing thoday?'
)
const expected = p(
'<span class="h-card">',
'<mentionsline-stub mentions="',
'[object Object]',
'"></mentionsline-stub>',
'</span>',
'how are you doing thoday?'
)
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: false,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('removes mentions from the end of the hellpost (<p>)', () => {
const html = [
p('How are you doing today, fine gentlemen?'),
p(
makeMention('John'),
makeMention('Josh'),
makeMention('Jeremy')
)
].join('')
const expected = [
p(
'How are you doing today, fine gentlemen?'
),
// TODO fix this extra line somehow?
p()
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('replaces mentions at the end of the hellpost if hideMentions=false (<p>)', () => {
const html = [ const html = [
p('How are you doing today, fine gentlemen?'), p('How are you doing today, fine gentlemen?'),
p( p(
@ -167,189 +105,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: false,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('removes mentions from the end of the hellpost (<br>)', () => {
const html = [
'How are you doing today, fine gentlemen?',
[
makeMention('John'),
makeMention('Josh'),
makeMention('Jeremy')
].join('')
].join('<br>')
const expected = [
'How are you doing today, fine gentlemen?',
// TODO fix this extra line somehow?
'<br>'
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('removes mentions from the end of the hellpost (\\n)', () => {
const html = [
'How are you doing today, fine gentlemen?',
[
makeMention('John'),
makeMention('Josh'),
makeMention('Jeremy')
].join('')
].join('\n')
const expected = [
'How are you doing today, fine gentlemen?',
// TODO fix this extra line somehow?
''
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('Does not remove mentions in the middle or at the end of text string', () => {
const html = [
[
makeMention('Jack'),
'let\'s meet up with ',
makeMention('Janet')
].join(''),
[
'cc: ',
makeMention('John'),
makeMention('Josh'),
makeMention('Jeremy')
].join('')
].join('\n')
const expected = [
[
removedMentionSpan,
'let\'s meet up with ',
stubMention('Janet')
].join(''),
[
'cc: ',
stubMention('John'),
stubMention('Josh'),
stubMention('Jeremy')
].join('')
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('removes mentions from the end if there\'s only one first mention', () => {
const html = [
p(
makeMention('Todd'),
'so anyway you are wrong'
),
p(
makeMention('Tom'),
makeMention('Trace'),
makeMention('Theodor')
)
].join('')
const expected = [
p(
removedMentionSpan,
'so anyway you are wrong'
),
// TODO fix this extra line somehow?
p()
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('does not remove mentions from the end if there\'s more than one first mention', () => {
const html = [
p(
makeMention('Zacharie'),
makeMention('Zinaide'),
'you guys have cool names, and so do these guys: '
),
p(
makeMention('Watson'),
makeMention('Wallace'),
makeMention('Wakamoto')
)
].join('')
const expected = [
p(
removedMentionSpan,
removedMentionSpan,
'you guys have cool names, and so do these guys: '
),
p(
lastMentions(
stubMention('Watson'),
stubMention('Wallace'),
stubMention('Wakamoto')
)
)
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -378,7 +133,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: false, handleLinks: false,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -403,7 +157,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: false, handleLinks: false,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -424,7 +177,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: false, handleLinks: false,
greentext: false, greentext: false,
emoji: [], emoji: [],
@ -446,7 +198,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: false, handleLinks: false,
greentext: false, greentext: false,
emoji: [{ url: 'about:blank', shortcode: 'spurdo' }], emoji: [{ url: 'about:blank', shortcode: 'spurdo' }],
@ -464,7 +215,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: false, handleLinks: false,
greentext: false, greentext: false,
emoji: [], emoji: [],
@ -484,7 +234,7 @@ describe('RichContent', () => {
].join('\n') ].join('\n')
const expected = [ const expected = [
'<span class="greentext">&gt;quote</span>', '<span class="greentext">&gt;quote</span>',
stubMention('lol'), mentionsLine(1),
'<span class="greentext">&gt;quote</span>', '<span class="greentext">&gt;quote</span>',
'<span class="greentext">&gt;quote</span>' '<span class="greentext">&gt;quote</span>'
].join('\n') ].join('\n')
@ -517,11 +267,7 @@ describe('RichContent', () => {
const expected = [ const expected = [
'Bruh', 'Bruh',
'Bruh', 'Bruh',
[ mentionsLine(3),
stubMention('foo'),
stubMention('bar'),
stubMention('baz')
].join(''),
'Bruh' 'Bruh'
].join('<br>') ].join('<br>')
@ -529,119 +275,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('Don\'t remove last mention if it\'s the only one', () => {
const html = [
'Bruh',
'Bruh',
makeMention('foo'),
makeMention('bar'),
makeMention('baz')
].join('<br>')
const expected = [
'Bruh',
'Bruh',
stubMention('foo'),
stubMention('bar'),
stubMention('baz')
].join('<br>')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('Don\'t remove last mentions if there are more than one first mention - remove first instead', () => {
const html = [
[
makeMention('foo'),
makeMention('bar')
].join(' '),
'Bruh',
'Bruh',
[
makeMention('foo'),
makeMention('bar'),
makeMention('baz')
].join(' ')
].join('\n')
const expected = [
[
removedMentionSpan,
removedMentionSpan,
'Bruh' // Due to trim we remove extra newline
].join(''),
'Bruh',
lastMentions([
stubMention('foo'),
stubMention('bar'),
stubMention('baz')
].join(' '))
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('Remove last mentions if there\'s just one first mention - remove all', () => {
const html = [
[
makeMention('foo')
].join(' '),
'Bruh',
'Bruh',
[
makeMention('foo'),
makeMention('bar'),
makeMention('baz')
].join(' ')
].join('\n')
const expected = [
[
removedMentionSpan,
'Bruh' // Due to trim we remove extra newline
].join(''),
'Bruh\n' // Can't remove this one yet
].join('\n')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: true,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -678,7 +311,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: true,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -690,54 +322,6 @@ describe('RichContent', () => {
}) })
it('rich contents of a mention are handled properly', () => { it('rich contents of a mention are handled properly', () => {
const html = [
p(
'Testing'
),
p(
'<a href="lol" class="mention">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>'
)
].join('')
const expected = [
p(
'Testing'
),
p(
'<mentionlink-stub url="lol" content="',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'">',
'</mentionlink-stub>'
)
].join('')
const wrapper = shallowMount(RichContent, {
localVue,
propsData: {
attentions,
hideMentions: false,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('rich contents of a mention in beginning are handled properly', () => {
attentions.push({ statusnet_profile_url: 'lol' }) attentions.push({ statusnet_profile_url: 'lol' })
const html = [ const html = [
p( p(
@ -757,16 +341,19 @@ describe('RichContent', () => {
const expected = [ const expected = [
p( p(
'<span class="MentionsLine">', '<span class="MentionsLine">',
'<mentionlink-stub content="', '<span class="MentionLink mention-link">',
'<a href="lol" target="_blank" class="original">',
'<span>', '<span>',
'https://</span>', 'https://</span>',
'<span>', '<span>',
'lol.tld/</span>', 'lol.tld/</span>',
'<span>', '<span>',
'</span>', '</span>',
'" url="lol" class="mention-link">', '</a>',
'</mentionlink-stub>', ' ',
'<!---->', // v-if placeholder '<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>',
'<!---->', // v-if placeholder, mentionsline's extra mentions and stuff
'</span>' '</span>'
), ),
p( p(
@ -776,12 +363,8 @@ describe('RichContent', () => {
const wrapper = mount(RichContent, { const wrapper = mount(RichContent, {
localVue, localVue,
stubs: {
MentionLink: true
},
propsData: { propsData: {
attentions, attentions,
hideMentions: false,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -826,7 +409,6 @@ describe('RichContent', () => {
localVue, localVue,
propsData: { propsData: {
attentions, attentions,
hideMentions: false,
handleLinks: true, handleLinks: true,
greentext: true, greentext: true,
emoji: [], emoji: [],
@ -836,4 +418,63 @@ describe('RichContent', () => {
expect(wrapper.html()).to.eql(compwrap(expected)) expect(wrapper.html()).to.eql(compwrap(expected))
}) })
it.skip('[INFORMATIVE] Performance testing, 10 000 simple posts', () => {
const amount = 20
const onePost = p(
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
makeMention('Lain'),
' i just landed in l a where are you'
)
const TestComponent = {
template: `
<div v-if="!vhtml">
${new Array(amount).fill(`<RichContent html="${onePost}" :greentext="true" :handleLinks="handeLinks" :emoji="[]" :attentions="attentions"/>`)}
</div>
<div v-else="vhtml">
${new Array(amount).fill(`<div v-html="${onePost}"/>`)}
</div>
`,
props: ['handleLinks', 'attentions', 'vhtml']
}
console.log(1)
const ptest = (handleLinks, vhtml) => {
const t0 = performance.now()
const wrapper = mount(TestComponent, {
localVue,
propsData: {
attentions,
handleLinks,
vhtml
}
})
const t1 = performance.now()
wrapper.destroy()
const t2 = performance.now()
return `Mount: ${t1 - t0}ms, destroy: ${t2 - t1}ms, avg ${(t1 - t0) / amount}ms - ${(t2 - t1) / amount}ms per item`
}
console.log(`${amount} items with links handling:`)
console.log(ptest(true))
console.log(`${amount} items without links handling:`)
console.log(ptest(false))
console.log(`${amount} items plain v-html:`)
console.log(ptest(false, true))
})
}) })