Merge branch 'fix-mentions-new-bugs' into 'develop'

Fix newfound bugs with rich mentions + user suggestions

See merge request pleroma/pleroma-fe!1430
This commit is contained in:
HJ 2022-02-20 15:11:52 +00:00
commit 58d0f9678b
20 changed files with 172 additions and 42 deletions

View file

@ -1,6 +1,7 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.chat-message-wrapper { .chat-message-wrapper {
&.hovered-message-chain { &.hovered-message-chain {
.animated.Avatar { .animated.Avatar {
canvas { canvas {
@ -40,6 +41,12 @@
.chat-message { .chat-message {
display: flex; display: flex;
padding-bottom: 0.5em; padding-bottom: 0.5em;
.status-body:hover {
--_still-image-img-visibility: visible;
--_still-image-canvas-visibility: hidden;
--_still-image-label-visibility: hidden;
}
} }
.avatar-wrapper { .avatar-wrapper {

View file

@ -1,5 +1,4 @@
<template> <template>
<!-- eslint-disable vue/no-v-html -->
<div <div
class="chat-title" class="chat-title"
:title="title" :title="title"
@ -14,12 +13,13 @@
height="23px" height="23px"
/> />
</router-link> </router-link>
<span <RichContent
class="username" class="username"
v-html="htmlTitle" :title="'@'+user.screen_name_ui"
:html="htmlTitle"
:emoji="user.emoji"
/> />
</div> </div>
<!-- eslint-enable vue/no-v-html -->
</template> </template>
<script src="./chat_title.js"></script> <script src="./chat_title.js"></script>
@ -34,6 +34,8 @@
white-space: nowrap; white-space: nowrap;
align-items: center; align-items: center;
--emoji-size: 14px;
.username { .username {
max-width: 100%; max-width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -41,14 +43,6 @@
display: inline; display: inline;
word-wrap: break-word; word-wrap: break-word;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
.emoji {
width: 14px;
height: 14px;
vertical-align: middle;
object-fit: contain
}
} }
.Avatar { .Avatar {

View file

@ -87,7 +87,7 @@ const MentionLink = {
classnames () { classnames () {
return [ return [
{ {
'-you': this.isYou, '-you': this.isYou && this.shouldBoldenYou,
'-highlighted': this.highlight '-highlighted': this.highlight
}, },
this.highlightType this.highlightType
@ -115,6 +115,12 @@ const MentionLink = {
shouldShowAvatar () { shouldShowAvatar () {
return this.mergedConfig.mentionLinkShowAvatar return this.mergedConfig.mentionLinkShowAvatar
}, },
shouldShowYous () {
return this.mergedConfig.mentionLinkShowYous
},
shouldBoldenYou () {
return this.mergedConfig.mentionLinkBoldenYou
},
shouldFadeDomain () { shouldFadeDomain () {
return this.mergedConfig.mentionLinkFadeDomain return this.mergedConfig.mentionLinkFadeDomain
}, },

View file

@ -3,12 +3,13 @@
.MentionLink { .MentionLink {
position: relative; position: relative;
white-space: normal; white-space: normal;
display: inline-block; display: inline;
color: var(--link); color: var(--link);
word-break: normal;
& .new, & .new,
& .original { & .original {
display: inline-block; display: inline;
border-radius: 2px; border-radius: 2px;
} }
@ -38,8 +39,8 @@
user-select: all; user-select: all;
} }
.short.-with-tooltip, & .short.-with-tooltip,
.you { & .you {
user-select: none; user-select: none;
} }
@ -48,6 +49,10 @@
white-space: nowrap; white-space: nowrap;
} }
.shortName {
white-space: normal;
}
.new { .new {
&.-you { &.-you {
& .shortName, & .shortName,

View file

@ -9,9 +9,7 @@
class="original" class="original"
target="_blank" target="_blank"
v-html="content" v-html="content"
/> /><!-- eslint-enable vue/no-v-html --><span
<!-- eslint-enable vue/no-v-html -->
<span
v-if="user" v-if="user"
class="new" class="new"
:style="style" :style="style"
@ -43,14 +41,12 @@
class="serverName" class="serverName"
:class="{ '-faded': shouldFadeDomain }" :class="{ '-faded': shouldFadeDomain }"
v-html="'@' + serverName" v-html="'@' + serverName"
/></span> /></span><span
<span v-if="isYou && shouldShowYous"
v-if="isYou" :class="{ '-you': shouldBoldenYou }"
class="you" > {{ $t('status.you') }}</span>
>{{ $t('status.you') }}</span>
<!-- eslint-enable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
</a> </a><span
<span
v-if="shouldShowTooltip" v-if="shouldShowTooltip"
class="full popover-default" class="full popover-default"
:class="[highlightType]" :class="[highlightType]"

View file

@ -1,11 +1,13 @@
.MentionsLine { .MentionsLine {
word-break: break-all;
.mention-link:not(:first-child)::before {
content: ' ';
}
.showMoreLess { .showMoreLess {
margin-left: 0.5em;
white-space: normal; white-space: normal;
color: var(--link); color: var(--link);
} }
.fullExtraMentions,
.mention-link:not(:last-child) {
margin-right: 0.25em;
}
} }

View file

@ -33,7 +33,7 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.popover-trigger-button { .popover-trigger-button {
display: block; display: inline-block;
} }
.popover { .popover {

View file

@ -120,7 +120,8 @@ export default Vue.component('RichContent', {
// don't include spaces when processing mentions - we'll include them // don't include spaces when processing mentions - we'll include them
// in MentionsLine // in MentionsLine
lastSpacing = item lastSpacing = item
return currentMentions !== null ? item.trim() : item // Don't remove last space in a container (fixes poast mentions)
return (index !== array.length - 1) && (currentMentions !== null) ? item.trim() : item
} }
currentMentions = null currentMentions = null

View file

@ -147,6 +147,11 @@
{{ $t('settings.greentext') }} {{ $t('settings.greentext') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="mentionLinkShowYous">
{{ $t('settings.show_yous') }}
</BooleanSetting>
</li>
<li> <li>
<ChoiceSetting <ChoiceSetting
id="mentionLinkDisplay" id="mentionLinkDisplay"
@ -181,6 +186,11 @@
{{ $t('settings.mention_link_fade_domain') }} {{ $t('settings.mention_link_fade_domain') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<BooleanSetting path="mentionLinkBoldenYou">
{{ $t('settings.mention_link_bolden_you') }}
</BooleanSetting>
</li>
</ul> </ul>
</ul> </ul>
</div> </div>

View file

@ -5,6 +5,8 @@ $status-margin: 0.75em;
.Status { .Status {
min-width: 0; min-width: 0;
white-space: normal; white-space: normal;
word-wrap: break-word;
word-break: break-word;
&:hover { &:hover {
--_still-image-img-visibility: visible; --_still-image-img-visibility: visible;
@ -164,18 +166,24 @@ $status-margin: 0.75em;
position: relative; position: relative;
align-content: baseline; align-content: baseline;
font-size: 12px; font-size: 12px;
line-height: 160%; margin-top: 0.2em;
line-height: 130%;
max-width: 100%; max-width: 100%;
align-items: stretch; align-items: stretch;
} }
& .reply-to-popover, & .reply-to-popover,
& .reply-to-no-popover { & .reply-to-no-popover,
& .mentions {
min-width: 0; min-width: 0;
margin-right: 0.4em; margin-right: 0.4em;
flex-shrink: 0; flex-shrink: 0;
} }
.reply-glued-label {
margin-right: 0.5em;
}
.reply-to-popover { .reply-to-popover {
.reply-to:hover::before { .reply-to:hover::before {
content: ''; content: '';
@ -209,7 +217,6 @@ $status-margin: 0.75em;
& .reply-to { & .reply-to {
white-space: nowrap; white-space: nowrap;
position: relative; position: relative;
padding-right: 0.25em;
} }
& .mentions-text, & .mentions-text,

View file

@ -227,7 +227,7 @@
> >
<span <span
v-if="isReply" v-if="isReply"
class="glued-label" class="glued-label reply-glued-label"
> >
<StatusPopover <StatusPopover
v-if="!isPreview" v-if="!isPreview"

View file

@ -5,7 +5,9 @@ const StillImage = {
'mimetype', 'mimetype',
'imageLoadError', 'imageLoadError',
'imageLoadHandler', 'imageLoadHandler',
'alt' 'alt',
'height',
'width'
], ],
data () { data () {
return { return {
@ -15,6 +17,13 @@ const StillImage = {
computed: { computed: {
animated () { animated () {
return this.stopGifs && (this.mimetype === 'image/gif' || this.src.endsWith('.gif')) return this.stopGifs && (this.mimetype === 'image/gif' || this.src.endsWith('.gif'))
},
style () {
const appendPx = (str) => /\d$/.test(str) ? str + 'px' : str
return {
height: this.height ? appendPx(this.height) : null,
width: this.width ? appendPx(this.width) : null
}
} }
}, },
methods: { methods: {

View file

@ -2,6 +2,7 @@
<div <div
class="still-image" class="still-image"
:class="{ animated: animated }" :class="{ animated: animated }"
:style="style"
> >
<canvas <canvas
v-if="animated" v-if="animated"

View file

@ -275,6 +275,7 @@
class="user-card-bio" class="user-card-bio"
:html="user.description_html" :html="user.description_html"
:emoji="user.emoji" :emoji="user.emoji"
:handle-links="true"
/> />
</div> </div>
</div> </div>

View file

@ -22,7 +22,12 @@
/> />
<div class="user-list-names"> <div class="user-list-names">
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<span v-html="user.name_html" /> <RichContent
class="username"
:title="'@'+user.screen_name_ui"
:html="user.name_html"
:emoji="user.emoji"
/>
<!-- eslint-enable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
<span class="user-list-screen-name">{{ user.screen_name_ui }}</span> <span class="user-list-screen-name">{{ user.screen_name_ui }}</span>
</div> </div>
@ -48,6 +53,8 @@
.user-list-popover { .user-list-popover {
padding: 0.5em; padding: 0.5em;
--emoji-size: 16px;
.user-list-row { .user-list-row {
padding: 0.25em; padding: 0.25em;
display: flex; display: flex;

View file

@ -493,8 +493,10 @@
"mention_link_show_tooltip": "Show full user names as tooltip for remote users", "mention_link_show_tooltip": "Show full user names as tooltip for remote users",
"mention_link_show_avatar": "Show user avatar beside the link", "mention_link_show_avatar": "Show user avatar beside the link",
"mention_link_fade_domain": "Fade domains (e.g. @example.org in @foo@example.org)", "mention_link_fade_domain": "Fade domains (e.g. @example.org in @foo@example.org)",
"mention_link_bolden_you": "Highlight mention of you when you are mentioned",
"fun": "Fun", "fun": "Fun",
"greentext": "Meme arrows", "greentext": "Meme arrows",
"show_yous": "Show (You)s",
"notifications": "Notifications", "notifications": "Notifications",
"notification_setting_filters": "Filters", "notification_setting_filters": "Filters",
"notification_setting_block_from_strangers": "Block notifications from users who you do not follow", "notification_setting_block_from_strangers": "Block notifications from users who you do not follow",

View file

@ -76,6 +76,8 @@ export const defaultState = {
mentionLinkShowTooltip: undefined, // instance default mentionLinkShowTooltip: undefined, // instance default
mentionLinkShowAvatar: undefined, // instance default mentionLinkShowAvatar: undefined, // instance default
mentionLinkFadeDomain: undefined, // instance default mentionLinkFadeDomain: undefined, // instance default
mentionLinkShowYous: undefined, // instance default
mentionLinkBoldenYou: undefined, // instance default
hidePostStats: undefined, // instance default hidePostStats: undefined, // instance default
hideUserStats: undefined, // instance default hideUserStats: undefined, // instance default
virtualScrolling: undefined, // instance default virtualScrolling: undefined, // instance default

View file

@ -25,6 +25,8 @@ const defaultState = {
mentionLinkShowTooltip: true, mentionLinkShowTooltip: true,
mentionLinkShowAvatar: false, mentionLinkShowAvatar: false,
mentionLinkFadeDomain: true, mentionLinkFadeDomain: true,
mentionLinkShowYous: false,
mentionLinkBoldenYou: true,
hideFilteredStatuses: false, hideFilteredStatuses: false,
// bad name: actually hides posts of muted USERS // bad name: actually hides posts of muted USERS
hideMutedPosts: false, hideMutedPosts: false,

View file

@ -1,4 +1,5 @@
import { getTagName } from './utility.service.js' import { getTagName } from './utility.service.js'
import { unescape } from 'lodash'
/** /**
* This is a not-so-tiny purpose-built HTML parser/processor. This parses html * This is a not-so-tiny purpose-built HTML parser/processor. This parses html
@ -49,7 +50,7 @@ export const convertHtmlToTree = (html = '') => {
const handleOpen = (tag) => { const handleOpen = (tag) => {
const curBuf = getCurrentBuffer() const curBuf = getCurrentBuffer()
const newLevel = [tag, []] const newLevel = [unescape(tag), []]
levels.push(newLevel) levels.push(newLevel)
curBuf.push(newLevel) curBuf.push(newLevel)
} }

View file

@ -350,7 +350,6 @@ describe('RichContent', () => {
'<span>', '<span>',
'</span>', '</span>',
'</a>', '</a>',
' ',
'<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display '<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>', '</span>',
'<!---->', // v-if placeholder, mentionsline's extra mentions and stuff '<!---->', // v-if placeholder, mentionsline's extra mentions and stuff
@ -375,6 +374,84 @@ describe('RichContent', () => {
expect(wrapper.html()).to.eql(compwrap(expected)) expect(wrapper.html()).to.eql(compwrap(expected))
}) })
it('rich contents of nested mentions are handled properly', () => {
attentions.push({ statusnet_profile_url: 'lol' })
const html = [
p(
'<span class="poast-style">',
'<a href="lol" class="mention">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
' ',
'<a href="lol" class="mention">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
'</span>'
),
p(
'Testing'
)
].join('')
const expected = [
p(
'<span class="poast-style">',
'<span class="MentionsLine">',
'<span class="MentionLink mention-link">',
'<a href="lol" target="_blank" class="original">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
'<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>',
'<span class="MentionLink mention-link">',
'<a href="lol" target="_blank" class="original">',
'<span>',
'https://</span>',
'<span>',
'lol.tld/</span>',
'<span>',
'</span>',
'</a>',
'<!---->', // v-if placeholder, mentionlink's "new" (i.e. rich) display
'</span>',
'<!---->', // v-if placeholder, mentionsline's extra mentions and stuff
'</span>',
'</span>'
),
' ',
p(
'Testing'
)
].join('')
const wrapper = mount(RichContent, {
localVue,
propsData: {
attentions,
handleLinks: true,
greentext: true,
emoji: [],
html
}
})
expect(wrapper.html()).to.eql(compwrap(expected))
})
it('rich contents of a link are handled properly', () => { it('rich contents of a link are handled properly', () => {
const html = [ const html = [
'<p>', '<p>',