diff --git a/locales/index.js b/locales/index.js
index df8f11842..ee260f269 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -14,6 +14,7 @@ const merge = (...args) => args.reduce((a, c) => ({
 }), {});
 
 const languages = [
+	'ar-SA',
 	//'cs-CZ',
 	//'da-DK',
 	'de-DE',
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3c7dc6640..38827ea35 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -509,6 +509,7 @@ addedRelays: "追加済みのリレー"
 serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。"
 deletedNote: "削除された投稿"
 invisibleNote: "非公開の投稿"
+enableInfiniteScroll: "自動でもっと見る"
 
 _theme:
   explore: "テーマを探す"
diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue
index 65eb1cb81..3e7b7d04a 100644
--- a/src/client/components/drive.vue
+++ b/src/client/components/drive.vue
@@ -19,17 +19,17 @@
 		@drop.prevent.stop="onDrop"
 	>
 		<div class="contents" ref="contents">
-			<div class="folders" ref="foldersContainer" v-if="folders.length > 0">
+			<div class="folders" ref="foldersContainer" v-show="folders.length > 0">
 				<x-folder v-for="f in folders" :key="f.id" class="folder" :folder="f" :select-mode="select === 'folder'" :is-selected="selectedFolders.some(x => x.id === f.id)" @chosen="chooseFolder"/>
 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
 				<div class="padding" v-for="(n, i) in 16" :key="i"></div>
-				<mk-button v-if="moreFolders">{{ $t('loadMore') }}</mk-button>
+				<mk-button ref="moreFolders" v-if="moreFolders">{{ $t('loadMore') }}</mk-button>
 			</div>
-			<div class="files" ref="filesContainer" v-if="files.length > 0">
+			<div class="files" ref="filesContainer" v-show="files.length > 0">
 				<x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="select === 'file'" :is-selected="selectedFiles.some(x => x.id === file.id)" @chosen="chooseFile"/>
 				<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
 				<div class="padding" v-for="(n, i) in 16" :key="i"></div>
-				<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('loadMore') }}</mk-button>
+				<mk-button ref="loadMoreFiles" @click="fetchMoreFiles" v-show="moreFiles">{{ $t('loadMore') }}</mk-button>
 			</div>
 			<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
 				<p v-if="draghover">{{ $t('empty-draghover') }}</p>
@@ -116,6 +116,13 @@ export default Vue.extend({
 
 			fetching: true,
 
+			ilFilesObserver: new IntersectionObserver(
+				(entries) => entries.some((entry) => entry.isIntersecting)
+				&& !this.fetching && this.moreFiles &&
+					this.fetchMoreFiles()
+			),
+			moreFilesElement: null as Element,
+
 			faAngleRight
 		};
 	},
@@ -127,6 +134,12 @@ export default Vue.extend({
 	},
 
 	mounted() {
+		if (this.$store.state.device.enableInfiniteScroll && this.$refs.loadMoreFiles) {
+			this.$nextTick(() => {
+				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el)
+			});
+		}
+
 		this.connection = this.$root.stream.useSharedConnection('drive');
 
 		this.connection.on('fileCreated', this.onStreamDriveFileCreated);
@@ -143,8 +156,17 @@ export default Vue.extend({
 		}
 	},
 
+	activated() {
+		if (this.$store.state.device.enableInfiniteScroll) {
+			this.$nextTick(() => {
+				this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el)
+			});
+		}
+	},
+
 	beforeDestroy() {
 		this.connection.dispose();
+		this.ilFilesObserver.disconnect();
 	},
 
 	methods: {
diff --git a/src/client/components/mfm.ts b/src/client/components/mfm.ts
index bfef80f23..122368f74 100644
--- a/src/client/components/mfm.ts
+++ b/src/client/components/mfm.ts
@@ -1,5 +1,5 @@
 import Vue, { VNode } from 'vue';
-import { MfmForest } from '../../mfm/types';
+import { MfmForest } from '../../mfm/prelude';
 import { parse, parsePlain } from '../../mfm/parse';
 import MkUrl from './url.vue';
 import MkLink from './link.vue';
@@ -53,11 +53,11 @@ export default Vue.component('misskey-flavored-markdown', {
 
 					if (!this.plain) {
 						const x = text.split('\n')
-							.map(t => t == '' ? [createElement('br')] : [createElement('span', t), createElement('br')]);
+							.map(t => t == '' ? [createElement('br')] : [this._v(t), createElement('br')]); // NOTE: this._vはHACK SEE: https://github.com/syuilo/misskey/pull/6399#issuecomment-632820283
 						x[x.length - 1].pop();
 						return x;
 					} else {
-						return [createElement('span', text.replace(/\n/g, ' '))];
+						return [this._v(text.replace(/\n/g, ' '))];
 					}
 				}
 
diff --git a/src/client/components/note-preview.vue b/src/client/components/note-preview.vue
index f58ab5fa5..14314889a 100644
--- a/src/client/components/note-preview.vue
+++ b/src/client/components/note-preview.vue
@@ -50,15 +50,7 @@ export default Vue.extend({
 	margin: 0;
 	padding: 0;
 	overflow: hidden;
-	font-size: 10px;
-
-	@media (min-width: 350px) {
-		font-size: 12px;
-	}
-
-	@media (min-width: 500px) {
-		font-size: 14px;
-	}
+	font-size: 0.95em;
 
 	> .avatar {
 
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index 515bc58e2..c83cbd1aa 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -7,8 +7,8 @@
 
 	<mk-error v-if="error" @retry="init()"/>
 
-	<div v-if="more && reversed" style="margin-bottom: var(--margin);">
-		<button class="_panel _button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()">
+	<div v-show="more && reversed" style="margin-bottom: var(--margin);">
+		<button class="_panel _button" ref="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
 			<template v-if="moreFetching"><mk-loading inline/></template>
 		</button>
@@ -18,8 +18,8 @@
 		<x-note :note="note" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/>
 	</x-list>
 
-	<div v-if="more && !reversed" style="margin-top: var(--margin);">
-		<button class="_panel _button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()">
+	<div v-show="more && !reversed" style="margin-top: var(--margin);">
+		<button class="_panel _button" ref="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
 			<template v-if="moreFetching"><mk-loading inline/></template>
 		</button>
diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue
index 9dcb4eb10..5c5b5fb81 100644
--- a/src/client/components/notifications.vue
+++ b/src/client/components/notifications.vue
@@ -5,7 +5,7 @@
 		<x-notification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
 	</x-list>
 
-	<button class="_panel _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
+	<button class="_panel _button" ref="loadMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
 		<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
 		<template v-if="moreFetching"><mk-loading inline/></template>
 	</button>
diff --git a/src/client/components/ui/pagination.vue b/src/client/components/ui/pagination.vue
index e888b7420..79d322bbc 100644
--- a/src/client/components/ui/pagination.vue
+++ b/src/client/components/ui/pagination.vue
@@ -4,8 +4,8 @@
 	<div class="empty" v-if="empty" key="_empty_">
 		<slot name="empty"></slot>
 	</div>
-	<div class="more" v-if="more" key="_more_">
-		<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary>
+	<div class="more" v-show="more" key="_more_">
+		<mk-button class="button" ref="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary>
 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
 			<template v-if="moreFetching"><mk-loading inline/></template>
 		</mk-button>
diff --git a/src/client/components/user-list.vue b/src/client/components/user-list.vue
index 7a9cd58a4..0204cf9d0 100644
--- a/src/client/components/user-list.vue
+++ b/src/client/components/user-list.vue
@@ -22,7 +22,7 @@
 			</div>
 			<mk-follow-button class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
 		</div>
-		<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMore()" :disabled="moreFetching">
+		<button class="more" ref="loadMore" :class="{ fetching: moreFetching }" v-show="more" :disabled="moreFetching">
 			<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }}
 		</button>
 	</div>
diff --git a/src/client/components/users-dialog.vue b/src/client/components/users-dialog.vue
index 0e0cc36c2..575d03118 100644
--- a/src/client/components/users-dialog.vue
+++ b/src/client/components/users-dialog.vue
@@ -15,8 +15,7 @@
 				</div>
 			</router-link>
 		</div>
-
-		<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
+		<button class="more _button" ref="loadMore" v-show="more" @click="fetchMore" :disabled="moreFetching">
 			<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
 			<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
 		</button>
diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue
index e97d5532a..a95305cab 100644
--- a/src/client/pages/messaging/messaging-room.vue
+++ b/src/client/pages/messaging/messaging-room.vue
@@ -16,7 +16,7 @@
 		<mk-loading v-if="fetching"/>
 		<p class="empty" v-if="!fetching && messages.length == 0"><fa :icon="faInfoCircle"/>{{ $t('noMessagesYet') }}</p>
 		<p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><fa :icon="faFlag"/>{{ $t('noMoreHistory') }}</p>
-		<button class="more _button" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
+		<button class="more _button" ref="loadMore" :class="{ fetching: fetchingMoreMessages }" v-show="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
 			<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }}
 		</button>
 		<x-list class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed>
@@ -40,7 +40,6 @@ import { faArrowCircleDown, faFlag, faUsers, faInfoCircle } from '@fortawesome/f
 import XList from '../../components/date-separated-list.vue';
 import XMessage from './messaging-room.message.vue';
 import XForm from './messaging-room.form.vue';
-import { url } from '../../config';
 import parseAcct from '../../../misc/acct/parse';
 
 export default Vue.extend({
@@ -61,6 +60,13 @@ export default Vue.extend({
 			connection: null,
 			showIndicator: false,
 			timer: null,
+			ilObserver: new IntersectionObserver(
+				(entries) => entries.some((entry) => entry.isIntersecting)
+					&& !this.fetching
+					&& !this.fetchingMoreMessages
+					&& this.existMoreMessages
+					&& this.fetchMoreMessages()
+			),
 			faArrowCircleDown, faFlag, faUsers, faInfoCircle
 		};
 	},
@@ -77,6 +83,9 @@ export default Vue.extend({
 
 	mounted() {
 		this.fetch();
+		if (this.$store.state.device.enableInfiniteScroll) {
+			this.$nextTick(() => this.ilObserver.observe(this.$refs.loadMore as Element));
+		}
 	},
 
 	beforeDestroy() {
@@ -85,6 +94,8 @@ export default Vue.extend({
 		window.removeEventListener('scroll', this.onScroll);
 
 		document.removeEventListener('visibilitychange', this.onVisibilitychange);
+
+		this.ilObserver.disconnect();
 	},
 
 	methods: {
@@ -112,8 +123,12 @@ export default Vue.extend({
 			document.addEventListener('visibilitychange', this.onVisibilitychange);
 
 			this.fetchMessages().then(() => {
-				this.fetching = false;
 				this.scrollToBottom();
+
+				// もっと見るの交差検知を発火させないためにfetchは
+				// スクロールが終わるまでfalseにしておく
+				// scrollendのようなイベントはないのでsetTimeoutで
+				setTimeout(() => this.fetching = false, 300);
 			});
 		},
 
diff --git a/src/client/pages/my-settings/index.vue b/src/client/pages/my-settings/index.vue
index c3080e0f8..3af896d78 100644
--- a/src/client/pages/my-settings/index.vue
+++ b/src/client/pages/my-settings/index.vue
@@ -96,15 +96,15 @@ export default Vue.extend({
 		},
 
 		readAllUnreadNotes() {
-			this.$root.api('i/read_all_unread_notes');
+			this.$root.api('i/read-all-unread-notes');
 		},
 
 		readAllMessagingMessages() {
-			this.$root.api('i/read_all_messaging_messages');
+			this.$root.api('i/read-all-messaging-messages');
 		},
 
 		readAllNotifications() {
-			this.$root.api('notifications/mark_all_as_read');
+			this.$root.api('notifications/mark-all-as-read');
 		},
 	}
 });
diff --git a/src/client/pages/preferences/index.vue b/src/client/pages/preferences/index.vue
index 14d22bf02..148b4a97b 100644
--- a/src/client/pages/preferences/index.vue
+++ b/src/client/pages/preferences/index.vue
@@ -67,6 +67,7 @@
 				<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
 			</mk-switch>
 			<mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch>
+			<mk-switch v-model="enableInfiniteScroll">{{ $t('enableInfiniteScroll') }}</mk-switch>
 			<mk-switch v-model="disablePagesScript">{{ $t('disablePagesScript') }}</mk-switch>
 		</div>
 		<div class="_content">
@@ -182,6 +183,11 @@ export default Vue.extend({
 			set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); }
 		},
 
+		enableInfiniteScroll: {
+			get() { return this.$store.state.device.enableInfiniteScroll; },
+			set(value) { this.$store.commit('device/setInfiniteScrollEnabling', value); }
+		},
+
 		sfxVolume: {
 			get() { return this.$store.state.device.sfxVolume; },
 			set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value: parseFloat(value, 10) }); }
diff --git a/src/client/scripts/paging.ts b/src/client/scripts/paging.ts
index 048c79775..1f302753e 100644
--- a/src/client/scripts/paging.ts
+++ b/src/client/scripts/paging.ts
@@ -15,6 +15,14 @@ export default (opts) => ({
 			more: false,
 			backed: false,
 			isBackTop: false,
+			ilObserver: new IntersectionObserver(
+				(entries) => entries.some((entry) => entry.isIntersecting)
+					&& !this.moreFetching
+					&& !this.fetching
+					&& this.fetchMore()
+				),
+			loadMoreElement: null as Element,
+			unsubscribeInfiniteScrollMutation: null as any,
 		};
 	},
 
@@ -51,6 +59,29 @@ export default (opts) => ({
 		});
 	},
 
+	mounted() {
+		this.$nextTick(() => {
+			if (this.$refs.loadMore) {
+				this.loadMoreElement = this.$refs.loadMore instanceof Element ? this.$refs.loadMore : this.$refs.loadMore.$el;
+				if (this.$store.state.device.enableInfiniteScroll) this.ilObserver.observe(this.loadMoreElement);
+				this.loadMoreElement.addEventListener('click', this.fetchMore);
+
+				this.unsubscribeInfiniteScrollMutation = this.$store.subscribe(mutation => {
+					if (mutation.type !== 'device/setInfiniteScrollEnabling') return;
+
+					if (mutation.payload) return this.ilObserver.observe(this.loadMoreElement);
+					return this.ilObserver.unobserve(this.loadMoreElement);
+				});
+			}
+		});
+	},
+
+	beforeDestroy() {
+		this.ilObserver.disconnect();
+		if (this.$refs.loadMore) this.loadMoreElement.removeEventListener('click', this.fetchMore);
+		if (this.unsubscribeInfiniteScrollMutation) this.unsubscribeInfiniteScrollMutation();
+	},
+
 	methods: {
 		updateItem(i, item) {
 			Vue.set((this as any).items, i, item);
diff --git a/src/client/store.ts b/src/client/store.ts
index c1d7d3991..3eb221b5c 100644
--- a/src/client/store.ts
+++ b/src/client/store.ts
@@ -56,6 +56,7 @@ export const defaultDeviceSettings = {
 	imageNewTab: false,
 	showFixedPostForm: false,
 	disablePagesScript: true,
+	enableInfiniteScroll: true,
 	roomGraphicsQuality: 'medium',
 	roomUseOrthographicCamera: true,
 	sfxVolume: 0.3,
@@ -333,6 +334,10 @@ export default () => new Vuex.Store({
 				setUserData(state, x: { userId: string; data: any }) {
 					state.userData[x.userId] = copy(x.data);
 				},
+
+				setInfiniteScrollEnabling(state, x: boolean) {
+					state.enableInfiniteScroll = x;
+				},
 			}
 		},
 
diff --git a/src/docs/api.ja-JP.md b/src/docs/api.ja-JP.md
index d30e2a2db..7171664f1 100644
--- a/src/docs/api.ja-JP.md
+++ b/src/docs/api.ja-JP.md
@@ -48,7 +48,7 @@ UUIDを生成する。以後これをセッションIDと呼びます。
 	* どのような権限があるかは[APIリファレンス](/api-doc)で確認できます
 
 #### Step 3
-ユーザーが連携を許可した後、`{_URL_}/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。
+ユーザーが連携を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。
 
 レスポンスに含まれるプロパティ:
 * `token` ... ユーザーのアクセストークン