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:
Henry Jameson 2020-06-07 00:10:21 +03:00
commit 9e3e6b0c69
21 changed files with 195 additions and 64 deletions

View file

@ -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)
}

View file

@ -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 () {

View file

@ -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>

View file

@ -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;

View file

@ -24,7 +24,7 @@ const ReactButton = {
},
computed: {
commonEmojis () {
return ['❤️', '😠', '👀', '😂', '🔥']
return ['👍', '😠', '👀', '😂', '🔥']
},
emojis () {
if (this.filterWord !== '') {

View file

@ -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: {

View file

@ -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) {

View file

@ -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

View file

@ -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 {

View file

@ -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)

View file

@ -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 {