Merge branch 'from/develop/tusooa/1118-enhanced-mention-link' into 'develop'

Enhanced mention link

Closes #1118

See merge request pleroma/pleroma-fe!1424
This commit is contained in:
HJ 2022-02-03 19:37:13 +00:00
commit 8ade11783a
8 changed files with 148 additions and 10 deletions

View file

@ -1,6 +1,7 @@
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import UserAvatar from '../user_avatar/user_avatar.vue'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {
faAt faAt
@ -12,6 +13,9 @@ library.add(
const MentionLink = { const MentionLink = {
name: 'MentionLink', name: 'MentionLink',
components: {
UserAvatar
},
props: { props: {
url: { url: {
required: true, required: true,
@ -50,6 +54,10 @@ const MentionLink = {
userName () { userName () {
return this.user && this.userNameFullUi.split('@')[0] return this.user && this.userNameFullUi.split('@')[0]
}, },
serverName () {
// XXX assumed that domain does not contain @
return this.user && (this.userNameFullUi.split('@')[1] || this.$store.getters.instanceDomain)
},
userNameFull () { userNameFull () {
return this.user && this.user.screen_name return this.user && this.user.screen_name
}, },
@ -85,6 +93,31 @@ const MentionLink = {
this.highlightType this.highlightType
] ]
}, },
useAtIcon () {
return this.mergedConfig.useAtIcon
},
isRemote () {
return this.userName !== this.userNameFull
},
shouldShowFullUserName () {
const conf = this.mergedConfig.mentionLinkDisplay
if (conf === 'short') {
return false
} else if (conf === 'full') {
return true
} else { // full_for_remote
return this.isRemote
}
},
shouldShowTooltip () {
return this.mergedConfig.mentionLinkShowTooltip && this.mergedConfig.mentionLinkDisplay === 'short' && this.isRemote
},
shouldShowAvatar () {
return this.mergedConfig.mentionLinkShowAvatar
},
shouldFadeDomain () {
return this.mergedConfig.mentionLinkFadeDomain
},
...mapGetters(['mergedConfig']), ...mapGetters(['mergedConfig']),
...mapState({ ...mapState({
currentUser: state => state.users.currentUser currentUser: state => state.users.currentUser

View file

@ -1,3 +1,5 @@
@import '../../_variables.scss';
.MentionLink { .MentionLink {
position: relative; position: relative;
white-space: normal; white-space: normal;
@ -10,6 +12,15 @@
border-radius: 2px; border-radius: 2px;
} }
.mention-avatar {
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
width: 1.5em;
height: 1.5em;
vertical-align: middle;
user-select: none;
margin-right: 0.2em;
}
.full { .full {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
@ -27,7 +38,8 @@
user-select: all; user-select: all;
} }
.short { .short.-with-tooltip,
.you {
user-select: none; user-select: none;
} }
@ -56,7 +68,7 @@
} }
&.-striped { &.-striped {
& .userName, & .shortName,
& .full { & .full {
background-image: background-image:
repeating-linear-gradient( repeating-linear-gradient(
@ -70,14 +82,14 @@
} }
&.-solid { &.-solid {
& .userName, & .shortName,
& .full { & .full {
background-image: linear-gradient(var(--____highlight-tintColor2), var(--____highlight-tintColor2)); background-image: linear-gradient(var(--____highlight-tintColor2), var(--____highlight-tintColor2));
} }
} }
&.-side { &.-side {
& .userName, & .shortName,
& .userNameFull { & .userNameFull {
box-shadow: 0 -5px 3px -4px inset var(--____highlight-solidColor); box-shadow: 0 -5px 3px -4px inset var(--____highlight-solidColor);
} }
@ -88,4 +100,12 @@
opacity: 1; opacity: 1;
pointer-events: initial; pointer-events: initial;
} }
.serverName.-faded {
color: var(--faintLink, $fallback--link);
}
.full .-faded {
color: var(--faint, $fallback--faint);
}
} }

View file

@ -19,17 +19,30 @@
> >
<a <a
class="short button-unstyled" class="short button-unstyled"
:class="{ '-with-tooltip': shouldShowTooltip }"
:href="url" :href="url"
@click.prevent="onClick" @click.prevent="onClick"
> >
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<FAIcon <UserAvatar
v-if="shouldShowAvatar"
class="mention-avatar"
:user="user"
/><span
class="shortName"
><FAIcon
v-if="useAtIcon"
size="sm" size="sm"
icon="at" icon="at"
class="at" class="at"
/><span class="shortName"><span />{{ !useAtIcon ? '@' : '' }}<span
class="userName" class="userName"
v-html="userName" v-html="userName"
/><span
v-if="shouldShowFullUserName"
class="serverName"
:class="{ '-faded': shouldFadeDomain }"
v-html="'@' + serverName"
/></span> /></span>
<span <span
v-if="isYou" v-if="isYou"
@ -38,14 +51,24 @@
<!-- eslint-enable vue/no-v-html --> <!-- eslint-enable vue/no-v-html -->
</a> </a>
<span <span
v-if="userName !== userNameFull" v-if="shouldShowTooltip"
class="full popover-default" class="full popover-default"
:class="[highlightType]" :class="[highlightType]"
> >
<span <span
class="userNameFull" class="userNameFull"
v-text="'@' + userNameFull" >
/> <!-- eslint-disable vue/no-v-html -->
@<span
class="userName"
v-html="userName"
/><span
class="serverName"
:class="{ '-faded': shouldFadeDomain }"
v-html="'@' + serverName"
/>
<!-- eslint-enable vue/no-v-html -->
</span>
</span> </span>
</span> </span>
</span> </span>

View file

@ -20,6 +20,11 @@ const GeneralTab = {
value: mode, value: mode,
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`) label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
})), })),
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.mention_link_display_${mode}`)
})),
loopSilentAvailable: loopSilentAvailable:
// Firefox // Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||

View file

@ -147,6 +147,41 @@
{{ $t('settings.greentext') }} {{ $t('settings.greentext') }}
</BooleanSetting> </BooleanSetting>
</li> </li>
<li>
<ChoiceSetting
id="mentionLinkDisplay"
path="mentionLinkDisplay"
:options="mentionLinkDisplayOptions"
>
{{ $t('settings.mention_link_display') }}
</ChoiceSetting>
</li>
<ul
class="setting-list suboptions"
>
<li
v-if="mentionLinkDisplay === 'short'"
>
<BooleanSetting path="mentionLinkShowTooltip">
{{ $t('settings.mention_link_show_tooltip') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="useAtIcon">
{{ $t('settings.use_at_icon') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="mentionLinkShowAvatar">
{{ $t('settings.mention_link_show_avatar') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="mentionLinkFadeDomain">
{{ $t('settings.mention_link_fade_domain') }}
</BooleanSetting>
</li>
</ul>
</ul> </ul>
</div> </div>

View file

@ -485,6 +485,14 @@
"true": "yes" "true": "yes"
}, },
"virtual_scrolling": "Optimize timeline rendering", "virtual_scrolling": "Optimize timeline rendering",
"use_at_icon": "Display @ symbol as an icon instead of text",
"mention_link_display": "Display mention links",
"mention_link_display_short": "always as short names (e.g. @foo)",
"mention_link_display_full_for_remote": "as full names only for remote users (e.g. @foo@example.org)",
"mention_link_display_full": "always as full names (e.g. @foo@example.org)",
"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_fade_domain": "Fade domains (e.g. @example.org in @foo@example.org)",
"fun": "Fun", "fun": "Fun",
"greentext": "Meme arrows", "greentext": "Meme arrows",
"notifications": "Notifications", "notifications": "Notifications",

View file

@ -11,7 +11,8 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0]
*/ */
export const multiChoiceProperties = [ export const multiChoiceProperties = [
'postContentType', 'postContentType',
'subjectLineBehavior' 'subjectLineBehavior',
'mentionLinkDisplay' // short | full_for_remote | full
] ]
export const defaultState = { export const defaultState = {
@ -70,6 +71,11 @@ export const defaultState = {
useOneClickNsfw: false, useOneClickNsfw: false,
useContainFit: false, useContainFit: false,
greentext: undefined, // instance default greentext: undefined, // instance default
useAtIcon: undefined, // instance default
mentionLinkDisplay: undefined, // instance default
mentionLinkShowTooltip: undefined, // instance default
mentionLinkShowAvatar: undefined, // instance default
mentionLinkFadeDomain: 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

@ -20,6 +20,11 @@ const defaultState = {
background: '/static/aurora_borealis.jpg', background: '/static/aurora_borealis.jpg',
collapseMessageWithSubject: false, collapseMessageWithSubject: false,
greentext: false, greentext: false,
useAtIcon: false,
mentionLinkDisplay: 'short',
mentionLinkShowTooltip: true,
mentionLinkShowAvatar: false,
mentionLinkFadeDomain: 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,
@ -100,6 +105,9 @@ const instance = {
return instanceDefaultProperties return instanceDefaultProperties
.map(key => [key, state[key]]) .map(key => [key, state[key]])
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}) .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
},
instanceDomain (state) {
return new URL(state.server).hostname
} }
}, },
actions: { actions: {