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:
parent
a0eaac2216
commit
2cfff1b8b9
@ -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
|
||||||
]
|
]
|
||||||
|
@ -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 {
|
||||||
|
@ -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 -->
|
||||||
|
@ -14,11 +14,8 @@ const MentionsLine = {
|
|||||||
MentionLink
|
MentionLink
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
oldStyle () {
|
|
||||||
return !this.mergedConfig.mentionsNewStyle
|
|
||||||
},
|
|
||||||
limit () {
|
limit () {
|
||||||
return 6
|
return 2
|
||||||
},
|
},
|
||||||
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: {
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
.MentionsLine {
|
.MentionsLine {
|
||||||
.showMoreLess {
|
.showMoreLess {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
&.-newStyle {
|
.mention-link:not(:last-child) {
|
||||||
line-height: 1.5;
|
margin-right: 0.25em;
|
||||||
font-size: inherit;
|
|
||||||
display: inline-block;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.-oldStyle {
|
|
||||||
color: var(--link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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') }}
|
||||||
|
@ -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) {
|
||||||
} else {
|
return <MentionsLine mentions={ currentMentions } />
|
||||||
return ''
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return <MentionLink
|
return ''
|
||||||
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 (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 (
|
return flattenDeep(result).join('')
|
||||||
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('')
|
|
||||||
}
|
|
||||||
}).reverse().join('')
|
}).reverse().join('')
|
||||||
|
|
||||||
return { newHtml, lastMentions }
|
return { newHtml, lastMentions }
|
||||||
|
@ -41,16 +41,6 @@
|
|||||||
{{ $t('settings.collapse_subject') }}
|
{{ $t('settings.collapse_subject') }}
|
||||||
</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>
|
|
||||||
<li>
|
<li>
|
||||||
<BooleanSetting path="streaming">
|
<BooleanSetting path="streaming">
|
||||||
{{ $t('settings.streaming') }}
|
{{ $t('settings.streaming') }}
|
||||||
|
@ -184,10 +184,7 @@ const Status = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
mentionsLine () {
|
mentionsLine () {
|
||||||
return this.mentionsOwnLine ? this.mentions : this.alsoMentions
|
return this.alsoMentions
|
||||||
},
|
|
||||||
mentionsOwnLine () {
|
|
||||||
return this.mergedConfig.mentionsOwnLine
|
|
||||||
},
|
},
|
||||||
hasMentionsLine () {
|
hasMentionsLine () {
|
||||||
return this.mentionsLine.length > 0
|
return this.mentionsLine.length > 0
|
||||||
|
@ -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"
|
||||||
|
@ -25,8 +25,7 @@ const StatusContent = {
|
|||||||
'focused',
|
'focused',
|
||||||
'noHeading',
|
'noHeading',
|
||||||
'fullContent',
|
'fullContent',
|
||||||
'singleLine',
|
'singleLine'
|
||||||
'hideMentions'
|
|
||||||
],
|
],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -45,7 +45,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"
|
||||||
|
@ -31,8 +31,7 @@ const StatusContent = {
|
|||||||
'focused',
|
'focused',
|
||||||
'noHeading',
|
'noHeading',
|
||||||
'fullContent',
|
'fullContent',
|
||||||
'singleLine',
|
'singleLine'
|
||||||
'hideMentions'
|
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
hideAttachments () {
|
hideAttachments () {
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
<StatusBody
|
<StatusBody
|
||||||
:status="status"
|
:status="status"
|
||||||
: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">
|
||||||
|
@ -55,8 +55,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
|
||||||
|
@ -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 first 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">>quote</span>',
|
'<span class="greentext">>quote</span>',
|
||||||
stubMention('lol'),
|
mentionsLine(1)
|
||||||
'<span class="greentext">>quote</span>',
|
'<span class="greentext">>quote</span>',
|
||||||
'<span class="greentext">>quote</span>'
|
'<span class="greentext">>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: [],
|
||||||
@ -726,7 +358,6 @@ describe('RichContent', () => {
|
|||||||
localVue,
|
localVue,
|
||||||
propsData: {
|
propsData: {
|
||||||
attentions,
|
attentions,
|
||||||
hideMentions: false,
|
|
||||||
handleLinks: true,
|
handleLinks: true,
|
||||||
greentext: true,
|
greentext: true,
|
||||||
emoji: [],
|
emoji: [],
|
||||||
@ -781,7 +412,6 @@ describe('RichContent', () => {
|
|||||||
},
|
},
|
||||||
propsData: {
|
propsData: {
|
||||||
attentions,
|
attentions,
|
||||||
hideMentions: false,
|
|
||||||
handleLinks: true,
|
handleLinks: true,
|
||||||
greentext: true,
|
greentext: true,
|
||||||
emoji: [],
|
emoji: [],
|
||||||
@ -826,7 +456,6 @@ describe('RichContent', () => {
|
|||||||
localVue,
|
localVue,
|
||||||
propsData: {
|
propsData: {
|
||||||
attentions,
|
attentions,
|
||||||
hideMentions: false,
|
|
||||||
handleLinks: true,
|
handleLinks: true,
|
||||||
greentext: true,
|
greentext: true,
|
||||||
emoji: [],
|
emoji: [],
|
||||||
|
Loading…
Reference in New Issue
Block a user