diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index bc7559b14b..69fde53f54 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -833,6 +833,7 @@ desktop/views/components/settings.vue: notification: "通知" apps: "アプリ" mute: "ミュート" + blocking: "ブロック" security: "セキュリティ" signin: "サインイン履歴" password: "パスワード" @@ -976,6 +977,9 @@ common/views/components/drive-settings.vue: desktop/views/components/settings.mute.vue: no-users: "ミュートしているユーザーはいません" +desktop/views/components/settings.blocking.vue: + no-users: "ブロックしているユーザーはいません" + desktop/views/components/settings.password.vue: reset: "パスワードを変更する" enter-current-password: "現在のパスワードを入力してください" diff --git a/src/client/app/desktop/views/components/settings.blocking.vue b/src/client/app/desktop/views/components/settings.blocking.vue new file mode 100644 index 0000000000..43e4bf9209 --- /dev/null +++ b/src/client/app/desktop/views/components/settings.blocking.vue @@ -0,0 +1,31 @@ +<template> +<div> + <div class="none ui info" v-if="!fetching && users.length == 0"> + <p>%fa:info-circle%%i18n:@no-users%</p> + </div> + <div class="users" v-if="users.length != 0"> + <div v-for="user in users" :key="user.id"> + <p><b>{{ user | userName }}</b> @{{ user | acct }}</p> + </div> + </div> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; + +export default Vue.extend({ + data() { + return { + fetching: true, + users: [] + }; + }, + mounted() { + (this as any).api('blocking/list').then(x => { + this.users = x.users; + this.fetching = false; + }); + } +}); +</script> diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue index b5c02e486e..e77320bef3 100644 --- a/src/client/app/desktop/views/components/settings.vue +++ b/src/client/app/desktop/views/components/settings.vue @@ -8,6 +8,7 @@ <p :class="{ active: page == 'drive' }" @mousedown="page = 'drive'">%fa:cloud .fw%%i18n:common.drive%</p> <p :class="{ active: page == 'hashtags' }" @mousedown="page = 'hashtags'">%fa:hashtag .fw%%i18n:@tags%</p> <p :class="{ active: page == 'mute' }" @mousedown="page = 'mute'">%fa:ban .fw%%i18n:@mute%</p> + <p :class="{ active: page == 'blocking' }" @mousedown="page = 'blocking'">%fa:ban .fw%%i18n:@blocking%</p> <p :class="{ active: page == 'apps' }" @mousedown="page = 'apps'">%fa:puzzle-piece .fw%%i18n:@apps%</p> <p :class="{ active: page == 'security' }" @mousedown="page = 'security'">%fa:unlock-alt .fw%%i18n:@security%</p> <p :class="{ active: page == 'api' }" @mousedown="page = 'api'">%fa:key .fw%API</p> @@ -207,6 +208,13 @@ </section> </ui-card> + <ui-card class="blocking" v-show="page == 'blocking'"> + <div slot="title">%fa:ban% %i18n:@blocking%</div> + <section> + <x-blocking/> + </section> + </ui-card> + <ui-card class="apps" v-show="page == 'apps'"> <div slot="title">%fa:puzzle-piece% %i18n:@apps%</div> <section> @@ -290,6 +298,7 @@ <script lang="ts"> import Vue from 'vue'; import XMute from './settings.mute.vue'; +import XBlocking from './settings.blocking.vue'; import XPassword from './settings.password.vue'; import X2fa from './settings.2fa.vue'; import XApps from './settings.apps.vue'; @@ -301,6 +310,7 @@ import checkForUpdate from '../../../common/scripts/check-for-update'; export default Vue.extend({ components: { XMute, + XBlocking, XPassword, X2fa, XApps, diff --git a/src/server/api/endpoints/blocking/list.ts b/src/server/api/endpoints/blocking/list.ts new file mode 100644 index 0000000000..ba1c77d2c7 --- /dev/null +++ b/src/server/api/endpoints/blocking/list.ts @@ -0,0 +1,59 @@ +import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; +import Blocking from '../../../../models/blocking'; +import { pack, ILocalUser } from '../../../../models/user'; + +export const meta = { + desc: { + 'ja-JP': 'ブロックしているユーザー一覧を取得します。', + 'en-US': 'Get blocking users.' + }, + + requireCredential: true, + + kind: 'following-read' +}; + +export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { + // Get 'limit' parameter + const [limit = 30, limitErr] = $.num.optional.range(1, 100).get(params.limit); + if (limitErr) return rej('invalid limit param'); + + // Get 'cursor' parameter + const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor); + if (cursorErr) return rej('invalid cursor param'); + + // Construct query + const query = { + blockerId: me._id + } as any; + + // カーソルが指定されている場合 + if (cursor) { + query._id = { + $lt: cursor + }; + } + + // Get blockings + const blockings = await Blocking + .find(query, { + limit: limit + 1, + sort: { _id: -1 } + }); + + // 「次のページ」があるかどうか + const inStock = blockings.length === limit + 1; + if (inStock) { + blockings.pop(); + } + + // Serialize + const users = await Promise.all(blockings.map(async m => + await pack(m.blockeeId, me, { detail: true }))); + + // Response + res({ + users: users, + next: inStock ? blockings[blockings.length - 1]._id : null, + }); +});