Merge branch 'fix-reprooted-muted-posts' into 'develop'

Fixed some issues with muting

See merge request pleroma/pleroma-fe!1120
This commit is contained in:
HJ 2020-06-06 20:31:43 +00:00
commit e47d0f2103
9 changed files with 143 additions and 39 deletions

View file

@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed ### Changed
- Removed the use of with_move parameters when fetching notifications - Removed the use of with_move parameters when fetching notifications
### Fixed
- Multiple issues with muted statuses/notifications
## [Unreleased patch] ## [Unreleased patch]
### Add ### Add
- Added private notifications option for push notifications - Added private notifications option for push notifications

View file

@ -1,3 +1,4 @@
import StatusContent from '../status_content/status_content.vue'
import Status from '../status/status.vue' import Status from '../status/status.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import UserCard from '../user_card/user_card.vue' import UserCard from '../user_card/user_card.vue'
@ -16,10 +17,11 @@ const Notification = {
}, },
props: [ 'notification' ], props: [ 'notification' ],
components: { components: {
Status, StatusContent,
UserAvatar, UserAvatar,
UserCard, UserCard,
Timeago Timeago,
Status
}, },
methods: { methods: {
toggleUserExpanded () { toggleUserExpanded () {

View file

@ -157,11 +157,9 @@
</router-link> </router-link>
</div> </div>
<template v-else> <template v-else>
<status <status-content
class="faint" class="faint"
:compact="true" :status="notification.action"
:statusoid="notification.action"
:no-heading="true"
/> />
</template> </template>
</div> </div>

View file

@ -36,6 +36,8 @@
border-bottom: 1px solid; border-bottom: 1px solid;
border-color: $fallback--border; border-color: $fallback--border;
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
word-wrap: break-word;
word-break: break-word;
&:hover .animated.avatar { &:hover .animated.avatar {
canvas { canvas {
@ -46,10 +48,6 @@
} }
} }
.muted {
padding: .25em .6em;
}
.non-mention { .non-mention {
display: flex; display: flex;
flex: 1; flex: 1;

View file

@ -12,7 +12,8 @@ import StatusPopover from '../status_popover/status_popover.vue'
import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
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 { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' 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' import { mapGetters, mapState } from 'vuex'
const Status = { const Status = {
@ -44,6 +45,12 @@ const Status = {
muteWords () { muteWords () {
return this.mergedConfig.muteWords return this.mergedConfig.muteWords
}, },
showReasonMutedThread () {
return (
this.status.thread_muted ||
(this.status.reblog && this.status.reblog.thread_muted)
) && !this.inConversation
},
repeaterClass () { repeaterClass () {
const user = this.statusoid.user const user = this.statusoid.user
return highlightClass(user) return highlightClass(user)
@ -93,20 +100,42 @@ const Status = {
return !!this.currentUser return !!this.currentUser
}, },
muteWordHits () { muteWordHits () {
const statusText = this.status.text.toLowerCase() return muteWordHits(this.status, this.muteWords)
const statusSummary = this.status.summary.toLowerCase()
const hits = filter(this.muteWords, (muteWord) => {
return statusText.includes(muteWord.toLowerCase()) || statusSummary.includes(muteWord.toLowerCase())
})
return hits
}, },
muted () { muted () {
const relationship = this.$store.getters.relationship(this.status.user.id) const { status } = this
return !this.unmuted && ( const { reblog } = status
(!(this.inProfile && this.status.user.id === this.profileUserId) && relationship.muting) || const relationship = this.$store.getters.relationship(status.user.id)
(!this.inConversation && this.status.thread_muted) || const relationshipReblog = reblog && this.$store.getters.relationship(reblog.user.id)
this.muteWordHits.length > 0) 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 () { hideFilteredStatuses () {
return this.mergedConfig.hideFilteredStatuses return this.mergedConfig.hideFilteredStatuses

View file

@ -17,12 +17,33 @@
</div> </div>
<template v-if="muted && !isPreview"> <template v-if="muted && !isPreview">
<div class="media status container muted"> <div class="media status container muted">
<small> <small class="username">
<i
v-if="muted && retweet"
class="button-icon icon-retweet"
/>
<router-link :to="userProfileLink"> <router-link :to="userProfileLink">
{{ status.user.screen_name }} {{ status.user.screen_name }}
</router-link> </router-link>
</small> </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 <a
href="#" href="#"
class="unmute" class="unmute"
@ -637,19 +658,48 @@ $status-margin: 0.75em;
} }
.muted { .muted {
padding: 0.25em 0.5em; padding: .25em .6em;
button { 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; margin-left: auto;
} }
.muteWords {
margin-left: 10px;
}
}
a.unmute {
display: block;
margin-left: auto;
} }
.reply-body { .reply-body {

View file

@ -621,7 +621,9 @@
"mute_conversation": "Mute conversation", "mute_conversation": "Mute conversation",
"unmute_conversation": "Unmute conversation", "unmute_conversation": "Unmute conversation",
"status_unavailable": "Status unavailable", "status_unavailable": "Status unavailable",
"copy_link": "Copy link to status" "copy_link": "Copy link to status",
"thread_muted": "Thread muted",
"thread_muted_and_words": ", has words:"
}, },
"user_card": { "user_card": {
"approve": "Approve", "approve": "Approve",

View file

@ -15,7 +15,7 @@ import {
import { set } from 'vue' import { set } from 'vue'
import { isStatusNotification } from '../services/notification_utils/notification_utils.js' import { isStatusNotification } from '../services/notification_utils/notification_utils.js'
import apiService from '../services/api/api.service.js' import apiService from '../services/api/api.service.js'
// import parse from '../services/status_parser/status_parser.js' import { muteWordHits } from '../services/status_parser/status_parser.js'
const emptyTl = (userId = 0) => ({ const emptyTl = (userId = 0) => ({
statuses: [], statuses: [],
@ -381,7 +381,18 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
notifObj.image = status.attachments[0].url notifObj.image = status.attachments[0].url
} }
if (!notification.seen && !state.notifications.desktopNotificationSilence && visibleNotificationTypes.includes(notification.type)) { const reasonsToMuteNotif = (
notification.seen ||
state.notifications.desktopNotificationSilence ||
!visibleNotificationTypes.includes(notification.type) ||
(
notification.type === 'mention' && status && (
status.muted ||
muteWordHits(status, rootGetters.mergedConfig.muteWords).length === 0
)
)
)
if (!reasonsToMuteNotif) {
let desktopNotification = new window.Notification(title, notifObj) let desktopNotification = new window.Notification(title, notifObj)
// Chrome is known for not closing notifications automatically // Chrome is known for not closing notifications automatically
// according to MDN, anyway. // according to MDN, anyway.

View file

@ -1,3 +1,4 @@
import { filter } from 'lodash'
import sanitize from 'sanitize-html' import sanitize from 'sanitize-html'
export const removeAttachmentLinks = (html) => { export const removeAttachmentLinks = (html) => {
@ -12,4 +13,14 @@ export const parse = (html) => {
return removeAttachmentLinks(html) return removeAttachmentLinks(html)
} }
export const muteWordHits = (status, muteWords) => {
const statusText = status.text.toLowerCase()
const statusSummary = status.summary.toLowerCase()
const hits = filter(muteWords, (muteWord) => {
return statusText.includes(muteWord.toLowerCase()) || statusSummary.includes(muteWord.toLowerCase())
})
return hits
}
export default parse export default parse