diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index dccc8df126..b540546ce3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -62,6 +62,8 @@ common:
   messaging: "トーク"
   deck: "デッキ"
   explore: "みつける"
+  following: "フォロー中"
+  followers: "フォロワー"
 
   weekday-short:
     sunday: "日"
@@ -1656,10 +1658,6 @@ mobile/views/components/user-timeline.vue:
   no-notes: "このユーザーは投稿していないようです。"
   no-notes-with-media: "メディア付き投稿はありません。"
 
-mobile/views/components/users-list.vue:
-  all: "すべて"
-  known: "知り合い"
-
 mobile/views/pages/favorites.vue:
   title: "お気に入り"
 
diff --git a/src/client/app/common/views/components/user-list.vue b/src/client/app/common/views/components/user-list.vue
index 9fcb80f8ad..5d851002d2 100644
--- a/src/client/app/common/views/components/user-list.vue
+++ b/src/client/app/common/views/components/user-list.vue
@@ -12,7 +12,7 @@
 					<router-link class="name" :to="user | userPage" v-user-preview="user.id"><mk-user-name :user="user"/></router-link>
 					<p class="username">@{{ user | acct }}</p>
 				</div>
-				<div class="description" v-if="user.description">
+				<div class="description" v-if="user.description" :title="user.description">
 					<mfm :text="user.description" :author="user" :i="$store.state.i" :custom-emojis="user.emojis" :should-break="false"/>
 				</div>
 			</div>
@@ -137,5 +137,6 @@ export default Vue.extend({
 				overflow hidden
 				text-overflow ellipsis
 				opacity 0.7
+				font-size 14px
 
 </style>
diff --git a/src/client/app/common/views/pages/followers.vue b/src/client/app/common/views/pages/followers.vue
new file mode 100644
index 0000000000..94d9c9b13c
--- /dev/null
+++ b/src/client/app/common/views/pages/followers.vue
@@ -0,0 +1,30 @@
+<template>
+<div>
+	<mk-user-list :make-promise="makePromise">{{ $t('@.followers') }}</mk-user-list>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import parseAcct from '../../../../../misc/acct/parse';
+import i18n from '../../../i18n';
+
+export default Vue.extend({
+	i18n: i18n(''),
+
+	data() {
+		return {
+			makePromise: cursor => this.$root.api('users/followers', {
+				...parseAcct(this.$route.params.user),
+				limit: 30,
+				cursor: cursor ? cursor : undefined
+			}).then(x => {
+				return {
+					users: x.users,
+					cursor: x.next
+				};
+			}),
+		};
+	},
+});
+</script>
diff --git a/src/client/app/common/views/pages/following.vue b/src/client/app/common/views/pages/following.vue
new file mode 100644
index 0000000000..39739fa3da
--- /dev/null
+++ b/src/client/app/common/views/pages/following.vue
@@ -0,0 +1,27 @@
+<template>
+<div>
+	<mk-user-list :make-promise="makePromise">{{ $t('@.following') }}</mk-user-list>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import parseAcct from '../../../../../misc/acct/parse';
+
+export default Vue.extend({
+	data() {
+		return {
+			makePromise: cursor => this.$root.api('users/following', {
+				...parseAcct(this.$route.params.user),
+				limit: 30,
+				cursor: cursor ? cursor : undefined
+			}).then(x => {
+				return {
+					users: x.users,
+					cursor: x.next
+				};
+			}),
+		};
+	},
+});
+</script>
diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index 1ec7de0cc5..4c5b29d1f4 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -130,7 +130,11 @@ init(async (launch, os) => {
 		routes: [
 			os.store.getters.isSignedIn && os.store.state.device.deckMode
 				? { path: '/', name: 'index', component: MkDeck, children: [
-					{ path: '/@:user', name: 'user', component: () => import('./views/deck/deck.user-column.vue').then(m => m.default) },
+					{ path: '/@:user', name: 'user', component: () => import('./views/deck/deck.user-column.vue').then(m => m.default), children: [
+						{ path: '', name: 'user', component: () => import('./views/deck/deck.user-column.home.vue').then(m => m.default) },
+						{ path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) },
+						{ path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) },
+					]},
 					{ path: '/notes/:note', name: 'note', component: () => import('./views/deck/deck.note-column.vue').then(m => m.default) },
 					{ path: '/search', component: () => import('./views/deck/deck.search-column.vue').then(m => m.default) },
 					{ path: '/tags/:tag', name: 'tag', component: () => import('./views/deck/deck.hashtag-column.vue').then(m => m.default) },
@@ -140,13 +144,17 @@ init(async (launch, os) => {
 				]}
 				: { path: '/', component: MkHome, children: [
 					{ path: '', name: 'index', component: MkHomeTimeline },
-					{ path: '/@:user', name: 'user', component: () => import('./views/home/user/user.vue').then(m => m.default) },
+					{ path: '/@:user', component: () => import('./views/home/user/index.vue').then(m => m.default), children: [
+						{ path: '', name: 'user', component: () => import('./views/home/user/user.home.vue').then(m => m.default) },
+						{ path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) },
+						{ path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) },
+					]},
 					{ path: '/notes/:note', name: 'note', component: () => import('./views/home/note.vue').then(m => m.default) },
 					{ path: '/search', component: () => import('./views/home/search.vue').then(m => m.default) },
 					{ path: '/tags/:tag', name: 'tag', component: () => import('./views/home/tag.vue').then(m => m.default) },
 					{ path: '/featured', component: () => import('./views/home/featured.vue').then(m => m.default) },
 					{ path: '/explore', component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
-					{ path: '/i/favorites', component: () => import('./views/home/favorites.vue').then(m => m.default) }
+					{ path: '/i/favorites', component: () => import('./views/home/favorites.vue').then(m => m.default) },
 				]},
 			{ path: '/i/messaging/:user', component: MkMessagingRoom },
 			{ path: '/i/drive', component: MkDrive },
@@ -155,8 +163,6 @@ init(async (launch, os) => {
 			{ path: '/selectdrive', component: MkSelectDrive },
 			{ path: '/share', component: MkShare },
 			{ path: '/games/reversi/:game?', component: MkReversi },
-			{ path: '/@:user/following', name: 'userFollowing', component: MkUserFollowingOrFollowers },
-			{ path: '/@:user/followers', name: 'userFollowers', component: MkUserFollowingOrFollowers },
 			{ path: '/authorize-follow', component: MkFollow },
 			{ path: '/deck', redirect: '/' },
 			{ path: '*', component: MkNotFound }
diff --git a/src/client/app/desktop/views/deck/deck.user-column.home.vue b/src/client/app/desktop/views/deck/deck.user-column.home.vue
new file mode 100644
index 0000000000..966c5bdb1b
--- /dev/null
+++ b/src/client/app/desktop/views/deck/deck.user-column.home.vue
@@ -0,0 +1,244 @@
+<template>
+<div>
+	<ui-container v-if="user.pinnedNotes && user.pinnedNotes.length > 0" :body-togglable="true">
+		<span slot="header"><fa icon="thumbtack"/> {{ $t('pinned-notes') }}</span>
+		<div>
+			<x-note v-for="n in user.pinnedNotes" :key="n.id" :note="n" :mini="true"/>
+		</div>
+	</ui-container>
+	<ui-container v-if="images.length > 0" :body-togglable="true">
+		<span slot="header"><fa :icon="['far', 'images']"/> {{ $t('images') }}</span>
+		<div class="sainvnaq">
+			<router-link v-for="image in images"
+				:style="`background-image: url(${image.thumbnailUrl})`"
+				:key="`${image.id}:${image._note.id}`"
+				:to="image._note | notePage"
+				:title="`${image.name}\n${(new Date(image.createdAt)).toLocaleString()}`"
+			></router-link>
+		</div>
+	</ui-container>
+	<ui-container :body-togglable="true">
+		<span slot="header"><fa :icon="['far', 'chart-bar']"/> {{ $t('activity') }}</span>
+		<div>
+			<div ref="chart"></div>
+		</div>
+	</ui-container>
+	<ui-container>
+		<span slot="header"><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</span>
+		<div>
+			<x-notes ref="timeline" :more="existMore ? fetchMoreNotes : null"/>
+		</div>
+	</ui-container>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../../i18n';
+import parseAcct from '../../../../../misc/acct/parse';
+import XNotes from './deck.notes.vue';
+import XNote from '../components/note.vue';
+import { concat } from '../../../../../prelude/array';
+import ApexCharts from 'apexcharts';
+
+const fetchLimit = 10;
+
+export default Vue.extend({
+	i18n: i18n('deck/deck.user-column.vue'),
+	components: {
+		XNotes,
+		XNote
+	},
+
+	props: {
+		user: {
+			type: Object,
+			required: true
+		}
+	},
+
+	data() {
+		return {
+			existMore: false,
+			moreFetching: false,
+			withFiles: false,
+			images: [],
+		};
+	},
+
+	created() {
+		this.fetch();
+	},
+
+	methods: {
+		fetch() {
+			this.$nextTick(() => {
+				(this.$refs.timeline as any).init(() => this.initTl());
+			});
+
+			const image = [
+				'image/jpeg',
+				'image/png',
+				'image/gif'
+			];
+
+			this.$root.api('users/notes', {
+				userId: this.user.id,
+				fileType: image,
+				excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
+				limit: 9,
+				untilDate: new Date().getTime() + 1000 * 86400 * 365
+			}).then(notes => {
+				for (const note of notes) {
+					for (const file of note.files) {
+						file._note = note;
+					}
+				}
+				const files = concat(notes.map((n: any): any[] => n.files));
+				this.images = files.filter(f => image.includes(f.type)).slice(0, 9);
+			});
+
+			this.$root.api('charts/user/notes', {
+				userId: this.user.id,
+				span: 'day',
+				limit: 21
+			}).then(stats => {
+				const normal = [];
+				const reply = [];
+				const renote = [];
+
+				const now = new Date();
+				const y = now.getFullYear();
+				const m = now.getMonth();
+				const d = now.getDate();
+
+				for (let i = 0; i < 21; i++) {
+					const x = new Date(y, m, d - i);
+					normal.push([
+						x,
+						stats.diffs.normal[i]
+					]);
+					reply.push([
+						x,
+						stats.diffs.reply[i]
+					]);
+					renote.push([
+						x,
+						stats.diffs.renote[i]
+					]);
+				}
+
+				const chart = new ApexCharts(this.$refs.chart, {
+					chart: {
+						type: 'bar',
+						stacked: true,
+						height: 100,
+						sparkline: {
+							enabled: true
+						},
+					},
+					plotOptions: {
+						bar: {
+							columnWidth: '90%'
+						}
+					},
+					grid: {
+						clipMarkers: false,
+						padding: {
+							top: 16,
+							right: 16,
+							bottom: 16,
+							left: 16
+						}
+					},
+					tooltip: {
+						shared: true,
+						intersect: false
+					},
+					series: [{
+						name: 'Normal',
+						data: normal
+					}, {
+						name: 'Reply',
+						data: reply
+					}, {
+						name: 'Renote',
+						data: renote
+					}],
+					xaxis: {
+						type: 'datetime',
+						crosshairs: {
+							width: 1,
+							opacity: 1
+						}
+					}
+				});
+
+				chart.render();
+			});
+		},
+
+		initTl() {
+			return new Promise((res, rej) => {
+				this.$root.api('users/notes', {
+					userId: this.user.id,
+					limit: fetchLimit + 1,
+					untilDate: new Date().getTime() + 1000 * 86400 * 365,
+					withFiles: this.withFiles,
+					includeMyRenotes: this.$store.state.settings.showMyRenotes,
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+				}).then(notes => {
+					if (notes.length == fetchLimit + 1) {
+						notes.pop();
+						this.existMore = true;
+					}
+					res(notes);
+				}, rej);
+			});
+		},
+
+		fetchMoreNotes() {
+			this.moreFetching = true;
+
+			const promise = this.$root.api('users/notes', {
+				userId: this.user.id,
+				limit: fetchLimit + 1,
+				untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
+				withFiles: this.withFiles,
+				includeMyRenotes: this.$store.state.settings.showMyRenotes,
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
+			});
+
+			promise.then(notes => {
+				if (notes.length == fetchLimit + 1) {
+					notes.pop();
+				} else {
+					this.existMore = false;
+				}
+				for (const n of notes) (this.$refs.timeline as any).append(n);
+				this.moreFetching = false;
+			});
+
+			return promise;
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+.sainvnaq
+	display grid
+	grid-template-columns 1fr 1fr 1fr
+	gap 8px
+	padding 16px
+
+	> *
+		height 70px
+		background-position center center
+		background-size cover
+		background-clip content-box
+		border-radius 4px
+
+</style>
diff --git a/src/client/app/desktop/views/deck/deck.user-column.vue b/src/client/app/desktop/views/deck/deck.user-column.vue
index 16a7aa5b35..d6618c5716 100644
--- a/src/client/app/desktop/views/deck/deck.user-column.vue
+++ b/src/client/app/desktop/views/deck/deck.user-column.vue
@@ -39,8 +39,10 @@
 			</div>
 			<div class="counts">
 				<div>
-					<b>{{ user.notesCount | number }}</b>
-					<span>{{ $t('posts') }}</span>
+					<router-link :to="user | userPage()">
+						<b>{{ user.notesCount | number }}</b>
+						<span>{{ $t('posts') }}</span>
+					</router-link>
 				</div>
 				<div>
 					<router-link :to="user | userPage('following')">
@@ -56,35 +58,7 @@
 				</div>
 			</div>
 		</div>
-		<ui-container v-if="user.pinnedNotes && user.pinnedNotes.length > 0" :body-togglable="true">
-			<span slot="header"><fa icon="thumbtack"/> {{ $t('pinned-notes') }}</span>
-			<div>
-				<x-note v-for="n in user.pinnedNotes" :key="n.id" :note="n" :mini="true"/>
-			</div>
-		</ui-container>
-		<ui-container v-if="images.length > 0" :body-togglable="true">
-			<span slot="header"><fa :icon="['far', 'images']"/> {{ $t('images') }}</span>
-			<div class="sainvnaq">
-				<router-link v-for="image in images"
-					:style="`background-image: url(${image.thumbnailUrl})`"
-					:key="`${image.id}:${image._note.id}`"
-					:to="image._note | notePage"
-					:title="`${image.name}\n${(new Date(image.createdAt)).toLocaleString()}`"
-				></router-link>
-			</div>
-		</ui-container>
-		<ui-container :body-togglable="true">
-			<span slot="header"><fa :icon="['far', 'chart-bar']"/> {{ $t('activity') }}</span>
-			<div>
-				<div ref="chart"></div>
-			</div>
-		</ui-container>
-		<ui-container>
-			<span slot="header"><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</span>
-			<div>
-				<x-notes ref="timeline" :more="existMore ? fetchMoreNotes : null"/>
-			</div>
-		</ui-container>
+		<router-view :user="user"></router-view>
 	</div>
 </x-column>
 </template>
@@ -94,30 +68,18 @@ import Vue from 'vue';
 import i18n from '../../../i18n';
 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 XUserMenu from '../../../common/views/components/user-menu.vue';
-import { concat } from '../../../../../prelude/array';
-import ApexCharts from 'apexcharts';
-
-const fetchLimit = 10;
 
 export default Vue.extend({
 	i18n: i18n('deck/deck.user-column.vue'),
 	components: {
 		XColumn,
-		XNotes,
-		XNote
 	},
 
 	data() {
 		return {
 			user: null,
 			fetching: true,
-			existMore: false,
-			moreFetching: false,
-			withFiles: false,
-			images: [],
 		};
 	},
 
@@ -146,160 +108,9 @@ export default Vue.extend({
 			this.$root.api('users/show', parseAcct(this.$route.params.user)).then(user => {
 				this.user = user;
 				this.fetching = false;
-
-				this.$nextTick(() => {
-					(this.$refs.timeline as any).init(() => this.initTl());
-				});
-
-				const image = [
-					'image/jpeg',
-					'image/png',
-					'image/gif'
-				];
-
-				this.$root.api('users/notes', {
-					userId: this.user.id,
-					fileType: image,
-					excludeNsfw: !this.$store.state.device.alwaysShowNsfw,
-					limit: 9,
-					untilDate: new Date().getTime() + 1000 * 86400 * 365
-				}).then(notes => {
-					for (const note of notes) {
-						for (const file of note.files) {
-							file._note = note;
-						}
-					}
-					const files = concat(notes.map((n: any): any[] => n.files));
-					this.images = files.filter(f => image.includes(f.type)).slice(0, 9);
-				});
-
-				this.$root.api('charts/user/notes', {
-					userId: this.user.id,
-					span: 'day',
-					limit: 21
-				}).then(stats => {
-					const normal = [];
-					const reply = [];
-					const renote = [];
-
-					const now = new Date();
-					const y = now.getFullYear();
-					const m = now.getMonth();
-					const d = now.getDate();
-
-					for (let i = 0; i < 21; i++) {
-						const x = new Date(y, m, d - i);
-						normal.push([
-							x,
-							stats.diffs.normal[i]
-						]);
-						reply.push([
-							x,
-							stats.diffs.reply[i]
-						]);
-						renote.push([
-							x,
-							stats.diffs.renote[i]
-						]);
-					}
-
-					const chart = new ApexCharts(this.$refs.chart, {
-						chart: {
-							type: 'bar',
-							stacked: true,
-							height: 100,
-							sparkline: {
-								enabled: true
-							},
-						},
-						plotOptions: {
-							bar: {
-								columnWidth: '90%'
-							}
-						},
-						grid: {
-							clipMarkers: false,
-							padding: {
-								top: 16,
-								right: 16,
-								bottom: 16,
-								left: 16
-							}
-						},
-						tooltip: {
-							shared: true,
-							intersect: false
-						},
-						series: [{
-							name: 'Normal',
-							data: normal
-						}, {
-							name: 'Reply',
-							data: reply
-						}, {
-							name: 'Renote',
-							data: renote
-						}],
-						xaxis: {
-							type: 'datetime',
-							crosshairs: {
-								width: 1,
-								opacity: 1
-							}
-						}
-					});
-
-					chart.render();
-				});
 			});
 		},
 
-		initTl() {
-			return new Promise((res, rej) => {
-				this.$root.api('users/notes', {
-					userId: this.user.id,
-					limit: fetchLimit + 1,
-					untilDate: new Date().getTime() + 1000 * 86400 * 365,
-					withFiles: this.withFiles,
-					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
-					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
-				}).then(notes => {
-					if (notes.length == fetchLimit + 1) {
-						notes.pop();
-						this.existMore = true;
-					}
-					res(notes);
-				}, rej);
-			});
-		},
-
-		fetchMoreNotes() {
-			this.moreFetching = true;
-
-			const promise = this.$root.api('users/notes', {
-				userId: this.user.id,
-				limit: fetchLimit + 1,
-				untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
-				withFiles: this.withFiles,
-				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
-				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
-			});
-
-			promise.then(notes => {
-				if (notes.length == fetchLimit + 1) {
-					notes.pop();
-				} else {
-					this.existMore = false;
-				}
-				for (const n of notes) (this.$refs.timeline as any).append(n);
-				this.moreFetching = false;
-			});
-
-			return promise;
-		},
-
 		menu() {
 			this.$root.new(XUserMenu, {
 				source: this.$refs.menu,
@@ -439,34 +250,13 @@ export default Vue.extend({
 				> a
 					color var(--text)
 
-				>>> b
-					display block
-					font-size 110%
+					> b
+						display block
+						font-size 110%
 
-				>>> span
-					display block
-					font-size 80%
-					opacity 0.7
-
-	.sainvnaq
-		display grid
-		grid-template-columns 1fr 1fr 1fr
-		gap 8px
-		padding 16px
-
-		> *
-			height 70px
-			background-position center center
-			background-size cover
-			background-clip content-box
-			border-radius 4px
-
-	> .activity
-		> div
-			background var(--face)
-
-	> .tl
-		> div
-			background var(--face)
+					> span
+						display block
+						font-size 80%
+						opacity 0.7
 
 </style>
diff --git a/src/client/app/desktop/views/home/user/user.vue b/src/client/app/desktop/views/home/user/index.vue
similarity index 54%
rename from src/client/app/desktop/views/home/user/user.vue
rename to src/client/app/desktop/views/home/user/index.vue
index 6a827f4beb..24abeadd6a 100644
--- a/src/client/app/desktop/views/home/user/user.vue
+++ b/src/client/app/desktop/views/home/user/index.vue
@@ -1,22 +1,10 @@
 <template>
-<div class="xygkxeaeontfaokvqmiblezmhvhostak" v-if="!fetching">
+<div class="omechnps" v-if="!fetching">
 	<div class="is-suspended" v-if="user.isSuspended"><fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}</div>
 	<div class="is-remote" v-if="user.host != null"><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a></div>
 	<div class="main">
-		<x-header :user="user"/>
-		<x-integrations :user="user" v-if="user.twitter || user.github || user.discord"/>
-		<mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/>
-		<!--<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>-->
-		<div class="activity">
-			<ui-container :body-togglable="true">
-				<template slot="header"><fa icon="chart-bar"/>{{ $t('activity') }}</template>
-				<x-activity :user="user" :limit="35" style="padding: 16px;"/>
-			</ui-container>
-		</div>
-		<x-photos :user="user"/>
-		<x-friends :user="user"/>
-		<x-followers-you-know v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
-		<x-timeline class="timeline" ref="tl" :user="user"/>
+		<x-header class="header" :user="user"/>
+		<router-view :user="user"></router-view>
 	</div>
 </div>
 </template>
@@ -27,23 +15,11 @@ import i18n from '../../../../i18n';
 import parseAcct from '../../../../../../misc/acct/parse';
 import Progress from '../../../../common/scripts/loading';
 import XHeader from './user.header.vue';
-import XTimeline from './user.timeline.vue';
-import XPhotos from './user.photos.vue';
-import XFollowersYouKnow from './user.followers-you-know.vue';
-import XFriends from './user.friends.vue';
-import XIntegrations from './user.integrations.vue';
-import XActivity from '../../../../common/views/components/activity.vue';
 
 export default Vue.extend({
 	i18n: i18n(),
 	components: {
-		XHeader,
-		XTimeline,
-		XPhotos,
-		XFollowersYouKnow,
-		XFriends,
-		XIntegrations,
-		XActivity
+		XHeader
 	},
 	data() {
 		return {
@@ -76,7 +52,7 @@ export default Vue.extend({
 </script>
 
 <style lang="stylus" scoped>
-.xygkxeaeontfaokvqmiblezmhvhostak
+.omechnps
 	width 100%
 	margin 0 auto
 
@@ -100,10 +76,7 @@ export default Vue.extend({
 			font-weight bold
 
 	> .main
-		> *
+		> .header
 			margin-bottom 16px
 
-		> .timeline
-			box-shadow var(--shadow)
-
 </style>
diff --git a/src/client/app/desktop/views/home/user/user.header.vue b/src/client/app/desktop/views/home/user/user.header.vue
index 05d3674996..debfb24393 100644
--- a/src/client/app/desktop/views/home/user/user.header.vue
+++ b/src/client/app/desktop/views/home/user/user.header.vue
@@ -40,7 +40,7 @@
 			<span class="birthday" v-if="user.host === null && user.profile.birthday"><fa icon="birthday-cake"/> {{ user.profile.birthday.replace('-', $t('year')).replace('-', $t('month')) + $t('day') }} ({{ $t('years-old', { age }) }})</span>
 		</div>
 		<div class="status">
-			<span class="notes-count"><b>{{ user.notesCount | number }}</b>{{ $t('posts') }}</span>
+			<router-link :to="user | userPage()" class="notes-count"><b>{{ user.notesCount | number }}</b>{{ $t('posts') }}</router-link>
 			<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>
 		</div>
diff --git a/src/client/app/desktop/views/home/user/user.home.vue b/src/client/app/desktop/views/home/user/user.home.vue
new file mode 100644
index 0000000000..b4426ac755
--- /dev/null
+++ b/src/client/app/desktop/views/home/user/user.home.vue
@@ -0,0 +1,63 @@
+<template>
+<div class="lnctpgve">
+	<x-integrations :user="user" v-if="user.twitter || user.github || user.discord"/>
+	<mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/>
+	<!--<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>-->
+	<div class="activity">
+		<ui-container :body-togglable="true">
+			<template slot="header"><fa icon="chart-bar"/>{{ $t('activity') }}</template>
+			<x-activity :user="user" :limit="35" style="padding: 16px;"/>
+		</ui-container>
+	</div>
+	<x-photos :user="user"/>
+	<x-friends :user="user"/>
+	<x-followers-you-know v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
+	<x-timeline class="timeline" ref="tl" :user="user"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../../../../i18n';
+import parseAcct from '../../../../../../misc/acct/parse';
+import Progress from '../../../../common/scripts/loading';
+import XTimeline from './user.timeline.vue';
+import XPhotos from './user.photos.vue';
+import XFollowersYouKnow from './user.followers-you-know.vue';
+import XFriends from './user.friends.vue';
+import XIntegrations from './user.integrations.vue';
+import XActivity from '../../../../common/views/components/activity.vue';
+
+export default Vue.extend({
+	i18n: i18n(),
+	components: {
+		XTimeline,
+		XPhotos,
+		XFollowersYouKnow,
+		XFriends,
+		XIntegrations,
+		XActivity
+	},
+	props: {
+		user: {
+			type: Object,
+			required: true
+		}
+	},
+	methods: {
+		warp(date) {
+			(this.$refs.tl as any).warp(date);
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+.lnctpgve
+	> *
+		margin-bottom 16px
+
+	> .timeline
+		box-shadow var(--shadow)
+
+</style>
diff --git a/src/client/app/desktop/views/pages/user-following-or-followers.vue b/src/client/app/desktop/views/pages/user-following-or-followers.vue
deleted file mode 100644
index fd842cbcd4..0000000000
--- a/src/client/app/desktop/views/pages/user-following-or-followers.vue
+++ /dev/null
@@ -1,120 +0,0 @@
-<template>
-<mk-ui>
-	<div class="yyyocnobkvdlnyapyauyopbskldsnipz" v-if="!fetching">
-		<header>
-			<mk-avatar class="avatar" :user="user"/>
-			<i18n :path="isFollowing ? 'following' : 'followers'" tag="p">
-				<router-link :to="user | userPage" place="user">
-					<mk-user-name :user="user"/>
-				</router-link>
-			</i18n>
-		</header>
-		<div class="users">
-			<mk-user-card v-for="user in users" :user="user" :key="user.id"/>
-		</div>
-		<div class="more" v-if="next">
-			<ui-button inline @click="fetchMore">{{ $t('@.load-more') }}</ui-button>
-		</div>
-	</div>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import i18n from '../../../i18n';
-import parseAcct from '../../../../../misc/acct/parse';
-import Progress from '../../../common/scripts/loading';
-
-const limit = 16;
-
-export default Vue.extend({
-	i18n: i18n('desktop/views/pages/user-following-or-followers.vue'),
-
-	data() {
-		return {
-			fetching: true,
-			user: null,
-			users: [],
-			next: undefined
-		};
-	},
-	computed: {
-		isFollowing(): boolean {
-			return this.$route.name == 'userFollowing';
-		},
-		endpoint(): string {
-			return this.isFollowing ? 'users/following' : 'users/followers';
-		}
-	},
-	watch: {
-		$route: 'fetch'
-	},
-	created() {
-		this.fetch();
-	},
-	methods: {
-		fetch() {
-			this.fetching = true;
-			Progress.start();
-			this.$root.api('users/show', parseAcct(this.$route.params.user)).then(user => {
-				this.user = user;
-				this.$root.api(this.endpoint, {
-					userId: this.user.id,
-					iknow: false,
-					limit: limit
-				}).then(x => {
-					this.users = x.users;
-					this.next = x.next;
-					this.fetching = false;
-					Progress.done();
-				});
-			});
-		},
-
-		fetchMore() {
-			this.$root.api(this.endpoint, {
-				userId: this.user.id,
-				iknow: false,
-				limit: limit,
-				cursor: this.next
-			}).then(x => {
-				this.users = this.users.concat(x.users);
-				this.next = x.next;
-			});
-		}
-	}
-});
-</script>
-
-<style lang="stylus" scoped>
-.yyyocnobkvdlnyapyauyopbskldsnipz
-	width 100%
-	max-width 1280px
-	padding 32px
-	margin 0 auto
-
-	> header
-		display flex
-		align-items center
-		margin 0 0 16px 0
-		color var(--text)
-
-		> .avatar
-			width 64px
-			height 64px
-
-		> p
-			margin 0 16px
-			font-size 24px
-			font-weight bold
-
-	> .users
-		display grid
-		grid-template-columns 1fr 1fr 1fr 1fr
-		gap 16px
-
-	> .more
-		margin 32px 16px 16px 16px
-		text-align center
-
-</style>
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index 1feff3d5eb..ad37ba70ab 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -21,8 +21,6 @@ import MkMessagingRoom from './views/pages/messaging-room.vue';
 import MkReceivedFollowRequests from './views/pages/received-follow-requests.vue';
 import MkNote from './views/pages/note.vue';
 import MkSearch from './views/pages/search.vue';
-import MkFollowers from './views/pages/followers.vue';
-import MkFollowing from './views/pages/following.vue';
 import MkFavorites from './views/pages/favorites.vue';
 import MkUserLists from './views/pages/user-lists.vue';
 import MkUserList from './views/pages/user-list.vue';
@@ -137,9 +135,11 @@ init((launch) => {
 			{ path: '/explore', name: 'explore', component: () => import('./views/pages/explore.vue').then(m => m.default) },
 			{ path: '/share', component: MkShare },
 			{ path: '/games/reversi/:game?', name: 'reversi', component: MkReversi },
-			{ path: '/@:user', component: () => import('./views/pages/user.vue').then(m => m.default) },
-			{ path: '/@:user/followers', component: MkFollowers },
-			{ path: '/@:user/following', component: MkFollowing },
+			{ path: '/@:user', component: () => import('./views/pages/user/index.vue').then(m => m.default), children: [
+				{ path: '', name: 'user', component: () => import('./views/pages/user/home.vue').then(m => m.default) },
+				{ path: 'following', component: () => import('../common/views/pages/following.vue').then(m => m.default) },
+				{ path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) },
+			]},
 			{ path: '/notes/:note', component: MkNote },
 			{ path: '/authorize-follow', component: MkFollow },
 			{ path: '*', component: MkNotFound }
diff --git a/src/client/app/mobile/views/components/index.ts b/src/client/app/mobile/views/components/index.ts
index 94bc8d23fd..864098640b 100644
--- a/src/client/app/mobile/views/components/index.ts
+++ b/src/client/app/mobile/views/components/index.ts
@@ -13,7 +13,6 @@ import friendsMaker from './friends-maker.vue';
 import notification from './notification.vue';
 import notifications from './notifications.vue';
 import notificationPreview from './notification-preview.vue';
-import usersList from './users-list.vue';
 import userPreview from './user-preview.vue';
 import userTimeline from './user-timeline.vue';
 import userListTimeline from './user-list-timeline.vue';
@@ -33,7 +32,6 @@ Vue.component('mk-friends-maker', friendsMaker);
 Vue.component('mk-notification', notification);
 Vue.component('mk-notifications', notifications);
 Vue.component('mk-notification-preview', notificationPreview);
-Vue.component('mk-users-list', usersList);
 Vue.component('mk-user-preview', userPreview);
 Vue.component('mk-user-timeline', userTimeline);
 Vue.component('mk-user-list-timeline', userListTimeline);
diff --git a/src/client/app/mobile/views/components/users-list.vue b/src/client/app/mobile/views/components/users-list.vue
deleted file mode 100644
index 0c5c934dcf..0000000000
--- a/src/client/app/mobile/views/components/users-list.vue
+++ /dev/null
@@ -1,135 +0,0 @@
-<template>
-<div class="mk-users-list">
-	<nav>
-		<span :data-active="mode == 'all'" @click="mode = 'all'">{{ $t('all') }}<span>{{ count }}</span></span>
-		<span v-if="$store.getters.isSignedIn && youKnowCount" :data-active="mode == 'iknow'" @click="mode = 'iknow'">{{ $t('known') }}<span>{{ youKnowCount }}</span></span>
-	</nav>
-	<div class="users" v-if="!fetching && users.length != 0">
-		<mk-user-preview v-for="u in users" :user="u" :key="u.id"/>
-	</div>
-	<ui-button class="more" v-if="!fetching && next != null" @click="more" :disabled="moreFetching">
-		<span v-if="!moreFetching">{{ $t('@.load-more') }}</span>
-		<span v-if="moreFetching">{{ $t('@.loading') }}<mk-ellipsis/></span>
-	</ui-button>
-	<p class="no" v-if="!fetching && users.length == 0">
-		<slot></slot>
-	</p>
-	<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import i18n from '../../../i18n';
-export default Vue.extend({
-	i18n: i18n('mobile/views/components/users-list.vue'),
-	props: ['fetch', 'count', 'youKnowCount'],
-	data() {
-		return {
-			limit: 30,
-			mode: 'all',
-			fetching: true,
-			moreFetching: false,
-			users: [],
-			next: null
-		};
-	},
-	watch: {
-		mode() {
-			this._fetch();
-		}
-	},
-	mounted() {
-		this._fetch(() => {
-			this.$emit('loaded');
-		});
-	},
-	methods: {
-		_fetch(cb?) {
-			this.fetching = true;
-			this.fetch(this.mode == 'iknow', this.limit, null, obj => {
-				this.users = obj.users;
-				this.next = obj.next;
-				this.fetching = false;
-				if (cb) cb();
-			});
-		},
-		more() {
-			this.moreFetching = true;
-			this.fetch(this.mode == 'iknow', this.limit, this.next, obj => {
-				this.moreFetching = false;
-				this.users = this.users.concat(obj.users);
-				this.next = obj.next;
-			});
-		}
-	}
-});
-</script>
-
-<style lang="stylus" scoped>
-
-
-.mk-users-list
-
-	> nav
-		display flex
-		justify-content center
-		margin 0 auto
-		max-width 600px
-		border-bottom solid 1px rgba(#000, 0.2)
-
-		> span
-			display block
-			flex 1 1
-			text-align center
-			line-height 52px
-			font-size 14px
-			color #657786
-			border-bottom solid 2px transparent
-
-			&[data-active]
-				font-weight bold
-				color var(--primary)
-				border-color var(--primary)
-
-			> span
-				display inline-block
-				margin-left 4px
-				padding 2px 5px
-				font-size 12px
-				line-height 1
-				color #fff
-				background rgba(#000, 0.3)
-				border-radius 20px
-
-	> .users
-		margin 8px auto
-		max-width 500px
-		width calc(100% - 16px)
-		background #fff
-		border-radius 8px
-		box-shadow 0 0 0 1px rgba(#000, 0.2)
-
-		@media (min-width 500px)
-			margin 16px auto
-			width calc(100% - 32px)
-
-		> *
-			border-bottom solid 1px rgba(#000, 0.05)
-
-	> .no
-		margin 0
-		padding 16px
-		text-align center
-		color var(--text)
-
-	> .fetching
-		margin 0
-		padding 16px
-		text-align center
-		color var(--text)
-
-		> [data-icon]
-			margin-right 4px
-
-</style>
diff --git a/src/client/app/mobile/views/pages/followers.vue b/src/client/app/mobile/views/pages/followers.vue
deleted file mode 100644
index f5ac8ef195..0000000000
--- a/src/client/app/mobile/views/pages/followers.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-<template>
-<mk-ui>
-	<template slot="header" v-if="!fetching">
-		<img :src="user.avatarUrl" alt="">
-		<mfm :text="$t('followers-of', { name })" :should-break="false" :plain-text="true" :custom-emojis="user.emojis"/>
-	</template>
-	<mk-users-list
-		v-if="!fetching"
-		:fetch="fetchUsers"
-		:count="user.followersCount"
-		:you-know-count="user.followersYouKnowCount"
-		@loaded="onLoaded"
-	>
-		%i18n:@no-users%
-	</mk-users-list>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import i18n from '../../../i18n';
-import Progress from '../../../common/scripts/loading';
-import parseAcct from '../../../../../misc/acct/parse';
-import getUserName from '../../../../../misc/get-user-name';
-
-export default Vue.extend({
-	i18n: i18n('mobile/views/pages/followers.vue'),
-	data() {
-		return {
-			fetching: true,
-			user: null
-		};
-	},
-	computed: {
-		name() {
-			return getUserName(this.user);
-		}
-	},
-	watch: {
-		$route: 'fetch'
-	},
-	created() {
-		this.fetch();
-	},
-	methods: {
-		fetch() {
-			Progress.start();
-			this.fetching = true;
-
-			this.$root.api('users/show', parseAcct(this.$route.params.user)).then(user => {
-				this.user = user;
-				this.fetching = false;
-
-				document.title = `${this.$t('followers-of').replace('{}', this.name)} | ${this.$root.instanceName}`;
-			});
-		},
-		onLoaded() {
-			Progress.done();
-		},
-		fetchUsers(iknow, limit, cursor, cb) {
-			this.$root.api('users/followers', {
-				userId: this.user.id,
-				iknow: iknow,
-				limit: limit,
-				cursor: cursor ? cursor : undefined
-			}).then(cb);
-		}
-	}
-});
-</script>
diff --git a/src/client/app/mobile/views/pages/following.vue b/src/client/app/mobile/views/pages/following.vue
deleted file mode 100644
index d603532498..0000000000
--- a/src/client/app/mobile/views/pages/following.vue
+++ /dev/null
@@ -1,69 +0,0 @@
-<template>
-<mk-ui>
-	<template slot="header" v-if="!fetching">
-		<img :src="user.avatarUrl" alt="">
-		<mfm :text="$t('following-of', { name })" :should-break="false" :plain-text="true" :custom-emojis="user.emojis"/>
-	</template>
-	<mk-users-list
-		v-if="!fetching"
-		:fetch="fetchUsers"
-		:count="user.followingCount"
-		:you-know-count="user.followingYouKnowCount"
-		@loaded="onLoaded"
-	>
-		%i18n:@no-users%
-	</mk-users-list>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import i18n from '../../../i18n';
-import Progress from '../../../common/scripts/loading';
-import parseAcct from '../../../../../misc/acct/parse';
-
-export default Vue.extend({
-	i18n: i18n('mobile/views/pages/following.vue'),
-	data() {
-		return {
-			fetching: true,
-			user: null
-		};
-	},
-	computed: {
-		name(): string {
-			return Vue.filter('userName')(this.user);
-		}
-	},
-	watch: {
-		$route: 'fetch'
-	},
-	created() {
-		this.fetch();
-	},
-	methods: {
-		fetch() {
-			Progress.start();
-			this.fetching = true;
-
-			this.$root.api('users/show', parseAcct(this.$route.params.user)).then(user => {
-				this.user = user;
-				this.fetching = false;
-
-				document.title = `${this.$t('followers-of').replace('{}', this.name)} | ${this.$root.instanceName}`;
-			});
-		},
-		onLoaded() {
-			Progress.done();
-		},
-		fetchUsers(iknow, limit, cursor, cb) {
-			this.$root.api('users/following', {
-				userId: this.user.id,
-				iknow: iknow,
-				limit: limit,
-				cursor: cursor ? cursor : undefined
-			}).then(cb);
-		}
-	}
-});
-</script>
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user/index.vue
similarity index 88%
rename from src/client/app/mobile/views/pages/user.vue
rename to src/client/app/mobile/views/pages/user/index.vue
index 5d15a9718a..48b65624ef 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user/index.vue
@@ -43,22 +43,22 @@
 					</p>
 				</div>
 				<div class="status">
-					<a>
+					<router-link :to="user | userPage()">
 						<b>{{ user.notesCount | number }}</b>
 						<i>{{ $t('notes') }}</i>
-					</a>
-					<a :href="user | userPage('following')">
+					</router-link>
+					<router-link :to="user | userPage('following')">
 						<b>{{ user.followingCount | number }}</b>
 						<i>{{ $t('following') }}</i>
-					</a>
-					<a :href="user | userPage('followers')">
+					</router-link>
+					<router-link :to="user | userPage('followers')">
 						<b>{{ user.followersCount | number }}</b>
 						<i>{{ $t('followers') }}</i>
-					</a>
+					</router-link>
 				</div>
 			</div>
 		</header>
-		<nav>
+		<nav v-if="$route.name == 'user'">
 			<div class="nav-container">
 				<a :data-active="page == 'home'" @click="page = 'home'"><fa icon="home"/> {{ $t('overview') }}</a>
 				<a :data-active="page == 'notes'" @click="page = 'notes'"><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</a>
@@ -66,9 +66,12 @@
 			</div>
 		</nav>
 		<div class="body">
-			<x-home v-if="page == 'home'" :user="user"/>
-			<mk-user-timeline v-if="page == 'notes'" :user="user" key="tl"/>
-			<mk-user-timeline v-if="page == 'media'" :user="user" :with-media="true" key="media"/>
+			<template v-if="$route.name == 'user'">
+				<x-home v-if="page == 'home'" :user="user"/>
+				<mk-user-timeline v-if="page == 'notes'" :user="user" key="tl"/>
+				<mk-user-timeline v-if="page == 'media'" :user="user" :with-media="true" key="media"/>
+			</template>
+			<router-view :user="user"></router-view>
 		</div>
 	</main>
 </mk-ui>
@@ -76,13 +79,13 @@
 
 <script lang="ts">
 import Vue from 'vue';
-import i18n from '../../../i18n';
+import i18n from '../../../../i18n';
 import * as age from 's-age';
-import parseAcct from '../../../../../misc/acct/parse';
-import Progress from '../../../common/scripts/loading';
-import XUserMenu from '../../../common/views/components/user-menu.vue';
-import XHome from './user/home.vue';
-import { getStaticImageUrl } from '../../../common/scripts/get-static-image-url';
+import parseAcct from '../../../../../../misc/acct/parse';
+import Progress from '../../../../common/scripts/loading';
+import XUserMenu from '../../../../common/views/components/user-menu.vue';
+import XHome from './home.vue';
+import { getStaticImageUrl } from '../../../../common/scripts/get-static-image-url';
 
 export default Vue.extend({
 	i18n: i18n('mobile/views/pages/user.vue'),
@@ -93,7 +96,7 @@ export default Vue.extend({
 		return {
 			fetching: true,
 			user: null,
-			page: 'home'
+			page: this.$route.name == 'user' ? 'home' : null
 		};
 	},
 	computed: {
diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts
index 2a39da4064..2f7f1af6a5 100644
--- a/src/server/api/endpoints/users/followers.ts
+++ b/src/server/api/endpoints/users/followers.ts
@@ -16,7 +16,7 @@ export const meta = {
 
 	params: {
 		userId: {
-			validator: $.type(ID),
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '対象のユーザーのID',
@@ -24,6 +24,14 @@ export const meta = {
 			}
 		},
 
+		username: {
+			validator: $.optional.str
+		},
+
+		host: {
+			validator: $.optional.nullable.str
+		},
+
 		limit: {
 			validator: $.optional.num.range(1, 100),
 			default: 10
@@ -43,14 +51,11 @@ export const meta = {
 };
 
 export default define(meta, (ps, me) => new Promise(async (res, rej) => {
-	// Lookup user
-	const user = await User.findOne({
-		_id: ps.userId
-	}, {
-		fields: {
-			_id: true
-		}
-	});
+	const q: any = ps.userId != null
+		? { _id: ps.userId }
+		: { usernameLower: ps.username.toLowerCase(), host: ps.host };
+
+	const user = await User.findOne(q);
 
 	if (user === null) {
 		return rej('user not found');
diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts
index 4ccc13f633..1485a63f24 100644
--- a/src/server/api/endpoints/users/following.ts
+++ b/src/server/api/endpoints/users/following.ts
@@ -16,7 +16,7 @@ export const meta = {
 
 	params: {
 		userId: {
-			validator: $.type(ID),
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '対象のユーザーのID',
@@ -24,6 +24,14 @@ export const meta = {
 			}
 		},
 
+		username: {
+			validator: $.optional.str
+		},
+
+		host: {
+			validator: $.optional.nullable.str
+		},
+
 		limit: {
 			validator: $.optional.num.range(1, 100),
 			default: 10
@@ -43,14 +51,11 @@ export const meta = {
 };
 
 export default define(meta, (ps, me) => new Promise(async (res, rej) => {
-	// Lookup user
-	const user = await User.findOne({
-		_id: ps.userId
-	}, {
-		fields: {
-			_id: true
-		}
-	});
+	const q: any = ps.userId != null
+		? { _id: ps.userId }
+		: { usernameLower: ps.username.toLowerCase(), host: ps.host };
+
+	const user = await User.findOne(q);
 
 	if (user === null) {
 		return rej('user not found');