Merge remote-tracking branch 'origin/develop' into settings-modal
* origin/develop: (22 commits) changelog alignment fixes Update CHANGELOG.md StillImage: Make it work properly in both firefox and chrome. ReactButton: Change the combined emoji (heart) to a simple one. Linting fixes. Settings: Keep a local version of the mutedWordsString MediaModal: Close on browser navigation events. StatusContent: Try to get hashtag from dataset first. Docs: Change wrong documentation. EntityNormalizerSpec: More fixes. EntityNormalizerSpec: Test new behavior. EntityNormalizer: Add colons to emoji alt text. fixed case in class name The sidebarRight option wasn't being read Use consistent naming for Pleroma-FE in README Remove mention of GNU Social lint multiple fixes fix non-mention notifs ...
This commit is contained in:
commit
9e3e6b0c69
21 changed files with 195 additions and 64 deletions
|
@ -84,10 +84,12 @@ const MediaModal = {
|
|||
}
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('popstate', this.hide)
|
||||
document.addEventListener('keyup', this.handleKeyupEvent)
|
||||
document.addEventListener('keydown', this.handleKeydownEvent)
|
||||
},
|
||||
destroyed () {
|
||||
window.removeEventListener('popstate', this.hide)
|
||||
document.removeEventListener('keyup', this.handleKeyupEvent)
|
||||
document.removeEventListener('keydown', this.handleKeydownEvent)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import StatusContent from '../status_content/status_content.vue'
|
||||
import Status from '../status/status.vue'
|
||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||
import UserCard from '../user_card/user_card.vue'
|
||||
|
@ -16,10 +17,11 @@ const Notification = {
|
|||
},
|
||||
props: [ 'notification' ],
|
||||
components: {
|
||||
Status,
|
||||
StatusContent,
|
||||
UserAvatar,
|
||||
UserCard,
|
||||
Timeago
|
||||
Timeago,
|
||||
Status
|
||||
},
|
||||
methods: {
|
||||
toggleUserExpanded () {
|
||||
|
|
|
@ -157,11 +157,9 @@
|
|||
</router-link>
|
||||
</div>
|
||||
<template v-else>
|
||||
<status
|
||||
<status-content
|
||||
class="faint"
|
||||
:compact="true"
|
||||
:statusoid="notification.action"
|
||||
:no-heading="true"
|
||||
:status="notification.action"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
border-bottom: 1px solid;
|
||||
border-color: $fallback--border;
|
||||
border-color: var(--border, $fallback--border);
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
|
||||
&:hover .animated.avatar {
|
||||
canvas {
|
||||
|
@ -46,10 +48,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.muted {
|
||||
padding: .25em .6em;
|
||||
}
|
||||
|
||||
.non-mention {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
|
|
|
@ -24,7 +24,7 @@ const ReactButton = {
|
|||
},
|
||||
computed: {
|
||||
commonEmojis () {
|
||||
return ['❤️', '😠', '👀', '😂', '🔥']
|
||||
return ['👍', '😠', '👀', '😂', '🔥']
|
||||
},
|
||||
emojis () {
|
||||
if (this.filterWord !== '') {
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
import { filter, trim } from 'lodash'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
import SharedComputedObject from './helpers/shared_computed_object.js'
|
||||
|
||||
const FilteringTab = {
|
||||
data () {
|
||||
return {
|
||||
muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
muteWordsString: {
|
||||
get () {
|
||||
return this.muteWordsStringLocal
|
||||
},
|
||||
set (value) {
|
||||
this.muteWordsStringLocal = value
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'muteWords',
|
||||
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// Updating nested properties
|
||||
watch: {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { filter, trim } from 'lodash'
|
||||
import {
|
||||
instanceDefaultProperties,
|
||||
multiChoiceProperties,
|
||||
|
@ -38,15 +37,6 @@ const SharedComputedObject = () => ({
|
|||
}])
|
||||
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
|
||||
// Special cases (need to transform values or perform actions first)
|
||||
muteWordsString: {
|
||||
get () { return this.$store.getters.mergedConfig.muteWords.join('\n') },
|
||||
set (value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'muteWords',
|
||||
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||
})
|
||||
}
|
||||
},
|
||||
useStreamingApi: {
|
||||
get () { return this.$store.getters.mergedConfig.useStreamingApi },
|
||||
set (value) {
|
||||
|
|
|
@ -12,7 +12,8 @@ import StatusPopover from '../status_popover/status_popover.vue'
|
|||
import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
|
||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||
import { filter, unescape, uniqBy } from 'lodash'
|
||||
import { muteWordHits } from '../../services/status_parser/status_parser.js'
|
||||
import { unescape, uniqBy } from 'lodash'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
|
||||
const Status = {
|
||||
|
@ -44,6 +45,12 @@ const Status = {
|
|||
muteWords () {
|
||||
return this.mergedConfig.muteWords
|
||||
},
|
||||
showReasonMutedThread () {
|
||||
return (
|
||||
this.status.thread_muted ||
|
||||
(this.status.reblog && this.status.reblog.thread_muted)
|
||||
) && !this.inConversation
|
||||
},
|
||||
repeaterClass () {
|
||||
const user = this.statusoid.user
|
||||
return highlightClass(user)
|
||||
|
@ -93,20 +100,42 @@ const Status = {
|
|||
return !!this.currentUser
|
||||
},
|
||||
muteWordHits () {
|
||||
const statusText = this.status.text.toLowerCase()
|
||||
const statusSummary = this.status.summary.toLowerCase()
|
||||
const hits = filter(this.muteWords, (muteWord) => {
|
||||
return statusText.includes(muteWord.toLowerCase()) || statusSummary.includes(muteWord.toLowerCase())
|
||||
})
|
||||
|
||||
return hits
|
||||
return muteWordHits(this.status, this.muteWords)
|
||||
},
|
||||
muted () {
|
||||
const relationship = this.$store.getters.relationship(this.status.user.id)
|
||||
return !this.unmuted && (
|
||||
(!(this.inProfile && this.status.user.id === this.profileUserId) && relationship.muting) ||
|
||||
(!this.inConversation && this.status.thread_muted) ||
|
||||
this.muteWordHits.length > 0)
|
||||
const { status } = this
|
||||
const { reblog } = status
|
||||
const relationship = this.$store.getters.relationship(status.user.id)
|
||||
const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id)
|
||||
const reasonsToMute = (
|
||||
// Post is muted according to BE
|
||||
status.muted ||
|
||||
// Reprööt of a muted post according to BE
|
||||
(reblog && reblog.muted) ||
|
||||
// Muted user
|
||||
relationship.muting ||
|
||||
// Muted user of a reprööt
|
||||
(relationshipReblog && relationshipReblog.muting) ||
|
||||
// Thread is muted
|
||||
status.thread_muted ||
|
||||
// Wordfiltered
|
||||
this.muteWordHits.length > 0
|
||||
)
|
||||
const excusesNotToMute = (
|
||||
(
|
||||
this.inProfile && (
|
||||
// Don't mute user's posts on user timeline (except reblogs)
|
||||
(!reblog && status.user.id === this.profileUserId) ||
|
||||
// Same as above but also allow self-reblogs
|
||||
(reblog && reblog.user.id === this.profileUserId)
|
||||
)
|
||||
) ||
|
||||
// Don't mute statuses in muted conversation when said conversation is opened
|
||||
(this.inConversation && status.thread_muted)
|
||||
// No excuses if post has muted words
|
||||
) && !this.muteWordHits.length > 0
|
||||
|
||||
return !this.unmuted && !excusesNotToMute && reasonsToMute
|
||||
},
|
||||
hideFilteredStatuses () {
|
||||
return this.mergedConfig.hideFilteredStatuses
|
||||
|
|
|
@ -17,12 +17,33 @@
|
|||
</div>
|
||||
<template v-if="muted && !isPreview">
|
||||
<div class="media status container muted">
|
||||
<small>
|
||||
<small class="username">
|
||||
<i
|
||||
v-if="muted && retweet"
|
||||
class="button-icon icon-retweet"
|
||||
/>
|
||||
<router-link :to="userProfileLink">
|
||||
{{ status.user.screen_name }}
|
||||
</router-link>
|
||||
</small>
|
||||
<small class="muteWords">{{ muteWordHits.join(', ') }}</small>
|
||||
<small
|
||||
v-if="showReasonMutedThread"
|
||||
class="mute-thread"
|
||||
>
|
||||
{{ $t('status.thread_muted') }}
|
||||
</small>
|
||||
<small
|
||||
v-if="showReasonMutedThread && muteWordHits.length > 0"
|
||||
class="mute-thread"
|
||||
>
|
||||
{{ $t('status.thread_muted_and_words') }}
|
||||
</small>
|
||||
<small
|
||||
class="mute-words"
|
||||
:title="muteWordHits.join(', ')"
|
||||
>
|
||||
{{ muteWordHits.join(', ') }}
|
||||
</small>
|
||||
<a
|
||||
href="#"
|
||||
class="unmute"
|
||||
|
@ -637,19 +658,48 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.muted {
|
||||
padding: 0.25em 0.5em;
|
||||
button {
|
||||
padding: .25em .6em;
|
||||
height: 1.2em;
|
||||
line-height: 1.2em;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.username, .mute-thread, .mute-words {
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.username, .mute-words {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.username {
|
||||
flex: 0 1 auto;
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
||||
.mute-thread {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mute-words {
|
||||
flex: 1 0 5em;
|
||||
margin-left: .2em;
|
||||
&::before {
|
||||
content: ' '
|
||||
}
|
||||
}
|
||||
|
||||
.unmute {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.muteWords {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
a.unmute {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.reply-body {
|
||||
|
|
|
@ -176,8 +176,8 @@ const StatusContent = {
|
|||
}
|
||||
}
|
||||
if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) {
|
||||
// Extract tag name from link url
|
||||
const tag = extractTagFromUrl(target.href)
|
||||
// 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)
|
||||
|
|
|
@ -23,12 +23,21 @@
|
|||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
.contain-fit {
|
||||
.still-image {
|
||||
img {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.still-image {
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
&:hover canvas {
|
||||
display: none;
|
||||
|
@ -36,8 +45,8 @@
|
|||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
&.animated {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue