Improve usability of users view (#5176)
* Improve usability of users view Resolve #5173 * Fix query * Follow review and fix * Follow review
This commit is contained in:
		| @@ -1502,6 +1502,8 @@ admin/views/users.vue: | |||||||
|   remote-user-updated: "リモートユーザー情報を更新しました" |   remote-user-updated: "リモートユーザー情報を更新しました" | ||||||
|   delete-all-files: "すべてのファイルを削除" |   delete-all-files: "すべてのファイルを削除" | ||||||
|   delete-all-files-confirm: "すべてのファイルを削除しますか?" |   delete-all-files-confirm: "すべてのファイルを削除しますか?" | ||||||
|  |   username: "ユーザー名" | ||||||
|  |   host: "ホスト" | ||||||
|   users: |   users: | ||||||
|     title: "ユーザー" |     title: "ユーザー" | ||||||
|     sort: |     sort: | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| <template> | <template> | ||||||
| <div class="kofvwchc"> | <div class="kofvwchc" @click="click(user.id)"> | ||||||
| 	<div> | 	<div> | ||||||
| 		<a :href="user | userPage(null, true)"> | 		<a :href="user | userPage(null, true)"> | ||||||
| 			<mk-avatar class="avatar" :user="user" :disable-link="true"/> | 			<mk-avatar class="avatar" :user="user" :disable-link="true"/> | ||||||
| @@ -32,7 +32,7 @@ import { faSnowflake } from '@fortawesome/free-regular-svg-icons'; | |||||||
|  |  | ||||||
| export default Vue.extend({ | export default Vue.extend({ | ||||||
| 	i18n: i18n('admin/views/users.vue'), | 	i18n: i18n('admin/views/users.vue'), | ||||||
| 	props: ['user'], | 	props: ['user', 'click'], | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			faSnowflake, faMicrophoneSlash | 			faSnowflake, faMicrophoneSlash | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ | |||||||
| 			</ui-input> | 			</ui-input> | ||||||
| 			<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button> | 			<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button> | ||||||
|  |  | ||||||
| 			<div class="user" v-if="user"> | 			<div ref="user" class="user" v-if="user" :key="user.id"> | ||||||
| 				<x-user :user="user"/> | 				<x-user :user="user"/> | ||||||
| 				<div class="actions"> | 				<div class="actions"> | ||||||
| 					<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button> | 					<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button> | ||||||
| @@ -54,8 +54,16 @@ | |||||||
| 					<option value="remote">{{ $t('users.origin.remote') }}</option> | 					<option value="remote">{{ $t('users.origin.remote') }}</option> | ||||||
| 				</ui-select> | 				</ui-select> | ||||||
| 			</ui-horizon-group> | 			</ui-horizon-group> | ||||||
|  | 			<ui-horizon-group searchboxes> | ||||||
|  | 				<ui-input v-model="searchUsername" type="text" spellcheck="false" @input="fetchUsers(true)"> | ||||||
|  | 					<span>{{ $t('username') }}</span> | ||||||
|  | 				</ui-input> | ||||||
|  | 				<ui-input v-model="searchHost" type="text" spellcheck="false" @input="fetchUsers(true)" :readonly="origin === 'local'"> | ||||||
|  | 					<span>{{ $t('host') }}</span> | ||||||
|  | 				</ui-input> | ||||||
|  | 			</ui-horizon-group> | ||||||
| 			<sequential-entrance animation="entranceFromTop" delay="25"> | 			<sequential-entrance animation="entranceFromTop" delay="25"> | ||||||
| 				<x-user v-for="user in users" :user='user' :key="user.id"/> | 				<x-user v-for="user in users" :key="user.id" :user='user' :click="showUserOnClick"/> | ||||||
| 			</sequential-entrance> | 			</sequential-entrance> | ||||||
| 			<ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button> | 			<ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button> | ||||||
| 		</section> | 		</section> | ||||||
| @@ -85,6 +93,8 @@ export default Vue.extend({ | |||||||
| 			sort: '+createdAt', | 			sort: '+createdAt', | ||||||
| 			state: 'all', | 			state: 'all', | ||||||
| 			origin: 'local', | 			origin: 'local', | ||||||
|  | 			searchUsername: '', | ||||||
|  | 			searchHost: '', | ||||||
| 			limit: 10, | 			limit: 10, | ||||||
| 			offset: 0, | 			offset: 0, | ||||||
| 			users: [], | 			users: [], | ||||||
| @@ -107,6 +117,7 @@ export default Vue.extend({ | |||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		origin() { | 		origin() { | ||||||
|  | 			if (this.origin === 'local') this.searchHost = ''; | ||||||
| 			this.users = []; | 			this.users = []; | ||||||
| 			this.offset = 0; | 			this.offset = 0; | ||||||
| 			this.fetchUsers(); | 			this.fetchUsers(); | ||||||
| @@ -157,6 +168,15 @@ export default Vue.extend({ | |||||||
| 			this.target = ''; | 			this.target = ''; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
|  | 		async showUserOnClick(userId: string) { | ||||||
|  | 			this.$root.api('admin/show-user', { userId: userId }).then(info => { | ||||||
|  | 				this.user = info; | ||||||
|  | 				this.$nextTick(() => { | ||||||
|  | 					this.$refs.user.scrollIntoView(); | ||||||
|  | 				}); | ||||||
|  | 			}); | ||||||
|  | 		}, | ||||||
|  |  | ||||||
| 		/** 処理対象ユーザーの情報を更新する */ | 		/** 処理対象ユーザーの情報を更新する */ | ||||||
| 		async refreshUser() { | 		async refreshUser() { | ||||||
| 			this.$root.api('admin/show-user', { userId: this.user.id }).then(info => { | 			this.$root.api('admin/show-user', { userId: this.user.id }).then(info => { | ||||||
| @@ -308,13 +328,16 @@ export default Vue.extend({ | |||||||
| 			return !confirm.canceled; | 			return !confirm.canceled; | ||||||
| 		}, | 		}, | ||||||
|  |  | ||||||
| 		fetchUsers() { | 		fetchUsers(truncate?: boolean) { | ||||||
|  | 			if (truncate) this.offset = 0; | ||||||
| 			this.$root.api('admin/show-users', { | 			this.$root.api('admin/show-users', { | ||||||
| 				state: this.state, | 				state: this.state, | ||||||
| 				origin: this.origin, | 				origin: this.origin, | ||||||
| 				sort: this.sort, | 				sort: this.sort, | ||||||
| 				offset: this.offset, | 				offset: this.offset, | ||||||
| 				limit: this.limit + 1 | 				limit: this.limit + 1, | ||||||
|  | 				username: this.searchUsername, | ||||||
|  | 				hostname: this.searchHost | ||||||
| 			}).then(users => { | 			}).then(users => { | ||||||
| 				if (users.length == this.limit + 1) { | 				if (users.length == this.limit + 1) { | ||||||
| 					users.pop(); | 					users.pop(); | ||||||
| @@ -322,7 +345,7 @@ export default Vue.extend({ | |||||||
| 				} else { | 				} else { | ||||||
| 					this.existMore = false; | 					this.existMore = false; | ||||||
| 				} | 				} | ||||||
| 				this.users = this.users.concat(users); | 				this.users = truncate ? users : this.users.concat(users); | ||||||
| 				this.offset += this.limit; | 				this.offset += this.limit; | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -49,6 +49,16 @@ export const meta = { | |||||||
| 				'remote', | 				'remote', | ||||||
| 			]), | 			]), | ||||||
| 			default: 'local' | 			default: 'local' | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		username: { | ||||||
|  | 			validator: $.optional.str, | ||||||
|  | 			default: null | ||||||
|  | 		}, | ||||||
|  |  | ||||||
|  | 		hostname: { | ||||||
|  | 			validator: $.optional.str, | ||||||
|  | 			default: null | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| @@ -70,6 +80,14 @@ export default define(meta, async (ps, me) => { | |||||||
| 		case 'remote': query.andWhere('user.host IS NOT NULL'); break; | 		case 'remote': query.andWhere('user.host IS NOT NULL'); break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if (ps.username) { | ||||||
|  | 		query.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' }); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (ps.hostname) { | ||||||
|  | 		query.andWhere('user.host like :hostname', { hostname: '%' + ps.hostname.toLowerCase() + '%' }); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	switch (ps.sort) { | 	switch (ps.sort) { | ||||||
| 		case '+follower': query.orderBy('user.followersCount', 'DESC'); break; | 		case '+follower': query.orderBy('user.followersCount', 'DESC'); break; | ||||||
| 		case '-follower': query.orderBy('user.followersCount', 'ASC'); break; | 		case '-follower': query.orderBy('user.followersCount', 'ASC'); break; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Satsuki Yanagi
					Satsuki Yanagi