summaryrefslogtreecommitdiff
path: root/src/client/app
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2019-01-19 19:16:48 +0900
committersyuilo <syuilotan@yahoo.co.jp>2019-01-19 19:16:48 +0900
commit048b9c295ed77f665c22f4b3f923013d0d9be990 (patch)
tree94fcc3b915b6dab60ff66f2e08d1c65d8a25e0c0 /src/client/app
parentFix typo (diff)
downloadmisskey-048b9c295ed77f665c22f4b3f923013d0d9be990.tar.gz
misskey-048b9c295ed77f665c22f4b3f923013d0d9be990.tar.bz2
misskey-048b9c295ed77f665c22f4b3f923013d0d9be990.zip
スパム報告機能
Resolve #1970
Diffstat (limited to 'src/client/app')
-rw-r--r--src/client/app/admin/views/abuse.vue87
-rw-r--r--src/client/app/admin/views/index.vue10
-rw-r--r--src/client/app/common/views/components/user-menu.vue157
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.user-column.vue38
-rw-r--r--src/client/app/desktop/views/pages/user/user.header.vue1
-rw-r--r--src/client/app/desktop/views/pages/user/user.profile.vue80
-rw-r--r--src/client/app/mobile/views/pages/user.vue85
7 files changed, 264 insertions, 194 deletions
diff --git a/src/client/app/admin/views/abuse.vue b/src/client/app/admin/views/abuse.vue
new file mode 100644
index 0000000000..9bb77e8e6c
--- /dev/null
+++ b/src/client/app/admin/views/abuse.vue
@@ -0,0 +1,87 @@
+<template>
+<div class="wbjusose">
+ <ui-card>
+ <div slot="title"><fa :icon="faExclamationCircle"/> {{ $t('title') }}</div>
+ <section class="fit-top">
+ <sequential-entrance animation="entranceFromTop" delay="25">
+ <div v-for="report in userReports" :key="report.id" class="haexwsjc">
+ <ui-horizon-group inputs>
+ <ui-input :value="report.user | acct" type="text">
+ <span>{{ $t('target') }}</span>
+ </ui-input>
+ <ui-input :value="report.reporter | acct" type="text">
+ <span>{{ $t('reporter') }}</span>
+ </ui-input>
+ </ui-horizon-group>
+ <ui-textarea :value="report.comment" readonly>
+ <span>{{ $t('details') }}</span>
+ </ui-textarea>
+ <ui-button @click="removeReport(report)">{{ $t('remove-report') }}</ui-button>
+ </div>
+ </sequential-entrance>
+ <ui-button v-if="existMore" @click="fetchUserReports">{{ $t('@.load-more') }}</ui-button>
+ </section>
+ </ui-card>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../i18n';
+import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
+
+export default Vue.extend({
+ i18n: i18n('admin/views/abuse.vue'),
+
+ data() {
+ return {
+ limit: 10,
+ untilId: undefined,
+ userReports: [],
+ existMore: false,
+ faExclamationCircle
+ };
+ },
+
+ mounted() {
+ this.fetchUserReports();
+ },
+
+ methods: {
+ fetchUserReports() {
+ this.$root.api('admin/abuse-user-reports', {
+ untilId: this.untilId,
+ limit: this.limit + 1
+ }).then(reports => {
+ if (reports.length == this.limit + 1) {
+ reports.pop();
+ this.existMore = true;
+ } else {
+ this.existMore = false;
+ }
+ this.userReports = this.userReports.concat(reports);
+ this.untilId = this.userReports[this.userReports.length - 1].id;
+ });
+ },
+
+ removeReport(report) {
+ this.$root.api('admin/remove-abuse-user-report', {
+ reportId: report.id
+ }).then(() => {
+ this.userReports = this.userReports.filter(r => r.id != report.id);
+ });
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.wbjusose
+ @media (min-width 500px)
+ padding 16px
+
+ .haexwsjc
+ padding-bottom 16px
+ border-bottom solid 1px var(--faceDivider)
+
+</style>
diff --git a/src/client/app/admin/views/index.vue b/src/client/app/admin/views/index.vue
index 9524a98542..5a1de2d76a 100644
--- a/src/client/app/admin/views/index.vue
+++ b/src/client/app/admin/views/index.vue
@@ -27,6 +27,7 @@
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
+ <li @click="nav('abuse')" :class="{ active: page == 'abuse' }"><fa :icon="faExclamationCircle" fixed-width/>{{ $t('abuse') }}</li>
</ul>
<div class="back-to-misskey">
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
@@ -45,7 +46,7 @@
<div v-if="page == 'announcements'"><x-announcements/></div>
<div v-if="page == 'hashtags'"><x-hashtags/></div>
<div v-if="page == 'drive'"><x-drive/></div>
- <div v-if="page == 'update'"></div>
+ <div v-if="page == 'abuse'"><x-abuse/></div>
</div>
</main>
</div>
@@ -63,7 +64,8 @@ import XAnnouncements from "./announcements.vue";
import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue";
import XDrive from "./drive.vue";
-import { faHeadset, faArrowLeft, faShareAlt } from '@fortawesome/free-solid-svg-icons';
+import XAbuse from "./abuse.vue";
+import { faHeadset, faArrowLeft, faShareAlt, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import { faGrin } from '@fortawesome/free-regular-svg-icons';
// Detect the user agent
@@ -81,6 +83,7 @@ export default Vue.extend({
XHashtags,
XUsers,
XDrive,
+ XAbuse,
},
provide: {
isMobile
@@ -94,7 +97,8 @@ export default Vue.extend({
faGrin,
faArrowLeft,
faHeadset,
- faShareAlt
+ faShareAlt,
+ faExclamationCircle
};
},
methods: {
diff --git a/src/client/app/common/views/components/user-menu.vue b/src/client/app/common/views/components/user-menu.vue
new file mode 100644
index 0000000000..a4a27142f9
--- /dev/null
+++ b/src/client/app/common/views/components/user-menu.vue
@@ -0,0 +1,157 @@
+<template>
+<div style="position:initial">
+ <mk-menu :source="source" :items="items" @closed="closed"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../../i18n';
+import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
+import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
+
+export default Vue.extend({
+ i18n: i18n('common/views/components/user-menu.vue'),
+
+ props: ['user', 'source'],
+
+ data() {
+ let menu = [{
+ icon: ['fas', 'at'],
+ text: this.$t('mention'),
+ action: () => {
+ this.$post({ mention: this.user });
+ }
+ }, null, {
+ icon: ['fas', 'list'],
+ text: this.$t('push-to-list'),
+ action: this.pushList
+ }, null, {
+ icon: this.user.isMuted ? ['fas', 'eye'] : ['far', 'eye-slash'],
+ text: this.user.isMuted ? this.$t('unmute') : this.$t('mute'),
+ action: this.toggleMute
+ }, {
+ icon: 'ban',
+ text: this.user.isBlocking ? this.$t('unblock') : this.$t('block'),
+ action: this.toggleBlock
+ }, null, {
+ icon: faExclamationCircle,
+ text: this.$t('report-abuse'),
+ action: this.reportAbuse
+ }];
+
+ return {
+ items: menu
+ };
+ },
+
+ methods: {
+ closed() {
+ this.$nextTick(() => {
+ this.destroyDom();
+ });
+ },
+
+ async pushList() {
+ const lists = await this.$root.api('users/lists/list');
+ const { canceled, result: listId } = await this.$root.dialog({
+ type: null,
+ title: this.$t('select-list'),
+ select: {
+ items: lists.map(list => ({
+ value: list.id, text: list.title
+ }))
+ },
+ showCancelButton: true
+ });
+ if (canceled) return;
+ await this.$root.api('users/lists/push', {
+ listId: listId,
+ userId: this.user.id
+ });
+ this.$root.dialog({
+ type: 'success',
+ text: this.$t('list-pushed', {
+ user: this.user.name,
+ list: lists.find(l => l.id === listId).title
+ })
+ });
+ },
+
+ toggleMute() {
+ if (this.user.isMuted) {
+ this.$root.api('mute/delete', {
+ userId: this.user.id
+ }).then(() => {
+ this.user.isMuted = false;
+ }, () => {
+ this.$root.dialog({
+ type: 'error',
+ text: e
+ });
+ });
+ } else {
+ this.$root.api('mute/create', {
+ userId: this.user.id
+ }).then(() => {
+ this.user.isMuted = true;
+ }, () => {
+ this.$root.dialog({
+ type: 'error',
+ text: e
+ });
+ });
+ }
+ },
+
+ toggleBlock() {
+ if (this.user.isBlocking) {
+ this.$root.api('blocking/delete', {
+ userId: this.user.id
+ }).then(() => {
+ this.user.isBlocking = false;
+ }, () => {
+ this.$root.dialog({
+ type: 'error',
+ text: e
+ });
+ });
+ } else {
+ this.$root.api('blocking/create', {
+ userId: this.user.id
+ }).then(() => {
+ this.user.isBlocking = true;
+ }, () => {
+ this.$root.dialog({
+ type: 'error',
+ text: e
+ });
+ });
+ }
+ },
+
+ async reportAbuse() {
+ const reported = this.$t('report-abuse-reported'); // なぜか後で参照すると null になるので最初にメモリに確保しておく
+ const { canceled, result: comment } = await this.$root.dialog({
+ title: this.$t('report-abuse-detail'),
+ input: true
+ });
+ if (canceled) return;
+ this.$root.api('users/report-abuse', {
+ userId: this.user.id,
+ comment: comment
+ }).then(() => {
+ this.$root.dialog({
+ type: 'success',
+ text: reported
+ });
+ }, e => {
+ this.$root.dialog({
+ type: 'error',
+ text: e
+ });
+ });
+ }
+ }
+});
+</script>
diff --git a/src/client/app/desktop/views/pages/deck/deck.user-column.vue b/src/client/app/desktop/views/pages/deck/deck.user-column.vue
index a856e74bf6..e640caa586 100644
--- a/src/client/app/desktop/views/pages/deck/deck.user-column.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.user-column.vue
@@ -49,9 +49,6 @@
<b>{{ user.followersCount | number }}</b>
<span>{{ $t('followers') }}</span>
</div>
- <div class="mention">
- <button @click="mention" :title="$t('mention')"><fa icon="at"/></button>
- </div>
</div>
</div>
<div class="pinned" v-if="user.pinnedNotes && user.pinnedNotes.length > 0">
@@ -100,8 +97,7 @@ import parseAcct from '../../../../../../misc/acct/parse';
import XColumn from './deck.column.vue';
import XNotes from './deck.notes.vue';
import XNote from '../../components/note.vue';
-import Menu from '../../../../common/views/components/menu.vue';
-import MkUserListsWindow from '../../components/user-lists-window.vue';
+import XUserMenu from '../../../../common/views/components/user-menu.vue';
import { concat } from '../../../../../../prelude/array';
import * as ApexCharts from 'apexcharts';
@@ -306,33 +302,10 @@ export default Vue.extend({
return promise;
},
- mention() {
- this.$post({ mention: this.user });
- },
-
menu() {
- let menu = [{
- icon: 'list',
- text: this.$t('push-to-a-list'),
- action: () => {
- const w = this.$root.new(MkUserListsWindow);
- w.$once('choosen', async list => {
- w.close();
- await this.$root.api('users/lists/push', {
- listId: list.id,
- userId: this.user.id
- });
- this.$root.dialog({
- type: 'success',
- splash: true
- });
- });
- }
- }];
-
- this.$root.new(Menu, {
+ this.$root.new(XUserMenu, {
source: this.$refs.menu,
- items: menu
+ user: this.user
});
},
@@ -459,7 +432,7 @@ export default Vue.extend({
> .counts
display grid
- grid-template-columns 2fr 2fr 2fr 1fr
+ grid-template-columns 2fr 2fr 2fr
margin-top 8px
border-top solid var(--lineWidth) var(--faceDivider)
@@ -476,9 +449,6 @@ export default Vue.extend({
font-size 80%
opacity 0.7
- > .mention
- display flex
-
> *
> p.caption
margin 0
diff --git a/src/client/app/desktop/views/pages/user/user.header.vue b/src/client/app/desktop/views/pages/user/user.header.vue
index b092a0003e..c33ca84ebc 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -36,7 +36,6 @@
<span class="notes-count"><b>{{ user.notesCount | number }}</b>{{ $t('posts') }}</span>
<router-link :to="user | userPage('following')" class="following clickable"><b>{{ user.followingCount | number }}</b>{{ $t('following') }}</router-link>
<router-link :to="user | userPage('followers')" class="followers clickable"><b>{{ user.followersCount | number }}</b>{{ $t('followers') }}</router-link>
- <button @click="mention" :title="$t('mention')"><fa icon="at"/></button>
</div>
</div>
</div>
diff --git a/src/client/app/desktop/views/pages/user/user.profile.vue b/src/client/app/desktop/views/pages/user/user.profile.vue
index 58afed4001..22cbf6546f 100644
--- a/src/client/app/desktop/views/pages/user/user.profile.vue
+++ b/src/client/app/desktop/views/pages/user/user.profile.vue
@@ -9,15 +9,7 @@
</p>
</div>
<div class="action-form">
- <ui-button @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
- <span v-if="user.isMuted"><fa icon="eye"/> {{ $t('unmute') }}</span>
- <span v-else><fa :icon="['far', 'eye-slash']"/> {{ $t('mute') }}</span>
- </ui-button>
- <ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
- <span v-if="user.isBlocking"><fa icon="ban"/> {{ $t('unblock') }}</span>
- <span v-else><fa icon="ban"/> {{ $t('block') }}</span>
- </ui-button>
- <ui-button @click="list"><fa icon="list"/> {{ $t('push-to-a-list') }}</ui-button>
+ <ui-button @click="menu" ref="menu">{{ $t('menu') }}</ui-button>
</div>
</div>
</template>
@@ -25,7 +17,7 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../../i18n';
-import MkUserListsWindow from '../../components/user-lists-window.vue';
+import XUserMenu from '../../../../common/views/components/user-menu.vue';
export default Vue.extend({
i18n: i18n('desktop/views/pages/user/user.profile.vue'),
@@ -52,72 +44,12 @@ export default Vue.extend({
});
},
- mute() {
- this.$root.api('mute/create', {
- userId: this.user.id
- }).then(() => {
- this.user.isMuted = true;
- }, () => {
- alert('error');
- });
- },
-
- unmute() {
- this.$root.api('mute/delete', {
- userId: this.user.id
- }).then(() => {
- this.user.isMuted = false;
- }, () => {
- alert('error');
- });
- },
-
- block() {
- this.$root.dialog({
- type: 'warning',
- text: this.$t('block-confirm'),
- showCancelButton: true
- }).then(({ canceled }) => {
- if (canceled) return;
-
- this.$root.api('blocking/create', {
- userId: this.user.id
- }).then(() => {
- this.user.isBlocking = true;
- }, () => {
- alert('error');
- });
- });
- },
-
- unblock() {
- this.$root.api('blocking/delete', {
- userId: this.user.id
- }).then(() => {
- this.user.isBlocking = false;
- }, () => {
- alert('error');
+ menu() {
+ this.$root.new(XUserMenu, {
+ source: this.$refs.menu.$el,
+ user: this.user
});
},
-
- list() {
- const w = this.$root.new(MkUserListsWindow);
- w.$once('choosen', async list => {
- w.close();
- await this.$root.api('users/lists/push', {
- listId: list.id,
- userId: this.user.id
- });
- this.$root.dialog({
- type: 'success',
- title: 'Done!',
- text: this.$t('list-pushed', {
- user: this.user.name,
- list: list.title
- })
- });
- });
- }
}
});
</script>
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index 5f3feabb6e..c475750cf2 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -55,7 +55,6 @@
<b>{{ user.followersCount | number }}</b>
<i>{{ $t('followers') }}</i>
</a>
- <button @click="mention"><fa icon="at"/></button>
</div>
</div>
</header>
@@ -81,7 +80,7 @@ import i18n from '../../../i18n';
import * as age from 's-age';
import parseAcct from '../../../../../misc/acct/parse';
import Progress from '../../../common/scripts/loading';
-import Menu from '../../../common/views/components/menu.vue';
+import XUserMenu from '../../../common/views/components/user-menu.vue';
import XHome from './user/home.vue';
export default Vue.extend({
@@ -127,88 +126,10 @@ export default Vue.extend({
});
},
- mention() {
- this.$post({ mention: this.user });
- },
-
menu() {
- let menu = [{
- icon: ['fas', 'list'],
- text: this.$t('push-to-list'),
- action: async () => {
- const lists = await this.$root.api('users/lists/list');
- const { canceled, result: listId } = await this.$root.dialog({
- type: null,
- title: this.$t('select-list'),
- select: {
- items: lists.map(list => ({
- value: list.id, text: list.title
- }))
- },
- showCancelButton: true
- });
- if (canceled) return;
- await this.$root.api('users/lists/push', {
- listId: listId,
- userId: this.user.id
- });
- this.$root.dialog({
- type: 'success',
- text: this.$t('list-pushed', {
- user: this.user.name,
- list: lists.find(l => l.id === listId).title
- })
- });
- }
- }, null, {
- icon: this.user.isMuted ? ['fas', 'eye'] : ['far', 'eye-slash'],
- text: this.user.isMuted ? this.$t('unmute') : this.$t('mute'),
- action: () => {
- if (this.user.isMuted) {
- this.$root.api('mute/delete', {
- userId: this.user.id
- }).then(() => {
- this.user.isMuted = false;
- }, () => {
- alert('error');
- });
- } else {
- this.$root.api('mute/create', {
- userId: this.user.id
- }).then(() => {
- this.user.isMuted = true;
- }, () => {
- alert('error');
- });
- }
- }
- }, {
- icon: 'ban',
- text: this.user.isBlocking ? this.$t('unblock') : this.$t('block'),
- action: () => {
- if (this.user.isBlocking) {
- this.$root.api('blocking/delete', {
- userId: this.user.id
- }).then(() => {
- this.user.isBlocking = false;
- }, () => {
- alert('error');
- });
- } else {
- this.$root.api('blocking/create', {
- userId: this.user.id
- }).then(() => {
- this.user.isBlocking = true;
- }, () => {
- alert('error');
- });
- }
- }
- }];
-
- this.$root.new(Menu, {
+ this.$root.new(XUserMenu, {
source: this.$refs.menu,
- items: menu
+ user: this.user
});
},
}