Merge pull request #3047 from mei23/mei-1031-blokings-list
blockings list
This commit is contained in:
		
						commit
						5e3372e932
					
				|  | @ -832,7 +832,8 @@ desktop/views/components/settings.vue: | |||
|   profile: "プロフィール" | ||||
|   notification: "通知" | ||||
|   apps: "アプリ" | ||||
|   mute: "ミュート" | ||||
|   mute-and-block: "ミュート/ブロック" | ||||
|   blocking: "ブロック" | ||||
|   security: "セキュリティ" | ||||
|   signin: "サインイン履歴" | ||||
|   password: "パスワード" | ||||
|  | @ -973,8 +974,12 @@ common/views/components/drive-settings.vue: | |||
|   in-use: "使用中" | ||||
|   stats: "統計" | ||||
| 
 | ||||
| desktop/views/components/settings.mute.vue: | ||||
|   no-users: "ミュートしているユーザーはいません" | ||||
| common/views/components/mute-and-block.vue: | ||||
|   mute-and-block: "ミュートとブロック" | ||||
|   mute: "ミュート" | ||||
|   block: "ブロック" | ||||
|   no-muted-users: "ミュートしているユーザーはいません" | ||||
|   no-blocked-users: "ブロックしているユーザーはいません" | ||||
| 
 | ||||
| desktop/views/components/settings.password.vue: | ||||
|   reset: "パスワードを変更する" | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| import Vue from 'vue'; | ||||
| 
 | ||||
| import muteAndBlock from './mute-and-block.vue'; | ||||
| import error from './error.vue'; | ||||
| import apiSettings from './api-settings.vue'; | ||||
| import driveSettings from './drive-settings.vue'; | ||||
|  | @ -50,6 +51,7 @@ import uiInfo from './ui/info.vue'; | |||
| import formButton from './ui/form/button.vue'; | ||||
| import formRadio from './ui/form/radio.vue'; | ||||
| 
 | ||||
| Vue.component('mk-mute-and-block', muteAndBlock); | ||||
| Vue.component('mk-error', error); | ||||
| Vue.component('mk-api-settings', apiSettings); | ||||
| Vue.component('mk-drive-settings', driveSettings); | ||||
|  |  | |||
|  | @ -0,0 +1,56 @@ | |||
| <template> | ||||
| <ui-card> | ||||
| 	<div slot="title">%fa:ban% %i18n:@mute-and-block%</div> | ||||
| 
 | ||||
| 	<section> | ||||
| 		<header>%i18n:@mute%</header> | ||||
| 		<ui-info v-if="!muteFetching && mute.length == 0"> | ||||
| 			<p>%i18n:@no-muted-users%</p> | ||||
| 		</ui-info> | ||||
| 		<div class="users" v-if="mute.length != 0"> | ||||
| 			<div v-for="user in mute" :key="user.id"> | ||||
| 				<p><b>{{ user | userName }}</b> @{{ user | acct }}</p> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</section> | ||||
| 
 | ||||
| 	<section> | ||||
| 		<header>%i18n:@block%</header> | ||||
| 		<ui-info v-if="!blockFetching && block.length == 0"> | ||||
| 			<p>%i18n:@no-blocked-users%</p> | ||||
| 		</ui-info> | ||||
| 		<div class="users" v-if="block.length != 0"> | ||||
| 			<div v-for="user in block" :key="user.id"> | ||||
| 				<p><b>{{ user | userName }}</b> @{{ user | acct }}</p> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</section> | ||||
| </ui-card> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	data() { | ||||
| 		return { | ||||
| 			muteFetching: true, | ||||
| 			blockFetching: true, | ||||
| 			mute: [], | ||||
| 			block: [] | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		(this as any).api('mute/list').then(mute => { | ||||
| 			this.mute = mute.map(x => x.mutee); | ||||
| 			this.muteFetching = false; | ||||
| 		}); | ||||
| 
 | ||||
| 		(this as any).api('blocking/list').then(blocking => { | ||||
| 			this.block = blocking.map(x => x.blockee); | ||||
| 			this.blockFetching = false; | ||||
| 		}); | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | @ -1,31 +0,0 @@ | |||
| <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('mute/list').then(x => { | ||||
| 			this.users = x.users; | ||||
| 			this.fetching = false; | ||||
| 		}); | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | @ -7,7 +7,7 @@ | |||
| 		<p :class="{ active: page == 'notification' }" @mousedown="page = 'notification'">%fa:R bell .fw%%i18n:@notification%</p> | ||||
| 		<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 == 'muteAndBlock' }" @mousedown="page = 'muteAndBlock'">%fa:ban .fw%%i18n:@mute-and-block%</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> | ||||
|  | @ -200,12 +200,9 @@ | |||
| 			</section> | ||||
| 		</ui-card> | ||||
| 
 | ||||
| 		<ui-card class="mute" v-show="page == 'mute'"> | ||||
| 			<div slot="title">%fa:ban% %i18n:@mute%</div> | ||||
| 			<section> | ||||
| 				<x-mute/> | ||||
| 			</section> | ||||
| 		</ui-card> | ||||
| 		<div class="muteAndBlock" v-show="page == 'muteAndBlock'"> | ||||
| 			<mk-mute-and-block/> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<ui-card class="apps" v-show="page == 'apps'"> | ||||
| 			<div slot="title">%fa:puzzle-piece% %i18n:@apps%</div> | ||||
|  | @ -289,7 +286,6 @@ | |||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import XMute from './settings.mute.vue'; | ||||
| import XPassword from './settings.password.vue'; | ||||
| import X2fa from './settings.2fa.vue'; | ||||
| import XApps from './settings.apps.vue'; | ||||
|  | @ -300,7 +296,6 @@ import checkForUpdate from '../../../common/scripts/check-for-update'; | |||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| 		XMute, | ||||
| 		XPassword, | ||||
| 		X2fa, | ||||
| 		XApps, | ||||
|  |  | |||
|  | @ -85,6 +85,8 @@ | |||
| 
 | ||||
| 			<mk-drive-settings/> | ||||
| 
 | ||||
| 			<mk-mute-and-block/> | ||||
| 
 | ||||
| 			<ui-card> | ||||
| 				<div slot="title">%fa:volume-up% %i18n:@sound%</div> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,12 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| const deepcopy = require('deepcopy'); | ||||
| import { pack as packUser } from './user'; | ||||
| 
 | ||||
| const Blocking = db.get<IBlocking>('blocking'); | ||||
| Blocking.createIndex('blockerId'); | ||||
| Blocking.createIndex('blockeeId'); | ||||
| Blocking.createIndex(['blockerId', 'blockeeId'], { unique: true }); | ||||
| export default Blocking; | ||||
| 
 | ||||
|  | @ -11,3 +16,39 @@ export type IBlocking = { | |||
| 	blockeeId: mongo.ObjectID; | ||||
| 	blockerId: mongo.ObjectID; | ||||
| }; | ||||
| 
 | ||||
| export const packMany = async ( | ||||
| 	blockings: (string | mongo.ObjectID | IBlocking)[], | ||||
| 	me?: string | mongo.ObjectID | IUser | ||||
| ) => { | ||||
| 	return (await Promise.all(blockings.map(x => pack(x, me)))).filter(x => x != null); | ||||
| }; | ||||
| 
 | ||||
| export const pack = ( | ||||
| 	blocking: any, | ||||
| 	me?: any | ||||
| ) => new Promise<any>(async (resolve, reject) => { | ||||
| 	let _blocking: any; | ||||
| 
 | ||||
| 	// Populate the blocking if 'blocking' is ID
 | ||||
| 	if (isObjectId(blocking)) { | ||||
| 		_blocking = await Blocking.findOne({ | ||||
| 			_id: blocking | ||||
| 		}); | ||||
| 	} else if (typeof blocking === 'string') { | ||||
| 		_blocking = await Blocking.findOne({ | ||||
| 			_id: new mongo.ObjectID(blocking) | ||||
| 		}); | ||||
| 	} else { | ||||
| 		_blocking = deepcopy(blocking); | ||||
| 	} | ||||
| 
 | ||||
| 	// Rename _id to id
 | ||||
| 	_blocking.id = _blocking._id; | ||||
| 	delete _blocking._id; | ||||
| 
 | ||||
| 	// Populate blockee
 | ||||
| 	_blocking.blockee = await packUser(_blocking.blockeeId, me); | ||||
| 
 | ||||
| 	resolve(_blocking); | ||||
| }); | ||||
|  |  | |||
|  | @ -1,7 +1,12 @@ | |||
| import * as mongo from 'mongodb'; | ||||
| import db from '../db/mongodb'; | ||||
| import isObjectId from '../misc/is-objectid'; | ||||
| const deepcopy = require('deepcopy'); | ||||
| import { pack as packUser } from './user'; | ||||
| 
 | ||||
| const Mute = db.get<IMute>('mute'); | ||||
| Mute.createIndex('muterId'); | ||||
| Mute.createIndex('muteeId'); | ||||
| Mute.createIndex(['muterId', 'muteeId'], { unique: true }); | ||||
| export default Mute; | ||||
| 
 | ||||
|  | @ -11,3 +16,39 @@ export interface IMute { | |||
| 	muterId: mongo.ObjectID; | ||||
| 	muteeId: mongo.ObjectID; | ||||
| } | ||||
| 
 | ||||
| export const packMany = async ( | ||||
| 	mutes: (string | mongo.ObjectID | IMute)[], | ||||
| 	me?: string | mongo.ObjectID | IUser | ||||
| ) => { | ||||
| 	return (await Promise.all(mutes.map(x => pack(x, me)))).filter(x => x != null); | ||||
| }; | ||||
| 
 | ||||
| export const pack = ( | ||||
| 	mute: any, | ||||
| 	me?: any | ||||
| ) => new Promise<any>(async (resolve, reject) => { | ||||
| 	let _mute: any; | ||||
| 
 | ||||
| 	// Populate the mute if 'mute' is ID
 | ||||
| 	if (isObjectId(mute)) { | ||||
| 		_mute = await Mute.findOne({ | ||||
| 			_id: mute | ||||
| 		}); | ||||
| 	} else if (typeof mute === 'string') { | ||||
| 		_mute = await Mute.findOne({ | ||||
| 			_id: new mongo.ObjectID(mute) | ||||
| 		}); | ||||
| 	} else { | ||||
| 		_mute = deepcopy(mute); | ||||
| 	} | ||||
| 
 | ||||
| 	// Rename _id to id
 | ||||
| 	_mute.id = _mute._id; | ||||
| 	delete _mute._id; | ||||
| 
 | ||||
| 	// Populate mutee
 | ||||
| 	_mute.mutee = await packUser(_mute.muteeId, me); | ||||
| 
 | ||||
| 	resolve(_mute); | ||||
| }); | ||||
|  |  | |||
|  | @ -0,0 +1,64 @@ | |||
| import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; | ||||
| import Blocking, { packMany } from '../../../../models/blocking'; | ||||
| import { ILocalUser } from '../../../../models/user'; | ||||
| import getParams from '../../get-params'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	desc: { | ||||
| 		'ja-JP': 'ブロックしているユーザー一覧を取得します。', | ||||
| 		'en-US': 'Get blocking users.' | ||||
| 	}, | ||||
| 
 | ||||
| 	requireCredential: true, | ||||
| 
 | ||||
| 	kind: 'following-read', | ||||
| 
 | ||||
| 	params: { | ||||
| 		limit: $.num.optional.range(1, 100).note({ | ||||
| 			default: 30 | ||||
| 		}), | ||||
| 
 | ||||
| 		sinceId: $.type(ID).optional.note({ | ||||
| 		}), | ||||
| 
 | ||||
| 		untilId: $.type(ID).optional.note({ | ||||
| 		}), | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { | ||||
| 	const [ps, psErr] = getParams(meta, params); | ||||
| 	if (psErr) return rej(psErr); | ||||
| 
 | ||||
| 	// Check if both of sinceId and untilId is specified
 | ||||
| 	if (ps.sinceId && ps.untilId) { | ||||
| 		return rej('cannot set sinceId and untilId'); | ||||
| 	} | ||||
| 
 | ||||
| 	const query = { | ||||
| 		blockerId: me._id | ||||
| 	} as any; | ||||
| 
 | ||||
| 	const sort = { | ||||
| 		_id: -1 | ||||
| 	}; | ||||
| 
 | ||||
| 	if (ps.sinceId) { | ||||
| 		sort._id = 1; | ||||
| 		query._id = { | ||||
| 			$gt: ps.sinceId | ||||
| 		}; | ||||
| 	} else if (ps.untilId) { | ||||
| 		query._id = { | ||||
| 			$lt: ps.untilId | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	const blockings = await Blocking | ||||
| 		.find(query, { | ||||
| 			limit: ps.limit, | ||||
| 			sort: sort | ||||
| 		}); | ||||
| 
 | ||||
| 	res(await packMany(blockings, me)); | ||||
| }); | ||||
|  | @ -1,7 +1,7 @@ | |||
| import $ from 'cafy'; import ID from '../../../../misc/cafy-id'; | ||||
| import Mute from '../../../../models/mute'; | ||||
| import { pack, ILocalUser } from '../../../../models/user'; | ||||
| import { getFriendIds } from '../../common/get-friends'; | ||||
| import Mute, { packMany } from '../../../../models/mute'; | ||||
| import { ILocalUser } from '../../../../models/user'; | ||||
| import getParams from '../../get-params'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	desc: { | ||||
|  | @ -11,64 +11,54 @@ export const meta = { | |||
| 
 | ||||
| 	requireCredential: true, | ||||
| 
 | ||||
| 	kind: 'account/read' | ||||
| 	kind: 'account/read', | ||||
| 
 | ||||
| 	params: { | ||||
| 		limit: $.num.optional.range(1, 100).note({ | ||||
| 			default: 30 | ||||
| 		}), | ||||
| 
 | ||||
| 		sinceId: $.type(ID).optional.note({ | ||||
| 		}), | ||||
| 
 | ||||
| 		untilId: $.type(ID).optional.note({ | ||||
| 		}), | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { | ||||
| 	// Get 'iknow' parameter
 | ||||
| 	const [iknow = false, iknowErr] = $.bool.optional.get(params.iknow); | ||||
| 	if (iknowErr) return rej('invalid iknow param'); | ||||
| 	const [ps, psErr] = getParams(meta, params); | ||||
| 	if (psErr) return rej(psErr); | ||||
| 
 | ||||
| 	// Get 'limit' parameter
 | ||||
| 	const [limit = 30, limitErr] = $.num.optional.range(1, 100).get(params.limit); | ||||
| 	if (limitErr) return rej('invalid limit param'); | ||||
| 	// Check if both of sinceId and untilId is specified
 | ||||
| 	if (ps.sinceId && ps.untilId) { | ||||
| 		return rej('cannot set sinceId and untilId'); | ||||
| 	} | ||||
| 
 | ||||
| 	// Get 'cursor' parameter
 | ||||
| 	const [cursor = null, cursorErr] = $.type(ID).optional.get(params.cursor); | ||||
| 	if (cursorErr) return rej('invalid cursor param'); | ||||
| 
 | ||||
| 	// Construct query
 | ||||
| 	const query = { | ||||
| 		muterId: me._id, | ||||
| 		deletedAt: { $exists: false } | ||||
| 		muterId: me._id | ||||
| 	} as any; | ||||
| 
 | ||||
| 	if (iknow) { | ||||
| 		// Get my friends
 | ||||
| 		const myFriends = await getFriendIds(me._id); | ||||
| 	const sort = { | ||||
| 		_id: -1 | ||||
| 	}; | ||||
| 
 | ||||
| 		query.muteeId = { | ||||
| 			$in: myFriends | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	// カーソルが指定されている場合
 | ||||
| 	if (cursor) { | ||||
| 	if (ps.sinceId) { | ||||
| 		sort._id = 1; | ||||
| 		query._id = { | ||||
| 			$lt: cursor | ||||
| 			$gt: ps.sinceId | ||||
| 		}; | ||||
| 	} else if (ps.untilId) { | ||||
| 		query._id = { | ||||
| 			$lt: ps.untilId | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
| 	// Get mutes
 | ||||
| 	const mutes = await Mute | ||||
| 		.find(query, { | ||||
| 			limit: limit + 1, | ||||
| 			sort: { _id: -1 } | ||||
| 			limit: ps.limit, | ||||
| 			sort: sort | ||||
| 		}); | ||||
| 
 | ||||
| 	// 「次のページ」があるかどうか
 | ||||
| 	const inStock = mutes.length === limit + 1; | ||||
| 	if (inStock) { | ||||
| 		mutes.pop(); | ||||
| 	} | ||||
| 
 | ||||
| 	// Serialize
 | ||||
| 	const users = await Promise.all(mutes.map(async m => | ||||
| 		await pack(m.muteeId, me, { detail: true }))); | ||||
| 
 | ||||
| 	// Response
 | ||||
| 	res({ | ||||
| 		users: users, | ||||
| 		next: inStock ? mutes[mutes.length - 1]._id : null, | ||||
| 	}); | ||||
| 	res(await packMany(mutes, me)); | ||||
| }); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue