[Client] Implement word mute

Closes #1739
This commit is contained in:
syuilo 2018-11-11 21:17:51 +09:00
parent 1b4072610a
commit 71d42f64dc
No known key found for this signature in database
GPG key ID: BDC4C49D06AB9D69
10 changed files with 77 additions and 71 deletions

View file

@ -935,6 +935,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック" block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません" no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません" no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue: common/views/components/password-settings.vue:
reset: "パスワードを変更する" reset: "パスワードを変更する"

View file

@ -1,5 +1,6 @@
import parse from '../../../../mfm/parse'; import parse from '../../../../mfm/parse';
import { sum } from '../../../../prelude/array'; import { sum } from '../../../../prelude/array';
import shouldMuteNote from './should-mute-note';
import MkNoteMenu from '../views/components/note-menu.vue'; import MkNoteMenu from '../views/components/note-menu.vue';
import MkReactionPicker from '../views/components/reaction-picker.vue'; import MkReactionPicker from '../views/components/reaction-picker.vue';
import Ok from '../views/components/ok.vue'; import Ok from '../views/components/ok.vue';
@ -22,7 +23,8 @@ type Opts = {
export default (opts: Opts = {}) => ({ export default (opts: Opts = {}) => ({
data() { data() {
return { return {
showContent: false showContent: false,
hideThisNote: false
}; };
}, },
@ -86,6 +88,10 @@ export default (opts: Opts = {}) => ({
} }
}, },
created() {
this.hideThisNote = shouldMuteNote(this.$store.state.i, this.$store.state.settings, this.appearNote);
},
methods: { methods: {
reply(viaKeyboard = false) { reply(viaKeyboard = false) {
this.$root.$post({ this.$root.$post({

View file

@ -0,0 +1,28 @@
export default function(me, settings, note) {
const isMyNote = note.userId == me.id;
const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
return true;
}
}
if (settings.showRenotedMyNotes === false) {
if (isPureRenote && (note.renote.userId == me.id)) {
return true;
}
}
if (settings.showLocalRenotes === false) {
if (isPureRenote && (note.renote.user.host == null)) {
return true;
}
}
if (!isMyNote && note.text && settings.mutedWords.some(q => !q.some(word => !note.text.includes(word)))) {
return true;
}
return false;
}

View file

@ -21,6 +21,14 @@
</div> </div>
</div> </div>
</section> </section>
<section>
<header>{{ $t('word-mute') }}</header>
<ui-textarea v-model="mutedWords">
{{ $t('muted-words') }}<span slot="desc">{{ $t('muted-words-description') }}</span>
</ui-textarea>
<ui-button @click="save">{{ $t('save') }}</ui-button>
</section>
</ui-card> </ui-card>
</template> </template>
@ -30,16 +38,27 @@ import i18n from '../../../i18n';
export default Vue.extend({ export default Vue.extend({
i18n: i18n('common/views/components/mute-and-block.vue'), i18n: i18n('common/views/components/mute-and-block.vue'),
data() { data() {
return { return {
muteFetching: true, muteFetching: true,
blockFetching: true, blockFetching: true,
mute: [], mute: [],
block: [] block: [],
mutedWords: ''
}; };
}, },
computed: {
_mutedWords: {
get() { return this.$store.state.settings.mutedWords; },
set(value) { this.$store.dispatch('settings/set', { key: 'mutedWords', value }); }
},
},
mounted() { mounted() {
this.mutedWords = this._mutedWords.map(words => words.join(' ')).join('\n');
this.$root.api('mute/list').then(mute => { this.$root.api('mute/list').then(mute => {
this.mute = mute.map(x => x.mutee); this.mute = mute.map(x => x.mutee);
this.muteFetching = false; this.muteFetching = false;
@ -49,6 +68,12 @@ export default Vue.extend({
this.block = blocking.map(x => x.blockee); this.block = blocking.map(x => x.blockee);
this.blockFetching = false; this.blockFetching = false;
}); });
},
methods: {
save() {
this._mutedWords = this.mutedWords.split('\n').map(line => line.split(' '));
}
} }
}); });
</script> </script>

View file

@ -2,7 +2,7 @@
<div <div
class="note" class="note"
:class="{ mini }" :class="{ mini }"
v-show="appearNote.deletedAt == null" v-show="appearNote.deletedAt == null && !hideThisNote"
:tabindex="appearNote.deletedAt == null ? '-1' : null" :tabindex="appearNote.deletedAt == null ? '-1' : null"
v-hotkey="keymap" v-hotkey="keymap"
:title="title" :title="title"

View file

@ -36,7 +36,7 @@
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import * as config from '../../../config'; import * as config from '../../../config';
import shouldMuteNote from '../../../common/scripts/should-mute-note';
import XNote from './note.vue'; import XNote from './note.vue';
const displayLimit = 30; const displayLimit = 30;
@ -119,28 +119,8 @@ export default Vue.extend({
}, },
prepend(note, silent = false) { prepend(note, silent = false) {
//#region //
const isMyNote = note.userId == this.$store.state.i.id; if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (this.$store.state.settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
return;
}
}
if (this.$store.state.settings.showRenotedMyNotes === false) {
if (isPureRenote && (note.renote.userId == this.$store.state.i.id)) {
return;
}
}
if (this.$store.state.settings.showLocalRenotes === false) {
if (isPureRenote && (note.renote.user.host == null)) {
return;
}
}
//#endregion
// //
if (document.hidden || !this.isScrollTop()) { if (document.hidden || !this.isScrollTop()) {

View file

@ -40,6 +40,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../../i18n'; import i18n from '../../../../i18n';
import shouldMuteNote from '../../../../common/scripts/should-mute-note';
import XNote from '../../components/note.vue'; import XNote from '../../components/note.vue';
@ -135,28 +136,8 @@ export default Vue.extend({
}, },
prepend(note, silent = false) { prepend(note, silent = false) {
//#region //
const isMyNote = note.userId == this.$store.state.i.id; if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (this.$store.state.settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
return;
}
}
if (this.$store.state.settings.showRenotedMyNotes === false) {
if (isPureRenote && (note.renote.userId == this.$store.state.i.id)) {
return;
}
}
if (this.$store.state.settings.showLocalRenotes === false) {
if (isPureRenote && (note.renote.user.host == null)) {
return;
}
}
//#endregion
// //
if (document.hidden) { if (document.hidden) {

View file

@ -1,7 +1,7 @@
<template> <template>
<div <div
class="note" class="note"
v-show="appearNote.deletedAt == null" v-show="appearNote.deletedAt == null && !hideThisNote"
:tabindex="appearNote.deletedAt == null ? '-1' : null" :tabindex="appearNote.deletedAt == null ? '-1' : null"
:class="{ renote: isRenote, smart: $store.state.device.postStyle == 'smart' }" :class="{ renote: isRenote, smart: $store.state.device.postStyle == 'smart' }"
v-hotkey="keymap" v-hotkey="keymap"

View file

@ -35,6 +35,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import i18n from '../../../i18n'; import i18n from '../../../i18n';
import shouldMuteNote from '../../../common/scripts/should-mute-note';
const displayLimit = 30; const displayLimit = 30;
@ -118,28 +119,8 @@ export default Vue.extend({
}, },
prepend(note, silent = false) { prepend(note, silent = false) {
//#region //
const isMyNote = note.userId == this.$store.state.i.id; if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (this.$store.state.settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
return;
}
}
if (this.$store.state.settings.showRenotedMyNotes === false) {
if (isPureRenote && (note.renote.userId == this.$store.state.i.id)) {
return;
}
}
if (this.$store.state.settings.showLocalRenotes === false) {
if (isPureRenote && (note.renote.user.host == null)) {
return;
}
}
//#endregion
// //
if (document.hidden || !this.isScrollTop()) { if (document.hidden || !this.isScrollTop()) {

View file

@ -34,6 +34,7 @@ const defaultSettings = {
iLikeSushi: false, iLikeSushi: false,
rememberNoteVisibility: false, rememberNoteVisibility: false,
defaultNoteVisibility: 'public', defaultNoteVisibility: 'public',
mutedWords: [],
games: { games: {
reversi: { reversi: {
showBoardLabels: false, showBoardLabels: false,