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:
commit
f96ffe3699
16 changed files with 132 additions and 646 deletions
|
|
@ -41,7 +41,7 @@ const MentionLink = {
|
|||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.url && this.$store.getters.findUserByUrl(this.url)
|
||||
return this.url && this.$store && this.$store.getters.findUserByUrl(this.url)
|
||||
},
|
||||
isYou () {
|
||||
// FIXME why user !== currentUser???
|
||||
|
|
@ -65,9 +65,6 @@ const MentionLink = {
|
|||
highlightClass () {
|
||||
if (this.highlight) return highlightClass(this.user)
|
||||
},
|
||||
oldStyle () {
|
||||
return !this.mergedConfig.mentionsNewStyle
|
||||
},
|
||||
style () {
|
||||
if (this.highlight) {
|
||||
const {
|
||||
|
|
@ -83,8 +80,7 @@ const MentionLink = {
|
|||
return [
|
||||
{
|
||||
'-you': this.isYou,
|
||||
'-highlighted': this.highlight,
|
||||
'-oldStyle': this.oldStyle
|
||||
'-highlighted': this.highlight
|
||||
},
|
||||
this.highlightType
|
||||
]
|
||||
|
|
|
|||
|
|
@ -10,10 +10,6 @@
|
|||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.original {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.full {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
|
|
@ -41,8 +37,6 @@
|
|||
}
|
||||
|
||||
.new {
|
||||
margin-right: 0.25em;
|
||||
|
||||
&.-you {
|
||||
& .shortName,
|
||||
& .full {
|
||||
|
|
@ -61,41 +55,6 @@
|
|||
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 {
|
||||
& .userName,
|
||||
& .full {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@
|
|||
:class="classnames"
|
||||
>
|
||||
<button
|
||||
class="short"
|
||||
:class="[{ '-sublime': !highlight }, oldStyle ? 'button-unstyled' : 'button-default']"
|
||||
class="short button-unstyled"
|
||||
@click.prevent="onClick"
|
||||
>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
|
|
|
|||
|
|
@ -14,11 +14,8 @@ const MentionsLine = {
|
|||
MentionLink
|
||||
},
|
||||
computed: {
|
||||
oldStyle () {
|
||||
return !this.mergedConfig.mentionsNewStyle
|
||||
},
|
||||
limit () {
|
||||
return 6
|
||||
return 5
|
||||
},
|
||||
mentionsComputed () {
|
||||
return this.mentions.slice(0, this.limit)
|
||||
|
|
@ -29,16 +26,6 @@ const MentionsLine = {
|
|||
manyMentions () {
|
||||
return this.extraMentions.length > 0
|
||||
},
|
||||
buttonClasses () {
|
||||
return [
|
||||
this.oldStyle
|
||||
? 'button-unstyled'
|
||||
: 'button-default -sublime',
|
||||
this.oldStyle
|
||||
? '-oldStyle'
|
||||
: '-newStyle'
|
||||
]
|
||||
},
|
||||
...mapGetters(['mergedConfig'])
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
.MentionsLine {
|
||||
.showMoreLess {
|
||||
white-space: normal;
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
&.-newStyle {
|
||||
line-height: 1.5;
|
||||
font-size: inherit;
|
||||
display: inline-block;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.-oldStyle {
|
||||
color: var(--link);
|
||||
}
|
||||
.mention-link:not(:last-child) {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,15 +25,13 @@
|
|||
/>
|
||||
</span><button
|
||||
v-if="!expanded"
|
||||
class="showMoreLess"
|
||||
:class="buttonClasses"
|
||||
class="button-unstyled showMoreLess"
|
||||
@click="toggleShowMore"
|
||||
>
|
||||
{{ $t('status.plus_more', { number: extraMentions.length }) }}
|
||||
</button><button
|
||||
v-if="expanded"
|
||||
class="showMoreLess"
|
||||
:class="buttonClasses"
|
||||
class="button-unstyled showMoreLess"
|
||||
@click="toggleShowMore"
|
||||
>
|
||||
{{ $t('general.show_less') }}
|
||||
|
|
|
|||
|
|
@ -56,25 +56,21 @@ export default Vue.component('RichContent', {
|
|||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hideMentions: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
// NEVER EVER TOUCH DATA INSIDE RENDER
|
||||
render (h) {
|
||||
// Pre-process HTML
|
||||
const { newHtml: html, lastMentions } = preProcessPerLine(this.html, this.greentext, this.handleLinks)
|
||||
const firstMentions = [] // Mentions that appear in the beginning of post body
|
||||
const { newHtml: html } = preProcessPerLine(this.html, this.greentext, this.handleLinks)
|
||||
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 writtenMentions = [] // All mentions that appear in post body
|
||||
const writtenTags = [] // All tags that appear in post body
|
||||
// unique index for vue "tag" property
|
||||
let mentionIndex = 0
|
||||
let tagsIndex = 0
|
||||
let firstMentionReplaced = false
|
||||
|
||||
const renderImage = (tag) => {
|
||||
return <StillImage
|
||||
|
|
@ -98,41 +94,32 @@ export default Vue.component('RichContent', {
|
|||
const renderMention = (attrs, children) => {
|
||||
const linkData = getLinkData(attrs, children, mentionIndex++)
|
||||
linkData.notifying = this.attentions.some(a => a.statusnet_profile_url === linkData.url)
|
||||
if (!linkData.notifying) {
|
||||
encounteredText = true
|
||||
}
|
||||
writtenMentions.push(linkData)
|
||||
if (!encounteredText) {
|
||||
firstMentions.push(linkData)
|
||||
if (!firstMentionReplaced && !this.hideMentions) {
|
||||
firstMentionReplaced = true
|
||||
return <MentionsLine mentions={ firstMentions } />
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
if (currentMentions === null) {
|
||||
currentMentions = []
|
||||
}
|
||||
currentMentions.push(linkData)
|
||||
if (currentMentions.length === 1) {
|
||||
return <MentionsLine mentions={ currentMentions } />
|
||||
} else {
|
||||
return <MentionLink
|
||||
url={attrs.href}
|
||||
content={flattenDeep(children).join('')}
|
||||
/>
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// We stop treating mentions as "first" ones when we encounter
|
||||
// non-whitespace text
|
||||
let encounteredText = false
|
||||
// Processor to use with html_tree_converter
|
||||
const processItem = (item, index, array, what) => {
|
||||
// Handle text nodes - just add emoji
|
||||
if (typeof item === 'string') {
|
||||
const emptyText = item.trim() === ''
|
||||
if (item.includes('\n')) {
|
||||
currentMentions = null
|
||||
}
|
||||
if (emptyText) {
|
||||
return encounteredText ? item : item.trim()
|
||||
}
|
||||
if (!encounteredText) {
|
||||
item = item.trimStart()
|
||||
encounteredText = true
|
||||
// don't include spaces when processing mentions - we'll include them
|
||||
// in MentionsLine
|
||||
return currentMentions !== null ? item.trim() : item
|
||||
}
|
||||
currentMentions = null
|
||||
if (item.includes(':')) {
|
||||
item = ['', processTextForEmoji(
|
||||
item,
|
||||
|
|
@ -156,28 +143,25 @@ export default Vue.component('RichContent', {
|
|||
const Tag = getTagName(opener)
|
||||
const attrs = getAttrs(opener)
|
||||
switch (Tag) {
|
||||
case 'span': // Replace last mentions class with mentionsline
|
||||
if (attrs['class'] && attrs['class'].includes('lastMentions')) {
|
||||
if (firstMentions.length > 1 && lastMentions.length > 1) {
|
||||
break
|
||||
} else {
|
||||
return !this.hideMentions ? <MentionsLine mentions={lastMentions} /> : ''
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
case 'br':
|
||||
currentMentions = null
|
||||
break
|
||||
case 'img': // replace images with StillImage
|
||||
return renderImage(opener)
|
||||
case 'a': // replace mentions with MentionLink
|
||||
if (!this.handleLinks) break
|
||||
if (attrs['class'] && attrs['class'].includes('mention')) {
|
||||
// Handling mentions here
|
||||
return renderMention(attrs, children, encounteredText)
|
||||
return renderMention(attrs, children)
|
||||
} else {
|
||||
// Everything else will be handled in reverse pass
|
||||
encounteredText = true
|
||||
currentMentions = null
|
||||
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) {
|
||||
|
|
@ -246,8 +230,6 @@ export default Vue.component('RichContent', {
|
|||
</span>
|
||||
|
||||
const event = {
|
||||
firstMentions,
|
||||
lastMentions,
|
||||
lastTags,
|
||||
writtenMentions,
|
||||
writtenTags
|
||||
|
|
@ -284,15 +266,12 @@ export const preProcessPerLine = (html, greentext, handleLinks) => {
|
|||
const lastMentions = []
|
||||
const greentextHandle = new Set(['p', 'div'])
|
||||
|
||||
let nonEmptyIndex = -1
|
||||
const lines = convertHtmlToLines(html)
|
||||
const linesNum = lines.filter(c => c.text).length
|
||||
const newHtml = lines.reverse().map((item, index, array) => {
|
||||
// Going over each line in reverse to detect last mentions,
|
||||
// keeping non-text stuff as-is
|
||||
if (!item.text) return item
|
||||
const string = item.text
|
||||
nonEmptyIndex += 1
|
||||
|
||||
// Greentext stuff
|
||||
if (
|
||||
|
|
@ -316,42 +295,19 @@ export const preProcessPerLine = (html, greentext, handleLinks) => {
|
|||
// Converting that line part into tree
|
||||
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) => {
|
||||
if (Array.isArray(item)) {
|
||||
const [opener, children, closer] = item
|
||||
const tag = getTagName(opener)
|
||||
// If we have a link we probably have mentions
|
||||
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') {
|
||||
if (tag === 'span' || tag === 'p') {
|
||||
// For span and p we need to go deeper
|
||||
return [opener, [...children].map(process), closer]
|
||||
} else {
|
||||
// Everything else equals to a loose text
|
||||
hasLooseText = true
|
||||
return [opener, children, closer]
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof item === 'string') {
|
||||
if (item.trim() !== '') {
|
||||
// only meaningful strings are loose text
|
||||
hasLooseText = true
|
||||
}
|
||||
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
|
||||
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('')
|
||||
|
||||
return { newHtml, lastMentions }
|
||||
|
|
|
|||
|
|
@ -83,16 +83,6 @@
|
|||
{{ $t('settings.emoji_reactions_on_timeline') }}
|
||||
</BooleanSetting>
|
||||
</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>
|
||||
<li>
|
||||
<BooleanSetting path="useContainFit">
|
||||
|
|
|
|||
|
|
@ -176,18 +176,18 @@ const Status = {
|
|||
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 () {
|
||||
return this.mentionsOwnLine ? this.mentions : this.alsoMentions
|
||||
},
|
||||
mentionsOwnLine () {
|
||||
return this.mergedConfig.mentionsOwnLine
|
||||
const writtenMentions = this.headTailLinks ? this.headTailLinks.writtenMentions : []
|
||||
const set = new Set(writtenMentions.map(_ => _.url))
|
||||
return this.status.attentions.filter(attn => {
|
||||
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 () {
|
||||
return this.mentionsLine.length > 0
|
||||
|
|
|
|||
|
|
@ -306,7 +306,6 @@
|
|||
:no-heading="noHeading"
|
||||
:highlight="highlight"
|
||||
:focused="isFocused"
|
||||
:hide-mentions="mentionsOwnLine && (isReply || true)"
|
||||
@mediaplay="addMediaPlaying($event)"
|
||||
@mediapause="removeMediaPlaying($event)"
|
||||
@parseReady="setHeadTailLinks"
|
||||
|
|
|
|||
|
|
@ -26,8 +26,7 @@ const StatusContent = {
|
|||
'focused',
|
||||
'noHeading',
|
||||
'fullContent',
|
||||
'singleLine',
|
||||
'hideMentions'
|
||||
'singleLine'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
:html="status.raw_html"
|
||||
:emoji="status.emojis"
|
||||
:handle-links="true"
|
||||
:hide-mentions="hideMentions"
|
||||
:greentext="mergedConfig.greentext"
|
||||
:attentions="status.attentions"
|
||||
@parseReady="onParseReady"
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ const StatusContent = {
|
|||
'focused',
|
||||
'noHeading',
|
||||
'fullContent',
|
||||
'singleLine',
|
||||
'hideMentions'
|
||||
'singleLine'
|
||||
],
|
||||
computed: {
|
||||
hideAttachments () {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
:status="status"
|
||||
:compact="compact"
|
||||
:single-line="singleLine"
|
||||
:hide-mentions="hideMentions"
|
||||
@parseReady="$emit('parseReady', $event)"
|
||||
>
|
||||
<div v-if="status.poll && status.poll.options">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue