Merge branch 'better-still-emoji' into shigusegubu

* better-still-emoji:
  lint & cleanup
  fix #935
  fixed console errors, improved user-selecting, added cyantexting
  fix infinite loop
  better handling of hellthreads with mentions at bottom
  stylistic changes
  Hellthread(tm) Certified
  don't hide mentions for OPs
  mentions on same line as replies
  use icon instead of symbol for @ in mentions links
  stylistic improvements for single-line mentions
  proper cachin of headTailLinks, show mentions in notificaitons always
  moved mentions onto reply line, replies moved below post body
  Moved greentext to RichContent, improved how first mentions are restored, now shows mentions not uh, mention in post body
This commit is contained in:
Henry Jameson 2021-06-11 12:20:10 +03:00
commit 3fbf6fc5ac
21 changed files with 618 additions and 330 deletions

View file

@ -1,11 +1,20 @@
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { mapGetters, mapState } from 'vuex'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faAt
} from '@fortawesome/free-solid-svg-icons'
library.add(
faAt
)
const MentionLink = {
name: 'MentionLink',
props: {
url: {
required: true,
type: String
},
content: {
@ -19,11 +28,6 @@ const MentionLink = {
userScreenName: {
required: false,
type: String
},
firstMention: {
required: false,
type: Boolean,
default: false
}
},
methods: {
@ -61,9 +65,6 @@ const MentionLink = {
highlightClass () {
if (this.highlight) return highlightClass(this.user)
},
oldPlace () {
return !this.mergedConfig.mentionsOwnLine
},
oldStyle () {
return !this.mergedConfig.mentionsNewStyle
},
@ -83,7 +84,6 @@ const MentionLink = {
{
'-you': this.isYou,
'-highlighted': this.highlight,
'-firstMention': this.firstMention,
'-oldStyle': this.oldStyle
},
this.highlightType

View file

@ -28,22 +28,21 @@
z-index: 1;
margin-top: 0.25em;
padding: 0.5em;
user-select: all;
}
.short {
user-select: none;
}
& .short,
& .full {
&::before {
content: '@';
}
white-space: nowrap;
}
.new {
margin-right: 0.25em;
&.-firstMention {
display: none;
}
&.-you {
& .shortName,
& .full {
@ -51,17 +50,31 @@
}
}
.at {
color: var(--link);
opacity: 0.8;
display: inline-block;
height: 50%;
line-height: 1;
padding: 0 0.1em;
vertical-align: -25%;
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;
&::before {
.at {
color: var(--faint);
display: inline-block;
height: 50%;
line-height: 1;
vertical-align: 6%;
opacity: 0.8;
padding-right: 0.25em;
vertical-align: -20%;
}
}
@ -69,18 +82,11 @@
padding-right: 0.25em;
}
.short {
padding-left: 0.25em;
padding-right: 0;
padding-top: 0;
padding-bottom: 0;
}
.userName {
display: inline-block;
color: var(--link);
line-height: inherit;
margin-left: 0.125em;
margin-left: 0;
padding-left: 0.125em;
padding-right: 0.25em;
padding-top: 0;

View file

@ -1,7 +1,6 @@
<template>
<span
class="MentionLink"
:class="{ '-oldPlace': oldPlace }"
>
<!-- eslint-disable vue/no-v-html -->
<a
@ -23,7 +22,11 @@
@click.prevent="onClick"
>
<!-- eslint-disable vue/no-v-html -->
<span class="shortName"><span
<FAIcon
size="sm"
icon="at"
class="at"
/><span class="shortName"><span
class="userName"
v-html="userName"
/></span>
@ -41,7 +44,7 @@
<!-- eslint-disable vue/no-v-html -->
<span
class="userNameFull"
v-html="userNameFull"
v-text="'@' + userNameFull"
/>
<!-- eslint-enable vue/no-v-html -->
</span>

View file

@ -4,7 +4,7 @@ import { mapGetters } from 'vuex'
const MentionsLine = {
name: 'MentionsLine',
props: {
attentions: {
mentions: {
required: true,
type: Array
}
@ -20,11 +20,11 @@ const MentionsLine = {
limit () {
return 6
},
mentions () {
return this.attentions.slice(0, this.limit)
mentionsComputed () {
return this.mentions.slice(0, this.limit)
},
extraMentions () {
return this.attentions.slice(this.limit)
return this.mentions.slice(this.limit)
},
manyMentions () {
return this.extraMentions.length > 0

View file

@ -1,5 +1,6 @@
.MentionsLine {
.showMoreLess {
white-space: normal;
&.-newStyle {
line-height: 1.5;
font-size: inherit;

View file

@ -1,11 +1,11 @@
<template>
<span class="MentionsLine">
<MentionLink
v-for="mention in mentions"
:key="mention.statusnet_profile_url"
v-for="mention in mentionsComputed"
:key="mention.index"
class="mention-link"
:content="mention.statusnet_profile_url"
:url="mention.statusnet_profile_url"
:content="mention.content"
:url="mention.url"
:first-mention="false"
/><span
v-if="manyMentions"
@ -17,10 +17,10 @@
>
<MentionLink
v-for="mention in extraMentions"
:key="mention.statusnet_profile_url"
:key="mention.index"
class="mention-link"
:content="mention.statusnet_profile_url"
:url="mention.statusnet_profile_url"
:content="mention.content"
:url="mention.url"
:first-mention="false"
/>
</span><button

View file

@ -1,6 +1,7 @@
import Vue from 'vue'
import { unescape, flattenDeep } from 'lodash'
import { convertHtml, getTagName, processTextForEmoji, getAttrs } from 'src/services/mini_html_converter/mini_html_converter.service.js'
import { convertHtmlToTree, getTagName, processTextForEmoji, getAttrs } from 'src/services/html_converter/html_tree_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 MentionLink from 'src/components/mention_link/mention_link.vue'
@ -24,27 +25,72 @@ export default Vue.component('RichContent', {
required: false,
type: Boolean,
default: false
},
// Meme arrows
greentext: {
required: false,
type: Boolean,
default: false
},
// Whether to hide last mentions (hellthreads)
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.hideMentions)
const firstMentions = [] // Mentions that appear in the beginning of post body
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
const renderImage = (tag) => {
return <StillImage
{...{ attrs: getAttrs(tag) }}
class="img"
/>
}
const renderMention = (attrs, children, encounteredText) => {
return <MentionLink
url={attrs.href}
content={flattenDeep(children).join('')}
firstMention={!encounteredText}
/>
const renderHashtag = (attrs, children, encounteredTextReverse) => {
const linkData = getLinkData(attrs, children, tagsIndex++)
writtenTags.push(linkData)
attrs.target = '_blank'
if (!encounteredTextReverse) {
lastTags.push(linkData)
attrs['data-parser-last'] = true
}
return <a {...{ attrs }}>
{ children.map(processItem) }
</a>
}
const renderMention = (attrs, children, encounteredText) => {
const linkData = getLinkData(attrs, children, mentionIndex++)
writtenMentions.push(linkData)
if (!encounteredText) {
firstMentions.push(linkData)
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 mini_html_converter
const processItem = (item) => {
// Handle text noes - just add emoji
// 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 (emptyText) {
@ -56,7 +102,7 @@ export default Vue.component('RichContent', {
encounteredText = true
}
if (item.includes(':')) {
return processTextForEmoji(
unescapedItem = processTextForEmoji(
unescapedItem,
this.emoji,
({ shortcode, url }) => {
@ -68,24 +114,42 @@ export default Vue.component('RichContent', {
/>
}
)
} else {
return unescapedItem
}
return unescapedItem
}
// Handle tag nodes
if (Array.isArray(item)) {
const [opener, children] = item
const Tag = getTagName(opener)
const attrs = getAttrs(opener)
switch (Tag) {
case 'span': // replace images with StillImage
if (attrs['class'] && attrs['class'].includes('lastMentions')) {
if (firstMentions.length > 0) {
break
} else {
return ''
}
} else {
break
}
case 'img': // replace images with StillImage
return renderImage(opener)
case 'a': // replace mentions with MentionLink
if (!this.handleLinks) break
const attrs = getAttrs(opener)
if (attrs['class'] && attrs['class'].includes('mention')) {
return renderMention(attrs, children, encounteredText)
} else if (attrs['class'] && attrs['class'].includes('hashtag')) {
return item // We'll handle it later
} else {
attrs.target = '_blank'
return <a {...{ attrs }}>
{ children.map(processItem) }
</a>
}
}
// Render tag as is
if (children !== undefined) {
return <Tag {...{ attrs: getAttrs(opener) }}>
@ -96,8 +160,163 @@ export default Vue.component('RichContent', {
}
}
}
return <span class="RichContent">
{ convertHtml(this.html).map(processItem) }
// Processor for back direction (for finding "last" stuff, just easier this way)
let encounteredTextReverse = false
const processItemReverse = (item, index, array, what) => {
// Handle text nodes - just add emoji
if (typeof item === 'string') {
const emptyText = item.trim() === ''
if (emptyText) return encounteredTextReverse ? item : item.trim()
if (!encounteredTextReverse) encounteredTextReverse = true
return item
} else if (Array.isArray(item)) {
// Handle tag nodes
const [opener, children] = item
const Tag = getTagName(opener)
switch (Tag) {
case 'a': // replace mentions with MentionLink
if (!this.handleLinks) break
const attrs = getAttrs(opener)
// should only be this
if (attrs['class'] && attrs['class'].includes('hashtag')) {
return renderHashtag(attrs, children, encounteredTextReverse)
}
}
}
return item
}
// DO NOT USE SLOTS they cause a re-render feedback loop here.
// slots updated -> rerender -> emit -> update up the tree -> rerender -> ...
// at least until vue3?
const result = <span class="RichContent">
{ convertHtmlToTree(html).map(processItem).reverse().map(processItemReverse).reverse() }
</span>
const event = {
firstMentions,
lastMentions,
lastTags,
writtenMentions,
writtenTags
}
// DO NOT MOVE TO UPDATE. BAD IDEA.
this.$emit('parseReady', event)
return result
}
})
const getLinkData = (attrs, children, index) => {
return {
index,
url: attrs.href,
hashtag: attrs['data-tag'],
content: flattenDeep(children).join('')
}
}
/** Pre-processing HTML
*
* Currently this does two things:
* - add green/cyantexting
* - wrap and mark last line containing only mentions as ".lastMentionsLine" for
* more compact hellthreads.
*
* @param {String} html - raw HTML to process
* @param {Boolean} greentext - whether to enable greentexting or not
*/
export const preProcessPerLine = (html, greentext) => {
const lastMentions = []
const newHtml = convertHtmlToLines(html).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
// Greentext stuff
if (greentext && (string.includes('&gt;') || string.includes('&lt;'))) {
const cleanedString = string.replace(/<[^>]+?>/gi, '') // remove all tags
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
.trim()
if (cleanedString.startsWith('&gt;')) {
return `<span class='greentext'>${string}</span>`
} else if (cleanedString.startsWith('&lt;')) {
return `<span class='cyantext'>${string}</span>`
}
}
// 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 hasMentions = false
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') {
const attrs = getAttrs(opener)
if (attrs['class'] && attrs['class'].includes('mention')) {
// Got mentions
hasMentions = true
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
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
}
}
// We now processed our tree, now we need to mark line as lastMentions
const result = [...tree].map(process)
// Only check last (first since list is reversed) line
if (hasMentions && !hasLooseText && index === 0) {
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('')
return { newHtml, lastMentions }
}

View file

@ -100,7 +100,8 @@ const Status = {
userExpanded: false,
mediaPlaying: [],
suspendable: true,
error: null
error: null,
headTailLinks: null
}
},
computed: {
@ -141,7 +142,8 @@ const Status = {
replyProfileLink () {
if (this.isReply) {
const user = this.$store.getters.findUser(this.status.in_reply_to_user_id)
return user && user.statusnet_profile_url
// FIXME Why user not found sometimes???
return user ? user.statusnet_profile_url : 'NOT_FOUND'
}
},
retweet () { return !!this.statusoid.retweeted_status },
@ -166,17 +168,31 @@ const Status = {
muteWordHits () {
return muteWordHits(this.status, this.muteWords)
},
mentionsOwnLine () {
return this.mergedConfig.mentionsOwnLine
},
mentions () {
return this.status.attentions.filter(attn => {
return attn.screen_name !== this.replyToName &&
attn.screen_name !== this.status.user.screen_name
}).map(attn => ({
url: attn.statusnet_profile_url,
content: attn.screen_name,
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)
})
},
hasMentions () {
return this.mentions.length > 0
mentionsLine () {
return this.mentionsOwnLine ? this.mentions : this.alsoMentions
},
mentionsOwnLine () {
return this.mergedConfig.mentionsOwnLine
},
hasMentionsLine () {
return this.mentionsLine.length > 0
},
muted () {
if (this.statusoid.user.id === this.currentUser.id) return false
@ -343,6 +359,9 @@ const Status = {
},
removeMediaPlaying (id) {
this.mediaPlaying = this.mediaPlaying.filter(mediaId => mediaId !== id)
},
setHeadTailLinks (headTailLinks) {
this.headTailLinks = headTailLinks
}
},
watch: {
@ -353,7 +372,7 @@ const Status = {
// Post is above screen, match its top to screen top
window.scrollBy(0, rect.top - 100)
} else if (rect.height >= (window.innerHeight - 50)) {
// Post we want to see is taller than screen so match its top to screen top
// Post we wahttp://localhost:8080/users/hj/dmsnt to see is taller than screen so match its top to screen top
window.scrollBy(0, rect.top - 100)
} else if (rect.bottom > window.innerHeight - 50) {
// Post is below screen, match its bottom to screen bottom

View file

@ -1,4 +1,3 @@
@import '../../_variables.scss';
$status-margin: 0.75em;
@ -151,36 +150,24 @@ $status-margin: 0.75em;
}
}
.glued-label {
display: inline-flex;
white-space: nowrap;
}
.timeago {
margin-right: 0.2em;
}
& .heading-mentions-row,
& .heading-reply-row {
position: relative;
align-content: baseline;
font-size: 12px;
line-height: 18px;
line-height: 160%;
max-width: 100%;
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
.reply-to-and-accountname {
display: flex;
height: 18px;
margin-right: 0.5em;
max-width: 100%;
.reply-to-link {
white-space: nowrap;
word-break: break-word;
text-overflow: ellipsis;
overflow-x: hidden;
}
}
& .reply-to-popover,
& .reply-to-no-popover {
min-width: 0;
@ -217,21 +204,25 @@ $status-margin: 0.75em;
}
}
.reply-to {
& .mentions,
& .reply-to {
white-space: nowrap;
position: relative;
padding-right: 0.25em;
}
.reply-to-text {
& .reply-to-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.replies-separator {
margin-left: 0.4em;
.mentions-line {
display: inline;
}
.replies {
margin-top: 0.25em;
line-height: 18px;
font-size: 12px;
display: flex;

View file

@ -221,10 +221,13 @@
</button>
</span>
</div>
<div class="heading-reply-row">
<div
<div
v-if="isReply || hasMentionsLine"
class="heading-reply-row"
>
<span
v-if="isReply"
class="reply-to-and-accountname"
class="glued-label"
>
<StatusPopover
v-if="!isPreview"
@ -257,81 +260,77 @@
>
<span class="reply-to-text">{{ $t('status.reply_to') }}</span>
</span>
<MentionLink
class="mention-link"
:content="replyToName"
:url="replyProfileLink"
:user-id="status.in_reply_to_user_id"
:user-screen-name="status.in_reply_to_screen_name"
:first-mention="false"
/>
<span
v-if="replies && replies.length"
class="faint replies-separator"
>
-
</span>
</div>
<div
v-if="inConversation && !isPreview && replies && replies.length"
class="replies"
>
<span class="faint">{{ $t('status.replies_list') }}</span>
<StatusPopover
v-for="reply in replies"
:key="reply.id"
:status-id="reply.id"
>
<button
class="button-unstyled -link reply-link"
@click.prevent="gotoOriginal(reply.id)"
>
{{ reply.name }}
</button>
</StatusPopover>
</div>
</div>
</span>
<div
v-if="hasMentions && mentionsOwnLine"
class="heading-mentions-row"
>
<div
class="mentions"
<!-- This little wrapper is made for sole purpose of "gluing" -->
<!-- "Mentions" label to the first mention -->
<span
v-if="hasMentionsLine"
class="glued-label"
>
<span
class="button-unstyled reply-to"
:aria-label="$t('tool_tip.reply')"
class="mentions"
:aria-label="$t('tool_tip.mentions')"
@click.prevent="gotoOriginal(status.in_reply_to_status_id)"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="at"
/>
<span
class="faint-link reply-to-text"
class="faint-link mentions-text"
>
{{ $t('status.mentions') }}
</span>
</span>
<MentionsLine
:attentions="mentions"
class="mentions-line"
v-if="hasMentionsLine"
:mentions="mentionsLine.slice(0, 1)"
class="mentions-line-first"
/>
</div>
</span>
<MentionsLine
v-if="hasMentionsLine"
:mentions="mentionsLine.slice(1)"
class="mentions-line"
/>
</div>
</div>
<StatusContent
ref="content"
:status="status"
:no-heading="noHeading"
:highlight="highlight"
:focused="isFocused"
:hide-mentions="mentionsOwnLine && (isReply || true)"
@mediaplay="addMediaPlaying($event)"
@mediapause="removeMediaPlaying($event)"
@parseReady="setHeadTailLinks"
/>
<div
v-if="inConversation && !isPreview && replies && replies.length"
class="replies"
>
<span class="faint">{{ $t('status.replies_list') }}</span>
<StatusPopover
v-for="reply in replies"
:key="reply.id"
:status-id="reply.id"
>
<button
class="button-unstyled -link reply-link"
@click.prevent="gotoOriginal(reply.id)"
>
{{ reply.name }}
</button>
</StatusPopover>
</div>
<transition name="fade">
<div
v-if="!hidePostStats && isFocused && combinedFavsAndRepeatsUsers.length > 0"

View file

@ -1,10 +1,9 @@
import fileType from 'src/services/file_type/file_type.service'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import RichContent, { getHeadTailLinks } from 'src/components/rich_content/rich_content.jsx'
import MentionsLine from 'src/components/mentions_line/mentions_line.vue'
import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'
import { extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
import { set } from 'vue'
import {
faFile,
faMusic,
@ -28,14 +27,18 @@ const StatusContent = {
'focused',
'noHeading',
'fullContent',
'singleLine'
'singleLine',
'hideMentions'
],
data () {
return {
showingTall: this.fullContent || (this.inConversation && this.focused),
showingLongSubject: false,
// not as computed because it sets the initial state which will be changed later
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject,
headTailLinks: null,
firstMentions: [],
lastMentions: []
}
},
computed: {
@ -72,45 +75,9 @@ const StatusContent = {
showingMore () {
return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject)
},
postBodyHtml () {
const html = this.status.raw_html
if (this.mergedConfig.greentext) {
try {
if (html.includes('&gt;')) {
// This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works
return processHtml(html, (string) => {
if (string.includes('&gt;') &&
string
.replace(/<[^>]+?>/gi, '') // remove all tags
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
.trim()
.startsWith('&gt;')) {
return `<span class='greentext'>${string}</span>`
} else {
return string
}
})
} else {
return html
}
} catch (e) {
console.error('Failed to process status html', e)
return html
}
} else {
return html
}
},
attachmentTypes () {
return this.status.attachments.map(file => fileType.fileType(file.mimetype))
},
mentionsOwnLine () {
return this.mergedConfig.mentionsOwnLine
},
mentions () {
return this.status.attentions
},
...mapGetters(['mergedConfig'])
},
components: {
@ -124,21 +91,6 @@ const StatusContent = {
})
},
methods: {
linkClicked (event) {
const target = event.target.closest('.status-content a')
if (target) {
if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) {
// Extract tag name from dataset or link url
const tag = target.dataset.tag || extractTagFromUrl(target.href)
if (tag) {
const link = this.generateTagLink(tag)
this.$router.push(link)
return
}
}
window.open(target.href, '_blank')
}
},
toggleShowMore () {
if (this.mightHideBecauseTall) {
this.showingTall = !this.showingTall
@ -146,6 +98,11 @@ const StatusContent = {
this.expandingSubject = !this.expandingSubject
}
},
setHeadTailLinks (headTailLinks) {
set(this, 'headTailLinks', headTailLinks)
set(this, 'firstMentions', headTailLinks.firstMentions)
set(this, 'lastMentions', headTailLinks.lastMentions)
},
generateTagLink (tag) {
return `/tag/${tag}`
}

View file

@ -112,6 +112,10 @@
color: var(--postGreentext, $fallback--cGreen);
}
.cyantext {
color: var(--postCyantext, $fallback--cBlue);
}
/* Not sure if this is necessary */
video {
max-width: 100%;

View file

@ -10,7 +10,6 @@
class="media-body summary"
:html="status.summary_raw_html"
:emoji="status.emojis"
@click.prevent="linkClicked"
/>
<button
v-if="longSubject && showingLongSubject"
@ -39,22 +38,24 @@
>
{{ $t("general.show_more") }}
</button>
<span
class="text-wrapper"
v-if="!hideSubjectStatus && !(singleLine && status.summary_html)"
>
<span v-if="!hideSubjectStatus && !(singleLine && status.summary_html)">
<MentionsLine
v-if="!mentionsOwnLine"
:attentions="status.attentions"
class="mentions-line"
v-if="!hideMentions && firstMentions && firstMentions.length > 0"
:mentions="firstMentions"
/>
<RichContent
:class="{ '-single-line': singleLine }"
class="text media-body"
:html="postBodyHtml"
:html="status.raw_html"
:emoji="status.emojis"
:handle-links="true"
@click.prevent="linkClicked"
:hide-mentions="hideMentions"
:greentext="mergedConfig.greentext"
@parseReady="setHeadTailLinks"
/>
<MentionsLine
v-if="!hideMentions && lastMentions.length > 0 && firstMentions.length === 0"
:mentions="lastMentions"
/>
</span>

View file

@ -31,7 +31,8 @@ const StatusContent = {
'focused',
'noHeading',
'fullContent',
'singleLine'
'singleLine',
'hideMentions'
],
computed: {
hideAttachments () {
@ -91,6 +92,9 @@ const StatusContent = {
StatusBody
},
methods: {
setHeadTailLinks (headTailLinks) {
this.$emit('parseReady', headTailLinks)
},
setMedia () {
const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments
return () => this.$store.dispatch('setMedia', attachments)

View file

@ -4,6 +4,8 @@
<StatusBody
:status="status"
:single-line="singleLine"
:hide-mentions="hideMentions"
@parseReady="setHeadTailLinks"
>
<div v-if="status.poll && status.poll.options">
<poll :base-poll="status.poll" />