From 7289d5b401d566bfb71e624b8562b504b7358af0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Thu, 9 Aug 2018 16:44:34 +0900
Subject: [PATCH 001/134] Add player

---
 .../common/views/components/url-preview.vue   | 56 ++++++-------------
 1 file changed, 16 insertions(+), 40 deletions(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 4f1e34c6ca..6100c1fbfb 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -1,12 +1,6 @@
 <template>
-<iframe v-if="youtubeId" type="text/html" height="250"
-	:src="`https://www.youtube.com/embed/${youtubeId}?origin=${misskeyUrl}`"
-	frameborder="0"/>
-<iframe v-else-if="spotifyId"
-	:src="`https://open.spotify.com/embed/track/${spotifyId}`"
-	frameborder="0" allowtransparency="true" allow="encrypted-media" />
-<iframe v-else-if="nicovideoId"
-	:src="`https://embed.nicovideo.jp/watch/${nicovideoId}?oldScript=1&referer=${misskeyUrl}&from=${position || '0'}&allowProgrammaticFullScreen=1`"
+<iframe v-if="player"
+	:src="player"
 	frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
 <div v-else-if="tweetUrl && detail" class="twitter">
 	<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
@@ -54,10 +48,7 @@ export default Vue.extend({
 			thumbnail: null,
 			icon: null,
 			sitename: null,
-			youtubeId: null,
-			spotifyId: null,
-			nicovideoId: null,
-			position: null,
+			player: null,
 			tweetUrl: null,
 			misskeyUrl
 		};
@@ -65,23 +56,7 @@ export default Vue.extend({
 	created() {
 		const url = new URL(this.url);
 
-		if (url.hostname == 'www.youtube.com') {
-			this.youtubeId = url.searchParams.get('v');
-			return;
-		} else if (url.hostname == 'youtu.be') {
-			this.youtubeId = url.pathname;
-			return;
-		} else if (url.hostname == 'open.spotify.com') {
-			this.spotifyId = url.pathname.split('/').reverse().filter(x => x !== '')[0];
-			return;
-		} else if (['nicovideo.jp', 'www.nicovideo.jp', 'nico.ms'].includes(url.hostname)) {
-			const id = url.pathname.split('/').reverse().filter(x => x !== '')[0];
-			if (['sm', 'nm', 'ax', 'ca', 'cd', 'cw', 'fx', 'ig', 'na', 'om', 'sd', 'sk', 'yk', 'yo', 'za', 'zb', 'zc', 'zd', 'ze', 'nl', 'so', ...Array(10).keys()].some(x => id.startsWith(x)) {
-				this.nicovideoId = id;
-				this.position = url.searchParams.get('from');
-				return;
-			}
-		} else if (this.detail && url.hostname == 'twitter.com' && /^\/.+\/status(es)?\/\d+/.test(url.pathname)) {
+		if (this.detail && url.hostname == 'twitter.com' && /^\/.+\/status(es)?\/\d+/.test(url.pathname)) {
 			this.tweetUrl = url;
 			const twttr = (window as any).twttr || {};
 			const loadTweet = () => twttr.widgets.load(this.$refs.tweet);
@@ -100,19 +75,20 @@ export default Vue.extend({
 				twttr.ready = loadTweet;
 				(window as any).twttr = twttr;
 			}
-			return;
-		}
-		fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
-			res.json().then(info => {
-				this.title = info.title;
-				this.description = info.description;
-				this.thumbnail = info.thumbnail;
-				this.icon = info.icon;
-				this.sitename = info.sitename;
+		} else {
+			fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
+				res.json().then(info => {
+					this.title = info.title;
+					this.description = info.description;
+					this.thumbnail = info.thumbnail;
+					this.player = info.player;
+					this.icon = info.icon;
+					this.sitename = info.sitename;
 
-				this.fetching = false;
+					this.fetching = false;
+				});
 			});
-		});
+		}
 	}
 });
 </script>

From 1b9f29395987270e823dc73bdef77daff567c85c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Thu, 9 Aug 2018 23:34:54 +0900
Subject: [PATCH 002/134] Update url-preview.vue
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

多分もう折り返す必要はないのだわ
---
 src/client/app/common/views/components/url-preview.vue | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 6100c1fbfb..0cfa76fd80 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -1,7 +1,5 @@
 <template>
-<iframe v-if="player"
-	:src="player"
-	frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
+<iframe v-if="player" :src="player" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
 <div v-else-if="tweetUrl && detail" class="twitter">
 	<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
 		<a :href="url"></a>

From 5c97da935df69bf633fa9cc30e740675e456566c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 12:21:18 +0900
Subject: [PATCH 003/134] New Crowdin translations (#2150)

---
 locales/es.yml | 156 ++++++++++++++++++++++++-------------------------
 1 file changed, 78 insertions(+), 78 deletions(-)

diff --git a/locales/es.yml b/locales/es.yml
index 21a36c6a4f..eb039df696 100644
--- a/locales/es.yml
+++ b/locales/es.yml
@@ -39,7 +39,7 @@ common:
     weeks_ago: "Hace {} semana(s)"
     months_ago: "Hace {} mes(es)"
     years_ago: "Hace {} año(s)"
-  month-and-day: "{month}月 {day}日"
+  month-and-day: "{day} de {month}"
   trash: "Papelera"
   weekday-short:
     sunday: "domingo"
@@ -235,13 +235,13 @@ common/views/components/messaging-room.vue:
   no-history: "El historial se ha acabado"
   resize-form: "Arrastra para redimensionar"
   new-message: "Nuevo mensaje"
-  only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
+  only-one-file-attached: "Un único archivo se puede conectar al mensaje"
 common/views/components/messaging-room.form.vue:
   input-message-here: "Escribe el mensaje aquí"
   send: "Enviar"
   attach-from-local: "Adjunta ficheros desde tu PC"
   attach-from-drive: "Adjunta ficheros desde tu disco"
-  only-one-file-attached: "メッセージに添付できるのはひとつのファイルのみです"
+  only-one-file-attached: "Un único archivo se puede conectar al mensaje"
 common/views/components/messaging-room.message.vue:
   is-read: "Leer"
   deleted: "El mensaje se ha borrado"
@@ -378,56 +378,56 @@ common/views/widgets/tips.vue:
   tips-line10: "Usando el accesorio de Máquina del Tiempo puedes encontrar publicaciones antiguas"
   tips-line11: "Puedes resaltar publicaciones en la página de usuario haciendo click en \"...\""
   tips-line13: "Todos los archivos añadidos a la publicación se han guardado en tu unidad."
-  tips-line14: "ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます"
-  tips-line17: "「**」でテキストを囲むと**強調表示**されます"
-  tips-line19: "いくつかのウィンドウはブラウザの外に切り離すことができます"
-  tips-line20: "カレンダーウィジェットのパーセンテージは、経過の割合を示しています"
-  tips-line21: "APIを利用してbotの開発なども行えます"
-  tips-line23: "まゆかわいいよまゆ"
-  tips-line24: "Misskeyは2014年にサービスを開始しました"
-  tips-line25: "対応ブラウザではMisskeyを開いていなくても通知を受け取れます"
+  tips-line14: "Cuando personalizas el inicio puedas dar click derecho a un accesorio y cambiar el diseño."
+  tips-line17: "Al colocar ** delante y luego del texto, lo estarás destacando en negrillas"
+  tips-line19: "Algunas ventanas pueden ser separadas fuera del navegador"
+  tips-line20: "El porcentaje mostrando en el accesorio de calendario indica el porcentaje de tiempo transcurrido."
+  tips-line21: "También puedes usar la API para desarrollar tus propios bots."
+  tips-line23: "Mayu is tan bonito con sus cejas."
+  tips-line24: "Misskey inició en 2014."
+  tips-line25: "Puedes recibir notificaciones incluso si Misskey no está abierto en un navegador compatible."
 common/views/pages/follow.vue:
-  signed-in-as: "{}としてサインイン中"
-  following: "フォロー中"
-  follow: "フォロー"
-  request-pending: "フォロー許可待ち"
-  follow-request: "フォロー申請"
+  signed-in-as: "Autenticado como {}"
+  following: "Siguiendo"
+  follow: "Seguir"
+  request-pending: "Solicitud pendiente"
+  follow-request: "Solicitar suscripción"
 desktop:
-  banner-crop-title: "バナーとして表示する部分を選択"
-  banner: "バナー"
-  uploading-banner: "新しいバナーをアップロードしています"
-  banner-updated: "バナーを更新しました"
-  choose-banner: "バナーにする画像を選択"
-  avatar-crop-title: "アバターとして表示する部分を選択"
-  avatar: "アバター"
-  uploading-avatar: "新しいアバターをアップロードしています"
-  avatar-updated: "アバターを更新しました"
-  choose-avatar: "アバターにする画像を選択"
+  banner-crop-title: "Corta la parte que aparece como un banner"
+  banner: "Banner"
+  uploading-banner: "Cargando un nuevo banner"
+  banner-updated: "Banner actualizado"
+  choose-banner: "Escoge un banner"
+  avatar-crop-title: "Corta la parte que aparece como un avatar"
+  avatar: "Avatar"
+  uploading-avatar: "Cargando un nuevo avatar"
+  avatar-updated: "Avatar actualizado"
+  choose-avatar: "Escoge una imagen de avatar"
 desktop/views/components/activity.chart.vue:
-  total: "Black ... Total"
-  notes: "Blue ... Notes"
-  replies: "Red ... Replies"
-  renotes: "Green ... Renotes"
+  total: "Negro ... Total"
+  notes: "Azul ... Notas"
+  replies: "Rojo ... Respuestas"
+  renotes: "Verde ... Republicaciones"
 desktop/views/components/activity.vue:
-  title: "アクティビティ"
-  toggle: "表示を切り替え"
+  title: "Actividad"
+  toggle: "Alternar vistas"
 desktop/views/components/calendar.vue:
-  title: "{1}年 {2}月"
-  prev: "前の月"
-  next: "次の月"
-  go: "クリックして時間遡行"
+  title: "{1} / {2}"
+  prev: "Mes anterior"
+  next: "Próximo mes"
+  go: "Click para navegar"
 desktop/views/components/choose-file-from-drive-window.vue:
-  choose-file: "ファイル選択中"
-  upload: "PCからドライブにファイルをアップロード"
-  cancel: "キャンセル"
-  ok: "決定"
-  choose-prompt: "ファイルを選択"
+  choose-file: "Escoger archivos"
+  upload: "Cargar archivos de tu dispositivo"
+  cancel: "Cancelar"
+  ok: "OK"
+  choose-prompt: "Escoger archivos"
 desktop/views/components/choose-folder-from-drive-window.vue:
-  cancel: "キャンセル"
-  ok: "決定"
+  cancel: "Cancelar"
+  ok: "OK"
   choose-prompt: "Escoge una Carpeta"
 desktop/views/components/crop-window.vue:
-  skip: "クロップをスキップ"
+  skip: "Ignorar el cortado"
   cancel: "Cancelar"
   ok: "OK"
 desktop/views/components/drive-window.vue:
@@ -438,8 +438,8 @@ desktop/views/components/drive.file.vue:
   banner: "Banner"
   contextmenu:
     rename: "Renombrar"
-    mark-as-sensitive: "閲覧注意に設定"
-    unmark-as-sensitive: "閲覧注意を解除"
+    mark-as-sensitive: "Marcar como 'sensible'"
+    unmark-as-sensitive: "Desmarcar como 'sensible'"
     copy-url: "Copia la URL"
     download: "Descargar"
     else-files: "Otros"
@@ -484,8 +484,8 @@ desktop/views/components/drive.vue:
     upload: "Subir fichero"
     url-upload: "Subir desde una URL"
 desktop/views/components/media-image.vue:
-  sensitive: "閲覧注意"
-  click-to-show: "クリックして表示"
+  sensitive: "El contenido es NSFW (no seguro para ver en el trabajo, 'not safe for work')"
+  click-to-show: "Click para mostrar"
 desktop/views/components/media-video.vue:
   sensitive: "閲覧注意"
   click-to-show: "クリックして表示"
@@ -652,26 +652,26 @@ desktop/views/components/settings.vue:
   version: "バージョン:"
   latest-version: "最新のバージョン:"
   update-checking: "アップデートを確認中"
-  do-update: "アップデートを確認"
-  update-settings: "詳細設定"
-  prevent-update: "アップデートを延期する(非推奨)"
-  prevent-update-desc: "この設定をオンにしてもアップデートが反映される場合があります。この設定はこのデバイスのみ有効です。"
-  no-updates: "利用可能な更新はありません"
-  no-updates-desc: "お使いのMisskeyは最新です。"
-  update-available: "新しいバージョンが利用可能です"
-  update-available-desc: "ページを再度読み込みすると更新が適用されます。"
-  advanced-settings: "高度な設定"
-  debug-mode: "デバッグモードを有効にする"
-  debug-mode-desc: "この設定はブラウザに記憶されます。"
-  experimental: "実験的機能を有効にする"
-  experimental-desc: "実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。"
-  tools: "ツール"
-  task-manager: "タスクマネージャ"
-  third-parties: "サードパーティ"
+  do-update: "Chequear por actualizaciones"
+  update-settings: "Configuración avanzada"
+  prevent-update: "Posponer actualizaciones (no recomendado)"
+  prevent-update-desc: "Incluso si activas esta configuración, algunas actualizaciones podrían aplicarse. Esta configuración está habilitada sólo para este dispositivo."
+  no-updates: "No hay actualizaciones disponibles"
+  no-updates-desc: "Tu Misskey está actualizado"
+  update-available: "Una nueva versión está disponible"
+  update-available-desc: "Las actualizaciones se aplicarán cuando actualices la página nuevamente."
+  advanced-settings: "Avanzado"
+  debug-mode: "Habilitar modo de depuración"
+  debug-mode-desc: "Esta configuración se ha guardado en el navegador."
+  experimental: "Habilitar herramientas experimentales"
+  experimental-desc: "Activar esto puede hacer que tu cliente de Misskey se vuelva inestable. La configuración se ha guardado en el navegador."
+  tools: "Herramientas"
+  task-manager: "Navegador de tareas"
+  third-parties: "Servicios externos"
 desktop/views/components/settings.2fa.vue:
   intro: "二段階認証を設定すると、サインイン時にパスワードだけでなく、予め登録しておいた物理的なデバイス(例えばあなたのスマートフォンなど)も必要になり、よりセキュリティが向上します。"
-  detail: "詳細..."
-  url: "https://www.google.co.jp/intl/ja/landing/2step/"
+  detail: "Ver detalles..."
+  url: "https://www.google.com/landing/2step/"
   caution: "Si pierdes acceso al dispositivo, no podrás conectarte a Misskey."
   register: "Registrar un dispositivo"
   already-registered: "Un dispositivo ya fue registrado"
@@ -704,19 +704,19 @@ desktop/views/components/settings.password.vue:
   reset: "Cambiar contraseña"
   enter-current-password: "Ingresar contraseña actual"
   enter-new-password: "Ingresar nueva contraseña"
-  enter-new-password-again: "もう一度新しいパスワードを入力してください"
-  not-match: "新しいパスワードが一致しません"
-  changed: "パスワードを変更しました"
+  enter-new-password-again: "Ingresar nueva contraseña de nuevo"
+  not-match: "Las nuevas contraseñas no se corresponden consigo mismas"
+  changed: "Contraseña actualizada"
 desktop/views/components/settings.profile.vue:
-  avatar: "アイコン"
-  choice-avatar: "画像を選択"
-  name: "名前"
-  location: "場所"
-  description: "自己紹介"
-  birthday: "誕生日"
-  save: "保存"
-  locked-account: "アカウントの保護"
-  is-locked: "投稿を非公開にする"
+  avatar: "Avatar"
+  choice-avatar: "Escoger una imagen"
+  name: "Nombre"
+  location: "Localización"
+  description: "Descripción"
+  birthday: "Fecha de nacimiento"
+  save: "Perfil actualizado"
+  locked-account: "Protege tu cuenta"
+  is-locked: "Crear una nota privada"
   other: "その他"
   is-bot: "このアカウントはBotです"
   is-cat: "このアカウントはCatです"

From 73cc425093cc1675efeb3fe524a10551069e5eba Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Sat, 11 Aug 2018 12:21:30 +0900
Subject: [PATCH 004/134] fix(package): update request to version 2.88.0
 (#2151)

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 64f9b577a4..ee85139df9 100644
--- a/package.json
+++ b/package.json
@@ -171,7 +171,7 @@
 		"recaptcha-promise": "0.1.3",
 		"reconnecting-websocket": "3.2.2",
 		"redis": "2.8.0",
-		"request": "2.87.0",
+		"request": "2.88.0",
 		"request-promise-native": "1.0.5",
 		"rimraf": "2.6.2",
 		"rndstr": "1.0.0",

From 12037fab9bb3dff6424d057b43a13b96757d090b Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Sat, 11 Aug 2018 12:21:42 +0900
Subject: [PATCH 005/134] fix(package): update @types/node to version 10.5.8
 (#2152)

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index ee85139df9..60095e6fe8 100644
--- a/package.json
+++ b/package.json
@@ -59,7 +59,7 @@
 		"@types/mocha": "5.2.3",
 		"@types/mongodb": "3.1.3",
 		"@types/ms": "0.7.30",
-		"@types/node": "10.5.7",
+		"@types/node": "10.5.8",
 		"@types/portscanner": "2.1.0",
 		"@types/pug": "2.0.4",
 		"@types/qrcode": "1.2.0",

From 3c90abfb96578400422773af669a7fb4aa2ccf7f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 12:24:16 +0900
Subject: [PATCH 006/134] 5.21.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 60095e6fe8..f7cd4fed3b 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.20.1",
-	"clientVersion": "1.0.8105",
+	"version": "5.21.0",
+	"clientVersion": "1.0.8117",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From a8f142096cebaa262e224f805bb58b68f28aefef Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sat, 11 Aug 2018 18:04:59 +0900
Subject: [PATCH 007/134] Cache failed url-preview (apply to proxy/client)
 (#2154)

---
 .../app/common/views/components/url-preview.vue    | 14 ++++++++------
 src/server/web/url-preview.ts                      |  4 +++-
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 4f1e34c6ca..34f2558c69 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -104,13 +104,15 @@ export default Vue.extend({
 		}
 		fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
 			res.json().then(info => {
-				this.title = info.title;
-				this.description = info.description;
-				this.thumbnail = info.thumbnail;
-				this.icon = info.icon;
-				this.sitename = info.sitename;
+				if (info.url != null) {
+					this.title = info.title;
+					this.description = info.description;
+					this.thumbnail = info.thumbnail;
+					this.icon = info.icon;
+					this.sitename = info.sitename;
 
-				this.fetching = false;
+					this.fetching = false;
+				}
 			});
 		});
 	}
diff --git a/src/server/web/url-preview.ts b/src/server/web/url-preview.ts
index 99ee2eaebd..e96eb309fe 100644
--- a/src/server/web/url-preview.ts
+++ b/src/server/web/url-preview.ts
@@ -14,7 +14,9 @@ module.exports = async (ctx: Koa.Context) => {
 
 		ctx.body = summary;
 	} catch (e) {
-		ctx.status = 500;
+		ctx.status = 200;
+		ctx.set('Cache-Control', 'max-age=86400, immutable');
+		ctx.body = '{}';
 	}
 };
 

From 19c872a1f34d50071283deeab05330a309210d22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Sat, 11 Aug 2018 19:56:53 +0900
Subject: [PATCH 008/134] Update url-preview.vue

---
 src/client/app/common/views/components/url-preview.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 8ad6a622d0..57b441a328 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -83,7 +83,7 @@ export default Vue.extend({
 					this.thumbnail = info.thumbnail;
 					this.icon = info.icon;
 					this.sitename = info.sitename;
-
+					this.player = info.player;
 					this.fetching = false;
 				}
 			});

From 9bc17974f288dfbc684053cf9c45358d6827d617 Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sat, 11 Aug 2018 20:20:35 +0900
Subject: [PATCH 009/134] =?UTF-8?q?=E6=9C=AA=E5=88=A9=E7=94=A8=E6=99=82?=
 =?UTF-8?q?=E3=81=AFTwitter=E3=81=A7=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3?=
 =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=20(#2156?=
 =?UTF-8?q?)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/client/app/common/views/components/signin.vue | 7 ++++---
 src/client/app/config.ts                          | 2 ++
 webpack.config.ts                                 | 3 ++-
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index deaeeca6a7..50c50cbe08 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -12,13 +12,13 @@
 	</ui-input>
 	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
 	<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
-	<p style="margin: 8px 0;">%i18n:@or%<a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
+	<p style="margin: 8px 0;" v-if="twitterIntegration">%i18n:@or%<a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
 </form>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
-import { apiUrl, host } from '../../../config';
+import { apiUrl, host, twitterIntegration } from '../../../config';
 
 export default Vue.extend({
 	props: {
@@ -36,7 +36,8 @@ export default Vue.extend({
 			password: '',
 			token: '',
 			apiUrl,
-			host
+			host,
+			twitterIntegration
 		};
 	},
 	methods: {
diff --git a/src/client/app/config.ts b/src/client/app/config.ts
index ceee0a2d62..04486ea230 100644
--- a/src/client/app/config.ts
+++ b/src/client/app/config.ts
@@ -22,6 +22,7 @@ declare const _CODENAME_: string;
 declare const _LICENSE_: string;
 declare const _GOOGLE_MAPS_API_KEY_: string;
 declare const _WELCOME_BG_URL_: string;
+declare const _TWITTER_INTEGRATION_: boolean;
 
 export const host = _HOST_;
 export const hostname = _HOSTNAME_;
@@ -47,3 +48,4 @@ export const codename = _CODENAME_;
 export const license = _LICENSE_;
 export const googleMapsApiKey = _GOOGLE_MAPS_API_KEY_;
 export const welcomeBgUrl = _WELCOME_BG_URL_;
+export const twitterIntegration = _TWITTER_INTEGRATION_;
diff --git a/webpack.config.ts b/webpack.config.ts
index e60eb8347e..8dca4c0ee3 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -95,7 +95,8 @@ const consts = {
 	_URL_: config.url,
 	_LICENSE_: licenseHtml,
 	_GOOGLE_MAPS_API_KEY_: config.google_maps_api_key,
-	_WELCOME_BG_URL_: config.welcome_bg_url
+	_WELCOME_BG_URL_: config.welcome_bg_url,
+	_TWITTER_INTEGRATION_: config.twitter != null
 };
 
 const _consts: { [ key: string ]: any } = {};

From 2fcf9288a57a152fbe2596494670c9079e892ece Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 20:53:03 +0900
Subject: [PATCH 010/134] Refactoring

---
 src/server/api/endpoints/messaging/history.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts
index 66798d50c5..43cceacf95 100644
--- a/src/server/api/endpoints/messaging/history.ts
+++ b/src/server/api/endpoints/messaging/history.ts
@@ -40,6 +40,5 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
 		});
 
 	// Serialize
-	res(await Promise.all(history.map(async h =>
-		await pack(h.messageId, user))));
+	res(await Promise.all(history.map(h => pack(h.messageId, user))));
 });

From 7942aa677fd8f3503cca977ea9bba5d4a2efdd85 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 21:01:26 +0900
Subject: [PATCH 011/134] Update doc

---
 src/server/api/endpoints/notes/timeline.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index faa8ccf3ca..c1b8644e4d 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -8,7 +8,8 @@ import getParams from '../../get-params';
 
 export const meta = {
 	desc: {
-		ja: 'タイムラインを取得します。'
+		ja: 'タイムラインを取得します。',
+		en: 'Get timeline of myself.'
 	},
 
 	requireCredential: true,
@@ -67,9 +68,6 @@ export const meta = {
 	}
 };
 
-/**
- * Get timeline of myself
- */
 export default async (params: any, user: ILocalUser) => {
 	const [ps, psErr] = getParams(meta, params);
 	if (psErr) throw psErr;

From be68f42220c1dbf4ce0fd7f57edb4c21ef70f1b2 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 21:01:39 +0900
Subject: [PATCH 012/134] Implement messaging/messages/read

---
 .../api/endpoints/messaging/messages/read.ts  | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 src/server/api/endpoints/messaging/messages/read.ts

diff --git a/src/server/api/endpoints/messaging/messages/read.ts b/src/server/api/endpoints/messaging/messages/read.ts
new file mode 100644
index 0000000000..f609337523
--- /dev/null
+++ b/src/server/api/endpoints/messaging/messages/read.ts
@@ -0,0 +1,43 @@
+import $ from 'cafy'; import ID from '../../../../../misc/cafy-id';
+import Message from '../../../../../models/messaging-message';
+import { ILocalUser } from '../../../../../models/user';
+import read from '../../../common/read-messaging-message';
+import getParams from '../../../get-params';
+
+export const meta = {
+	desc: {
+		ja: '指定した自分宛てのメッセージを既読にします。',
+		en: 'Mark as read a message of messaging.'
+	},
+
+	requireCredential: true,
+
+	kind: 'messaging-write',
+
+	params: {
+		messageId: $.type(ID).note({
+			desc: {
+				ja: '既読にするメッセージのID',
+				en: 'The ID of a message that you want to mark as read'
+			}
+		})
+	}
+};
+
+export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
+	const [ps, psErr] = getParams(meta, params);
+	if (psErr) throw psErr;
+
+	const message = await Message.findOne({
+		_id: ps.messageId,
+		recipientId: user._id
+	});
+
+	if (message == null) {
+		return rej('message not found');
+	}
+
+	read(user._id, message.userId, message);
+
+	res();
+});

From 4a5e1450489c4153a00d8b1f0281657bec2da847 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 21:08:34 +0900
Subject: [PATCH 013/134] =?UTF-8?q?Add=20new=20reaction:=20rip=20?=
 =?UTF-8?q?=F0=9F=98=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/ja.yml                                   |   1 +
 .../common/views/components/reaction-icon.vue    |   1 +
 .../common/views/components/reaction-picker.vue  |   7 ++++---
 .../common/views/components/reactions-viewer.vue |   1 +
 src/client/assets/reactions/rip.png              | Bin 0 -> 6793 bytes
 src/misc/get-reaction-emoji.ts                   |   1 +
 src/models/note-reaction.ts                      |   1 +
 7 files changed, 9 insertions(+), 3 deletions(-)
 create mode 100644 src/client/assets/reactions/rip.png

diff --git a/locales/ja.yml b/locales/ja.yml
index ecde1bb6a1..98397212d4 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -70,6 +70,7 @@ common:
     congrats: "おめでとう"
     angry: "おこ"
     confused: "こまこまのこまり"
+    rip: "RIP"
     pudding: "Pudding"
 
   note-placeholders:
diff --git a/src/client/app/common/views/components/reaction-icon.vue b/src/client/app/common/views/components/reaction-icon.vue
index 2d5391a21e..46886b8ab2 100644
--- a/src/client/app/common/views/components/reaction-icon.vue
+++ b/src/client/app/common/views/components/reaction-icon.vue
@@ -8,6 +8,7 @@
 	<img v-if="reaction == 'congrats'" src="/assets/reactions/congrats.png" alt="%i18n:common.reactions.congrats%">
 	<img v-if="reaction == 'angry'" src="/assets/reactions/angry.png" alt="%i18n:common.reactions.angry%">
 	<img v-if="reaction == 'confused'" src="/assets/reactions/confused.png" alt="%i18n:common.reactions.confused%">
+	<img v-if="reaction == 'rip'" src="/assets/reactions/rip.png" alt="%i18n:common.reactions.rip%">
 	<template v-if="reaction == 'pudding'">
 		<img v-if="$store.getters.isSignedIn && $store.state.settings.iLikeSushi" src="/assets/reactions/sushi.png" alt="%i18n:common.reactions.pudding%">
 		<img v-else src="/assets/reactions/pudding.png" alt="%i18n:common.reactions.pudding%">
diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue
index 5a149cc4d1..a455afbf7d 100644
--- a/src/client/app/common/views/components/reaction-picker.vue
+++ b/src/client/app/common/views/components/reaction-picker.vue
@@ -10,9 +10,10 @@
 			<button @click="react('hmm')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="4" title="%i18n:common.reactions.hmm%"><mk-reaction-icon reaction='hmm'/></button>
 			<button @click="react('surprise')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="5" title="%i18n:common.reactions.surprise%"><mk-reaction-icon reaction='surprise'/></button>
 			<button @click="react('congrats')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="6" title="%i18n:common.reactions.congrats%"><mk-reaction-icon reaction='congrats'/></button>
-			<button @click="react('angry')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="4" title="%i18n:common.reactions.angry%"><mk-reaction-icon reaction='angry'/></button>
-			<button @click="react('confused')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="5" title="%i18n:common.reactions.confused%"><mk-reaction-icon reaction='confused'/></button>
-			<button @click="react('pudding')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="6" title="%i18n:common.reactions.pudding%"><mk-reaction-icon reaction='pudding'/></button>
+			<button @click="react('angry')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="7" title="%i18n:common.reactions.angry%"><mk-reaction-icon reaction='angry'/></button>
+			<button @click="react('confused')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="8" title="%i18n:common.reactions.confused%"><mk-reaction-icon reaction='confused'/></button>
+			<button @click="react('rip')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="9" title="%i18n:common.reactions.rip%"><mk-reaction-icon reaction='rip'/></button>
+			<button @click="react('pudding')" @mouseover="onMouseover" @mouseout="onMouseout" tabindex="10" title="%i18n:common.reactions.pudding%"><mk-reaction-icon reaction='pudding'/></button>
 		</div>
 	</div>
 </div>
diff --git a/src/client/app/common/views/components/reactions-viewer.vue b/src/client/app/common/views/components/reactions-viewer.vue
index 97cb6be17c..23f4b59d62 100644
--- a/src/client/app/common/views/components/reactions-viewer.vue
+++ b/src/client/app/common/views/components/reactions-viewer.vue
@@ -9,6 +9,7 @@
 		<span v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span>
 		<span v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span>
 		<span v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span>
+		<span v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span>
 		<span v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span>
 	</template>
 </div>
diff --git a/src/client/assets/reactions/rip.png b/src/client/assets/reactions/rip.png
new file mode 100644
index 0000000000000000000000000000000000000000..4800fdb91b18be14cddce63fe372f9dba4c4172a
GIT binary patch
literal 6793
zcmZ{JbySqyxBt*3E!`j}DP4oo%@C5(-5o=Bh=3qShvWc5qcqYv5>i8VcQ+z;eBbZy
zcklh<t|#_-&RKh(z0Qv3v-kO^t}2g<O^FQv0B{u*WZogD&))|V9q|^i6r4a1R2xZ<
zBmhvGfb-i74e?EHq3{j_0Qj*003i_oz&&CpWETMN;syZrO#uMWbO3<NHM>Pa3^9S>
zsR)wASjWU7L#M9_WSK(@p_?nq%K)DKUU_XLUjP7#Pl_^<T0ZkfMi@R?@aeV?Ow!di
zfjR;A;u^tdtnZj)_m~+-i=@^4h>;|b7-nTp*N7qLMT##yh>?-=BbJj0XleEoG?OG`
z7*R1Fj=YYWzrZZ5c^_3xw;!&BV8d@7D%z&sY<(4sai?mtweiqnXfSt#%+lJh_voZn
ziY>%W{2coAbI?Hv#;;J0YSK;=t*}Rv3wgHU+NS=b9lZ3Yde}z<vdQ)<oi1WhgXe<R
zBR_u}DAUStEpy(&vgvjJiNIO-*&r7o%|j2WM!b7Hy%AlBX$_4ueaN!4eYH+z3q#=K
zqh|}g>_DcD1@>y$%vZM|)lg-Ypt0cN^S6WH7xp8*Q4B7ofYi)5=-H*_lMj%k4YdZD
zuZAfKc&+XKQ$rRiDreWWE0qWyhcb(1PAGZvLv*9nC#lX?Xn@eY@Vk@RBhr%5puo{)
zE8JkQX2^6=;X~Ocalaq^5f&ZroI3Hsqsx(uL>y9^5rb=|>-dN+Pd{awd%jwdC6%BY
z=Oahe&HOHmfUcJTO!%V*+nA%@X?Ad*bt0<>P#A>Uy=(xmbE5#wB{=j|_DBer%w>n8
zja*%UKjyhqBT0@;mDpj|&oJ~8P>z3wChV6Mzh1o=ag?8ZmyullI}ltR*`c{xF{vfA
zFC3{NrUW0LgWP|)(U^JfU~4}7e7^k}rKP5lX~`7n6|>g&95dQj`<3qDJ9@Rve-yYP
zr{YHb&+5@wwJ>s_p>+9B6jvnGPF!JRU8G0*`qeOAFd1tA8QF%!*G+m#S8kL!>u*k0
zz>!oKIQA1qWuPZpo5+cnn;kP3VJ~yIJxEyQsKlk{jK^@Hib^5PyKa<Wp(IR{Rk#C%
zxaN~?A^8DID8(!xin*`Fu?S(7*N>!d6&OqhHZI|ceyn$CPUrNKD4X)$oF_#vmMbsf
z?axk1-a&;1E5`v#z~&&q?*orfdx%#zp`>a!%-?S82Xh_oA7Z7YX~}YTTrzm=Y$s*~
zyG{^W&CCut0gkzpWAu3Z*mNpuvjH|&7i$yRz|n8*ZV?MTvxW&mdwuqx1oMT1xV-oX
zC$X)L%eeyI<m}V8Mj(5_&H_<Vs@X5XQEFtPWIS_3EnmKfDXQMsQ<v1s7|`Y$quwl&
z)qlzCKe4-UXQ@7YTB)Tm9g1<K<(OyIAeqU^WO6KowMN#o&UB<rR*{lx9FY&W=2%Fo
z5VUk{zInIX!w7Z-a2c+ty5o*^O!6zV>Q45e)H<lWE%r2a$D)A{8EXH4jjqq~8ltjP
z2zZN8g2ZJ7Uedz0O@ntvVaJ1!IKK*ejXR7Y>X|OD>%(1ev*-c~MvKB(5f0FN%Ih&^
zd#~8_5ooA{yPk(t7b-XQ^HZ5{W$Es|!_mIUaflsVj@+7f4y=mrZg-E8IG$tOW^j3O
zBWTyn&3_?f5p1=b+C5K@Y4OMu=-=8W-m7w?Zn1u5yo#J1Lgl(yJQl3zM5$DnO{%kJ
zsuY;~MB4)%9~p)o72xP*j@}C7BVsvl=o9d9xJ*D=dAy3{jqoZlT6y4=JuvXI$nLky
zW}ie6C~T2i-k~d{RZRI)zip;SX@kM&+bU0EKiQAmkd&QRo$pqy8Ee{AZ&TX@cN~RR
zpv=KW0y_CdpK>?ocWRAVFCs=PW>Cq7VaF-ruNLW8k*Yc=JiJFhqu(Q~D`@y5Qk!!v
z$o`DdK(!!+to1}IXsoo0pn)dhv6QZ5aVM{eZtp`QY89M@M@h4b*`O|54}LE=QI)_X
z_qQ3)TefCcus$|I$QZvaeE90l;&=NrirWoer~aE%8&)!6p!rzG3xgL6ki2y8QPO7V
z3t<%RRRTl5ydHh1)#&GS6ejPQjY~DhPy8Ya-#0EtR^ppWEt%^>njmx5wMO_c>D5MQ
zhAHTOd0HNNk#X~x`AMYn3d{r{kIL~GE&ekVYaebOrmN{H2kn~Rl52*7t~%hysB!Ej
z)DqfVH<?NP5iwDC)KL@$o<-Rxle0@t8PDKmo}RM-uW$wLhhuZqG@rZyTqu;WJfcnk
zKU)LiYJD7j@U#^9F7pU0W^>QO<NKRcqHMncEdIy_r8H)W-LAmBM7}HY>n4)342q9O
z6qdH?FB&<MI~o2iNg_J^eExhRv9tigXI%w6zIU)iwKtY3ncT)F_Np$nJB-g21Fs9R
z<5z?q(op)<EWM*`9Xg4(&%96UpJZL3uYy5wSYOmwG=}B`9>g!yPa<<gMcAte<%2Q7
z4K_lB(yu9a<95Lt_(DEiJY^0@-{cP|D6cM9^i#PL;~n?Zf>|0;l<kjYx;#+PF#dJJ
ze*|1ap^p(|N*O@5!d6mL7s)R^92sWJc<I>wxQTlMN*n@D)M@k7`7H(ScBXbEw`|a~
z{PaHIY!g+pvt|xL%EE6T&`3b5WsvKQNhbsf1ISi;lbBr2xC9(AK>3wEwe!CZQ*^RB
z(pS4o<=f30b@_Uqmdr=TJDET1#^yFoB;PU@RprQK2?l^3gn|`H8m|iKXmVchK?RJ|
zf;5)_W+J_N*6kEhPK)GUGjUh81F|wMkOfUqbxhI0y&-x8#OBg*(Ih;v6oT8G7zO%V
z7?}Xhd}t+{5?|gnw#WI7-eaL4t1blSHc;R&>^qK!8v|5eN@JcB8k!j|x`pujUm;7*
zmR)V2`hKJm&u`26FtJvNvb)*PuHvXB+Smp(T1`qCm9cpN&KpuZiZD{NXF{#9x>fDK
zv-?c?b`|IC8kH~o>&=fpLXq@Bux3LD_5RJwwQ(cz=JQ0}p;Kemaqr|cMGq6>?eCwp
z@3zoM%sn?IGkFQiEcKqknPT1+(qQxJ`=b<Sp{Cx!JYZ|O_&RJCKkc|MU-<@G+Uxf0
z+l#EIlgoq8=yq!;?W@G?i5TZnU#EqN<CO=tUosrwIt~-eXPjq{#gF;HnMJy|8*tE(
zz;{tK{5hVwBdOIA2`2WJiDSdb+lw)H?~`I(yZsC^H$=t`jQ86yMsQeM<fdxQ9KG_j
z+nHzX;hz1~O1%>YN?&Z*uIz@le=%Bs7Xu7Hki4;eY#dxhiw2wE{>&Od=7rLX<Jawl
z0$U{WdJV7obh0l$=qC_ODcl<_DHu&%WWSe_gmz=s*@0A}R;d@IQEa<1RY>P+<c?3K
z3=AdnXW8;C%aSFFV`Pf4J^QpifHUvMEWMDfbn#(8_E!zBW%^cD$qmCjT=8-FIrWHb
ziH$Ve9OR1#e11_uY^B?L^!0J^AYHz`Ep}y6J5#Rul|d>iW#XD0={@Ub62YUfnApc*
zTXDo8(ymylU7W(`GpmN!<rMtyO<W*jgW926Zw>S!^j@S6;@@CZ6ZO5vHd0&$ZSA!{
zMRT-GN<Qs#$GqE_RQG!6rTHrcI6i=Xjn))lAGYq|iZ<FwBaE81szqEc<vhAkMpPLX
zX)Dhch-4B`l~#@{KG?uI^O-uks#^Kd1DElNIm_H8%edf~W}s+kJ7@@AZ5u`j>aj>F
zP4i2tQ`PUMVQzt>N7GP;;J6|?cM>)s+pLZjm#+Fz;Ivm&aaBN^lJrH`ozt!M>f4Ih
zSd(o%qzkm`qf@o&i9|~%XHctE(%>zg-BXzjW#P7o_b7oS{o}3ziObpvF{9zj@dqzZ
zJ3B+OQ20c6R)M`y<b>?za)+w`!w_QE!i(hc`%kGStEAN72Sw`g4lS0fcpfd6=CYfV
zCFt{R@=g_M&rOueyDyR*jZU$*TQ7>CY>#$++FG*yaOVP>CiNT%;=A}iyw)_h8r~Y>
zSD0S9ztZJDU%ne#-j^G5j#3sxf5p`@g}zO*SwoEqtNHnm<ST<q9!ioPO2b1>+8Z(Z
z4@yg=-Xxm(R#bo;Uk!C@DTIC}rgbQ?R>h%~o7G>x<V7seK<Q02AHBs8fT05;6q`@n
z)gNmrtj>^<@roSnGt-n;(s`H=opg=S92hecP^!~>(ny=owMP!DaE6%i0PWO=-}<RN
zM&6eVlnUtvzk!>eof*MBx<$+9)e9kOe2@NyvHWdt;_m-I%gso!L?Lbf4mZ-jpsrPi
z&0`=%e%KaAvWu`uUsHaAt2P73r=RhT?`1CtugCmqj#e$7`GcMJYB74|D!a+FUl32q
z1$w5ukHeYAU`yZ8b0z6iD7saqeXLElXFboOGd6^;SZKFsJ|-S|wELM-@&P{T8RC>M
zp99YD+u{v~I&sCVlFigCPW>M`xqK9y5UiqJOmU3Gl0Gcm()m&0hHw~-Z@R^DHp)lm
zoNdC9+ZAftAmV14x#@^<Iy{awl1etI54domu*QmIrx=$L>oFHhW%iST_a)V~Cj9b@
zjOYxO+MI&Dm^_3R4gLC9plg`n&3kALjoX)zoN~!_6A@eO2}1q|d6GzD(K2>|@G_h>
z`OhQ<%qJjI#hUsILw=ip-F2k@zg2(H_q0^;t<_l<{d=4R&Nk**qBtW#d*-)(3G@y?
zT`L1B2h;+TZ|z^N9pfizTN<VpP{&LE=tS5;<b^QJhB&GvDT^ZUZ^P7X<!A?Sd>Ik-
z2B|5s;8X1GAW=~i7!A*?m3;OfigQ@w!NrcCJN3QEj^fu)GcrgNSiUL>RacDEhbqa&
zQaQ*Kf8m^75^XtEnezfO4m4j9j!NPK<g6c_^miTsGnp<d8Lf$=5;+5+T-l!?v7h2X
zA=6n6w!!ik1*LO~cXt+n(xMj^?4E2@csa(`*hVV05<;gcM-Bzd=>P2>1|czT<`?mX
z#ACkqVLX#yIjXJ%+3gJf<bMpGxO97X*n6CbdG}P)YHRAFK{<YJoH_eMI4{<W{8*cx
zDEAGYyHn^~!g!F_7X_sTKwN{wRCA9S{y_R#Kmg~rnr~gz+G<VxMl_TqaOH+!f3k?%
ztGq<L@gCdjgTUt#$J-w^JyQH-9;`|k+L6vIk-jA_YX9HHmA8^aITNA$ElF0esY4T#
zKqJUAmaUHb+*-Z{#aemzD;z9?2jld?Jzgh~%d*Py;>t4(LKiBQsyK9~Xt`CU0Bh4<
zs(8_yLTQj<>rh~g6&2@k3*ApFa}B9%1|1>8m2wRR{oi{XuZD~gMQ|q&UfLXvG)pKA
zh|cCjXfM{ii>JMQnI`kF%~qR=dgH8>o*I1d<V+aHuYg1M)l}kHG&4Zdn27@|p7)!y
z7h9EJ;MBhHc-0(}YF*z%%Z#_mY1N#z;lbr;y~4&Wx(6u=!mpxXtMR(YT&_HK{%BFj
zi{*<bIk(G9-~}LoNS?1ojNcPa4Am9K2<^2R<wQYKVA$NR9YSVSCs5$d*R(rIM2N?8
z)Bk$k^Ob<Sm^U>v4n~NMNI=gxU}JUZjKOY}u^JHX$s+8jRyT&$-k|OTCjT9sjjxs0
zi?LtXlm;5#(0S_37l;REIx_>u9d;aCE166N4>ptL^<QW3<fXN_7<=6p^*n+dX#SDf
zH&&O-7_4KRK*%JWHZ4%}T6B&}gdQxD<-ez=Qu*V>*%Q=ozyI22jHOHAp^K_-*D?tH
zBKQMBEN5uw!O9XHf$v<mYj&+{L)$MF^debA`Sm$b2sAlBa<?EU|LIlRuG_VvtwCPU
zA`>W6W3FA|_Z=3Oc8~tDg>Lp8(^G=7ZF=|w6-!mEcQ|g~0j(&l!4N}K4qy4~(H)au
zudO<3RI%E@%gLNG?}e=9W~@)g_uj3WqZEIN1bq_KIBTvS+}CUSze;peRyeexp4r^u
z&CHUP5LEs-jto$y;~tF-_<0OM{7lM`oOt*-%d5LYPfjLc1rL{sWe>4Ez<2C-D|agM
zxmlEx>CVB83zGW4stPCZ-xTfNXB5M@(}V;^O~~7kMk{^b&Y-ZrVj$$1?Y8NiZYJUm
zjhC5ClN&Lw<LKwu`i$m+r#c55-;x#j#rr+Exqb2yz3nEiV=pK*Nv<`BNeg;3R5vwR
zl&oIjw06v9iBMMWzmK|nKS1RB+pDsCEcrKTRhBLRBA=81<=5Z0$9Fn&p3|_O=F2jy
z-%Y=J{`rm5>BhWPUaZso#Q3j_m#7>cUJHTPTpDBgy;FOV{QPd+>qk(nO&foh5m1@S
zRLEf;KB<yMp*MRZb<(_eMjx{2on`->7YvlW!wm4XV9FG@#Nweu3r?Sp{c~v7(lYYr
z8b1JGRj1d-S`3^4ooPckjB|F<&!W-D5%Pvrc`Y617h$~HJc@Bof@CzwM&HY0S`81J
zqFR^u1}wXH>RBGs9?iIArchbqCs#8ye>X+$NfwQqTgt`MGNJc=dnmDIY&f*+k^7cs
zN3nq}>c|<Xc&2*gB4|?VW{n7ne2ekzP`r&Ow(#w=e5DN*s<7=e7`jCXPWbl4p47(6
z-w28o+>>^QI?|{NMm8Te8VhLL<uXkjK_3wsvPN-O^B?=se!A3Qp!*KloPHG?4>v!E
z=@80=@RwZH<L<6@6F7+sRW0ak8qI%ufdK!H{Cm0n7a1mv3Fh_*DE*=@Ih3fbR9WZW
zl$0%kiGmDL^bz6FzWx`6jGq3wuUK4=e{4ab#(|qVXm(h8XUtGopxXp(YI>S!zu~-1
z4&$g`;WEz<KW8P(YLBAip-X^nB~T6>qr0xqt#zUr&Q0lYNW*tRuS8}OkB$rLSJEKj
zLW0<rkB#7$<TU}9v4;e)TZ{aETR}_sZn(9+!q62wGc(4Z_taA1m8oUxE*zdc`lU`9
zBzxG!cqm4;tcNBBnPc&GDYnln27Y|=GDMUW5BZn#ZzKAmEt03>*oT}n&9WTQPw#}5
zX!9yH*(R(GpFmhOw2i&tkw{Gx1J^#CKi~o<$hX~$IvWWZ=NFQ;>3X@qb;S<`ytQoW
zLygK4KWl$Y`l@_=8A%Zofimr#WAZ*tC-6bcVgpaBPztXxgR!4Ot+C#3gPy;G+R9dz
zT&XkSh8EnErS=kk$i(`YuT;g4&M@IXs*h@YsRfLPx&zmqkD}*uW6Vyswad^@L=gVa
zmUvJz0}Y+k?p2RM&I<yn4i2DEJSH)4zxiiR5Tv;$w`h|hMK<9|G@f7e?DR8f`Q<6r
z${-4a;NkP7y>Q17q{*6NAbI}~UHC-r&xgd8!0eh2Ib2%4wjMc4Y2|deA^UEx(u&;z
zX$#U{52;UFruCj{i@8`kITUlI)xXUj*e{|s40q6wN))v7vx<4#r&uFyzhY}L2;8~B
zWFqa|9FU~mi6;ghNVvXEu}n^N3;eS-J?a<utExTI-E!ffx3hanGGP)qi1riJRh;d>
zMn|9|`Jev3X=qiyh_Ia9OETvbw4sI`{erLU=^nF+8uPd}@z2`@r&);#&f~e7&+@H@
zUgNF$PO}aCj~*8SS0oANh%lCa7DP%$>BVT1aOr(pNF78V@+Fs5W9iyKr+3eOT;YI4
z4jO|w-03WVzfw=PRde@UG>BHLQ}{TZj@{1R<p8awDGmW&;SK}B&DO_{74eaSdhc2%
zO1n@GxwA!gd0N9U<*;#7I$6MyltP%_-`Q_&zs|Lrj^pfkmFVUSzlNstI^WUaVpHfl
zIrx$h>4hi)(}uX+_=*INAM0}ZJkFM)UYDm_l$|X;^bv;L;qu-V^sdwRqDY4fTgWRq
zJ#RE2g^*KC1z|;;tz6b$_01m782yn8%pwKvQNZC7*a!>yGN^6R&~L1FD1e?PppFuX
zP@lYcTwZhMUQcdr<~hnR08!_LXs{d>H3s~4+e#b|rTMp=x=A9T99LzhAK}kcrLEYW
z>tvh<f}3TN%RkWSsY_>T3Hf#wnxovT&YX}!)Kpdk1_Prr2#A1pcR+-l6aT47{|pK&
z)2L4-UlftX%ox{I#BiPCI6feI8S9AmV8^15as{X0aX!oMr86n;+8D}^ul4Ntk3pNE
zzjV-!#V+4ML&ti{U&mhGayBTVneD|b@$8COOrV*g!I)uf>+lH&=}kt*#OI|0Pb*)+
z((cB+HtX~0ap@E<mR$rV_h6{6b>NUW9Sqn_b7iU_dl2EQ(x$s3Op-9b8kS`7^bx_n
zk2Lq4>$btf{UF%QDcUgWNJ9O2^&Aom>b5>lD596trs<9%ZAitai_-w3ZES|un+<X^
z=Nan|6L&4w`?qa<L(+D?@Qujanh`KUo2gQ+Qo^Wf0;5Yaq6Fzp-mVQObl{rbN?3CA
zIx_(ABqcAv#rg{I$;fx#(v^dZRXvPwu-S#vOWcU6p6j`dFjo7`xxa13SXcw<E%qd#
z@Z{n+J7=ZIVm!OZ(8M(uv{^)MhHf`6h8f#mkm+-fI<O=>G=sBr2hQ{>Xt_SK=Rum2
z4V&6BI1dDg1eN=r=T{rFTl6A2nc+!s0znJ#-BzO@hfU*rj1xUGCl?=_ncN)dxu7Jf
zOcxS>hKpi_xZ)u*ihC(fr;?Y32)?MoSD+iK1O_Y7i+*<^4|R!>NL6fY+hFBK({ut~
zu`%q4AW(1gm8p>BGW&~Ef>2y?3Ww9J9HcM)o2<^2TfTASo3OI9wt!`?hD%?qZ65vY
zyX+_ivDt=usE3XrZI*#1ZO9v`<HGI}l2v)uYlTGh^HGC2in}FaU#DyppwxXCYakgu
zI2Bp>R;uW643k#v^oyk0kX_UH`SVP&t(Dt~xF5yIDw?}s;`3l&jN6CiXuc)HQ4O=F
zte&T(nWvSgg}W7k0C>51ggLnpw}=)mpD4GWD31s`H@7G^H=XUx<NsoCa<R0t_Wi#Z
VoCVYnXG#EoqO7V+m6U1Ne*w3M^8^3@

literal 0
HcmV?d00001

diff --git a/src/misc/get-reaction-emoji.ts b/src/misc/get-reaction-emoji.ts
index c661205379..9d6956c4ac 100644
--- a/src/misc/get-reaction-emoji.ts
+++ b/src/misc/get-reaction-emoji.ts
@@ -8,6 +8,7 @@ export default function(reaction: string): string {
 		case 'congrats': return '🎉';
 		case 'angry': return '💢';
 		case 'confused': return '😥';
+		case 'rip': return '😇';
 		case 'pudding': return '🍮';
 		default: return '';
 	}
diff --git a/src/models/note-reaction.ts b/src/models/note-reaction.ts
index 915dc0cf91..a710fef364 100644
--- a/src/models/note-reaction.ts
+++ b/src/models/note-reaction.ts
@@ -26,6 +26,7 @@ export const validateReaction = $.str.or([
 	'congrats',
 	'angry',
 	'confused',
+	'rip',
 	'pudding'
 ]);
 

From fc94df06eb160f0a78d7e31469322fc3dd94a1ab Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 21:13:38 +0900
Subject: [PATCH 014/134] New Crowdin translations (#2157)

---
 locales/ca.yml | 1 +
 locales/de.yml | 1 +
 locales/en.yml | 1 +
 locales/es.yml | 1 +
 locales/fr.yml | 1 +
 locales/it.yml | 1 +
 locales/ko.yml | 1 +
 locales/pl.yml | 1 +
 locales/pt.yml | 1 +
 locales/ru.yml | 1 +
 locales/zh.yml | 1 +
 11 files changed, 11 insertions(+)

diff --git a/locales/ca.yml b/locales/ca.yml
index fbd955c3fd..ccdd780fb9 100644
--- a/locales/ca.yml
+++ b/locales/ca.yml
@@ -66,6 +66,7 @@ common:
     congrats: "おめでとう"
     angry: "おこ"
     confused: "こまこまのこまり"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "今どうしてる?"
diff --git a/locales/de.yml b/locales/de.yml
index e1f382aa71..ef9d585d00 100644
--- a/locales/de.yml
+++ b/locales/de.yml
@@ -66,6 +66,7 @@ common:
     congrats: "Glückwunsch!"
     angry: "Wütend"
     confused: "Verwirrt"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "Was machst du gerade?"
diff --git a/locales/en.yml b/locales/en.yml
index 351e1f4423..0072ef05d0 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -66,6 +66,7 @@ common:
     congrats: "Congrats!"
     angry: "Angry"
     confused: "Confused"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "What are you doing?"
diff --git a/locales/es.yml b/locales/es.yml
index eb039df696..3558ea05e9 100644
--- a/locales/es.yml
+++ b/locales/es.yml
@@ -66,6 +66,7 @@ common:
     congrats: "felicidades"
     angry: "enfadado"
     confused: "confundido"
+    rip: "RIP"
     pudding: "Chafado"
   note-placeholders:
     a: "¿Qué haces?"
diff --git a/locales/fr.yml b/locales/fr.yml
index 271914d460..b301447057 100644
--- a/locales/fr.yml
+++ b/locales/fr.yml
@@ -66,6 +66,7 @@ common:
     congrats: "Félicitations !"
     angry: "En colère"
     confused: "Confus"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "Que faîtes vous maintenant ?"
diff --git a/locales/it.yml b/locales/it.yml
index fbd955c3fd..ccdd780fb9 100644
--- a/locales/it.yml
+++ b/locales/it.yml
@@ -66,6 +66,7 @@ common:
     congrats: "おめでとう"
     angry: "おこ"
     confused: "こまこまのこまり"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "今どうしてる?"
diff --git a/locales/ko.yml b/locales/ko.yml
index 252027fa87..304d8f9487 100644
--- a/locales/ko.yml
+++ b/locales/ko.yml
@@ -66,6 +66,7 @@ common:
     congrats: "받으세요"
     angry: "화냈어"
     confused: "곤란하고 있어"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "지금 어떻게하고있어?"
diff --git a/locales/pl.yml b/locales/pl.yml
index 319062a9e0..f209c7fb4b 100644
--- a/locales/pl.yml
+++ b/locales/pl.yml
@@ -66,6 +66,7 @@ common:
     congrats: "Gratuluję!"
     angry: "Wściekły"
     confused: "Zmieszany"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "Co robisz?"
diff --git a/locales/pt.yml b/locales/pt.yml
index 3cc4530447..4256ca974d 100644
--- a/locales/pt.yml
+++ b/locales/pt.yml
@@ -66,6 +66,7 @@ common:
     congrats: "おめでとう"
     angry: "おこ"
     confused: "こまこまのこまり"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "今どうしてる?"
diff --git a/locales/ru.yml b/locales/ru.yml
index 7b2c240f0e..75c07e26b0 100644
--- a/locales/ru.yml
+++ b/locales/ru.yml
@@ -66,6 +66,7 @@ common:
     congrats: "おめでとう"
     angry: "おこ"
     confused: "こまこまのこまり"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "今どうしてる?"
diff --git a/locales/zh.yml b/locales/zh.yml
index d11899b159..73ca466d89 100644
--- a/locales/zh.yml
+++ b/locales/zh.yml
@@ -66,6 +66,7 @@ common:
     congrats: "おめでとう"
     angry: "おこ"
     confused: "こまこまのこまり"
+    rip: "RIP"
     pudding: "Pudding"
   note-placeholders:
     a: "今どうしてる?"

From c9ee7370784753565174d9e4d8da3b300b9df913 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 21:34:12 +0900
Subject: [PATCH 016/134] Fix bug

---
 src/services/note/create.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 4f90a19f2c..85a03fdc07 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -188,6 +188,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 		// 通知
 		if (isLocalUser(data.reply._user)) {
 			nm.push(data.reply.userId, 'reply');
+			publishUserStream(data.reply.userId, 'reply', noteObj);
 		}
 	}
 
@@ -209,7 +210,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 		}
 
 		// Publish event
-		if (!user._id.equals(data.renote.userId)) {
+		if (!user._id.equals(data.renote.userId) && isLocalUser(data.renote._user)) {
 			publishUserStream(data.renote.userId, 'renote', noteObj);
 		}
 	}

From c8c4ec6ad4652cdb2502daffe0db0bc4e41218ca Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Aug 2018 21:35:22 +0900
Subject: [PATCH 017/134] 5.22.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index f7cd4fed3b..6fb22b91c5 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.21.0",
-	"clientVersion": "1.0.8117",
+	"version": "5.22.0",
+	"clientVersion": "1.0.8127",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From 4ee6d0b5497ffcb2fda91e7f45091eac24e37943 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Sun, 12 Aug 2018 00:01:07 +0900
Subject: [PATCH 018/134] Refactoring (#2160)

---
 src/db/mongodb.ts | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts
index 034473b0d3..f82ced1765 100644
--- a/src/db/mongodb.ts
+++ b/src/db/mongodb.ts
@@ -3,9 +3,7 @@ import config from '../config';
 const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null;
 const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null;
 
-const uri = u && p
-	? `mongodb://${u}:${p}@${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`
-	: `mongodb://${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
+const uri = `mongodb://${u && p ? `${u}:${p}@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
 
 /**
  * monk

From 3a77d871d59fabfbb4f06da832b29454776e0850 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Sun, 12 Aug 2018 00:29:17 +0900
Subject: [PATCH 019/134] Stop supporting docker

---
 docker/Dockerfile | 26 --------------------------
 docker/misskey.sh |  6 ------
 docs/docker.en.md | 29 -----------------------------
 3 files changed, 61 deletions(-)
 delete mode 100644 docker/Dockerfile
 delete mode 100644 docker/misskey.sh
 delete mode 100644 docs/docker.en.md

diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index f089c02545..0000000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,26 +0,0 @@
-FROM base/archlinux
-
-MAINTAINER Aya Morisawa
-
-RUN rm /etc/pacman.d/mirrorlist
-RUN echo 'Server = http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/$repo/os/$arch'    >> /etc/pacman.d/mirrorlist
-RUN echo 'Server = http://ftp.tsukuba.wide.ad.jp/Linux/archlinux/$repo/os/$arch' >> /etc/pacman.d/mirrorlist
-
-RUN rm /etc/localtime
-RUN ln -s /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
-
-RUN pacman -Sy --noconfirm
-RUN pacman -S --noconfirm pacman
-RUN pacman-db-upgrade
-RUN pacman -S --noconfirm archlinux-keyring
-RUN pacman -Syyu --noconfirm
-RUN pacman -S --noconfirm git nodejs npm mongodb redis
-
-COPY misskey.sh /root/misskey.sh
-RUN chmod u+x /root/misskey.sh
-
-EXPOSE 80
-EXPOSE 443
-EXPOSE 27017
-
-CMD ["/root/misskey.sh"]
diff --git a/docker/misskey.sh b/docker/misskey.sh
deleted file mode 100644
index 82291e3a97..0000000000
--- a/docker/misskey.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-redis-server --daemonize yes
-mongod > /dev/null &
-cd /root/misskey
-npm start
-tail -f /dev/null
diff --git a/docs/docker.en.md b/docs/docker.en.md
deleted file mode 100644
index 591ad86607..0000000000
--- a/docs/docker.en.md
+++ /dev/null
@@ -1,29 +0,0 @@
-Setup with Docker :whale:
-================================================================
-
-Ensure that the working directory is the repository root directory.
-
-To create misskey image:
-``` console
-$ sudo docker build -t misskey ./docker
-```
-
-To run misskey:
-``` console
-$ sudo docker run --rm -i -t -p $PORT:80 -v $(pwd):/root/misskey -v $DBPATH:/data/db misskey
-```
-
-where `$PORT` is the port used to access Misskey Web from host browser
-and `$DBPATH` is the path of MongoDB database on the host for data persistence.
-
-ex:
-``` console
-$ sudo docker run --rm -i -t -p 80:80 -v $(pwd):/root/misskey -v /data/db:/data/db misskey
-```
-
-If you want to run misskey in production mode, add `--env NODE_ENV=production` like this:
-``` console
-$ sudo docker run --rm -i -t -p 80:80 -v $(pwd):/root/misskey -v /data/db:/data/db --env NODE_ENV=production misskey
-```
-
-Note that `$(pwd)` is the working directory.

From 7c763600b7a5ee4ed9b414929bffba74a4901a19 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 12 Aug 2018 03:09:22 +0900
Subject: [PATCH 020/134] Fix #2162

---
 src/models/drive-file.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index ad5496d7ca..38e1bf549c 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -10,7 +10,7 @@ import DriveFileThumbnail, { deleteDriveFileThumbnail } from './drive-file-thumb
 
 const DriveFile = monkDb.get<IDriveFile>('driveFiles.files');
 DriveFile.createIndex('md5');
-DriveFile.createIndex(['metadata.uri', 'metadata.userId'], { sparse: true, unique: true });
+DriveFile.createIndex('metadata.uri');
 export default DriveFile;
 
 export const DriveFileChunk = monkDb.get('driveFiles.chunks');

From 8ccbabf5cad2dc34c77c9e71e008d6b021325fdd Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 12 Aug 2018 03:09:45 +0900
Subject: [PATCH 021/134] 5.22.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 6fb22b91c5..eb6a65d1e0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.22.0",
+	"version": "5.22.1",
 	"clientVersion": "1.0.8127",
 	"codename": "nighthike",
 	"main": "./built/index.js",

From f56ec82f6b1ea5bca06be1d094a3785bcc1f1963 Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Sun, 12 Aug 2018 04:27:32 +0900
Subject: [PATCH 022/134] Fix ActivityPub attachment url

---
 src/remote/activitypub/renderer/document.ts | 2 +-
 src/remote/activitypub/renderer/image.ts    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/remote/activitypub/renderer/document.ts b/src/remote/activitypub/renderer/document.ts
index 1985c6bc8b..1f4543d7ee 100644
--- a/src/remote/activitypub/renderer/document.ts
+++ b/src/remote/activitypub/renderer/document.ts
@@ -4,5 +4,5 @@ import { IDriveFile } from '../../../models/drive-file';
 export default (file: IDriveFile) => ({
 	type: 'Document',
 	mediaType: file.contentType,
-	url: `${config.drive_url}/${file._id}`
+	url: file.metadata.url || `${config.drive_url}/${file._id}`
 });
diff --git a/src/remote/activitypub/renderer/image.ts b/src/remote/activitypub/renderer/image.ts
index 69bddd9188..b2f2555003 100644
--- a/src/remote/activitypub/renderer/image.ts
+++ b/src/remote/activitypub/renderer/image.ts
@@ -3,6 +3,6 @@ import { IDriveFile } from '../../../models/drive-file';
 
 export default (file: IDriveFile) => ({
 	type: 'Image',
-	url: `${config.drive_url}/${file._id}`,
+	url: file.metadata.url || `${config.drive_url}/${file._id}`,
 	sensitive: file.metadata.isSensitive
 });

From 1f28a0dfebd8ba2bf41e952e215b182ac917ab38 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Sun, 12 Aug 2018 05:19:32 +0900
Subject: [PATCH 023/134] Improve nya

---
 src/models/note.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/models/note.ts b/src/models/note.ts
index af6f6f7c01..9d2e23d901 100644
--- a/src/models/note.ts
+++ b/src/models/note.ts
@@ -340,7 +340,7 @@ export const pack = async (
 	_note = await rap(_note);
 
 	if (_note.user.isCat && _note.text) {
-		_note.text = _note.text.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ');
+		_note.text = _note.text.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ');
 	}
 
 	if (hide) {

From 765b922a8b85a435f41ba4bdd0d20b9d4847bbd3 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Sat, 11 Aug 2018 22:14:05 +0000
Subject: [PATCH 024/134] fix(package): update ts-node to version 7.0.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index eb6a65d1e0..474bbcac48 100644
--- a/package.json
+++ b/package.json
@@ -193,7 +193,7 @@
 		"textarea-caret": "3.1.0",
 		"tmp": "0.0.33",
 		"ts-loader": "4.4.1",
-		"ts-node": "7.0.0",
+		"ts-node": "7.0.1",
 		"tslint": "5.10.0",
 		"typescript": "2.9.2",
 		"typescript-eslint-parser": "18.0.0",

From c5073b33ef3cf29c2e1ff2ab1054082b917fa258 Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Sun, 12 Aug 2018 18:56:29 +0900
Subject: [PATCH 025/134] Fix ActivityPub followers/specified detection

---
 src/remote/activitypub/models/note.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts
index 85a8f89bc8..dbad63ea42 100644
--- a/src/remote/activitypub/models/note.ts
+++ b/src/remote/activitypub/models/note.ts
@@ -69,12 +69,13 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 	if (!note.to.includes('https://www.w3.org/ns/activitystreams#Public')) {
 		if (note.cc.includes('https://www.w3.org/ns/activitystreams#Public')) {
 			visibility = 'home';
+		} else if (note.to.includes(`${actor.uri}/followers`)) {	// TODO: person.followerと照合するべき?
+			visibility = 'followers';
 		} else {
 			visibility = 'specified';
 			visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri)));
 		}
 	}
-	if (note.cc.length == 0) visibility = 'followers';
 	//#endergion
 
 	// 添付メディア

From 7f9a88fd1c057d0cb925383ac0d2b92ab1f9b5e5 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Sun, 12 Aug 2018 13:50:20 +0000
Subject: [PATCH 026/134] fix(package): update vue-js-modal to version 1.3.17

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 474bbcac48..315f455061 100644
--- a/package.json
+++ b/package.json
@@ -203,7 +203,7 @@
 		"v-animate-css": "0.0.2",
 		"vue": "2.5.17",
 		"vue-cropperjs": "2.2.1",
-		"vue-js-modal": "1.3.16",
+		"vue-js-modal": "1.3.17",
 		"vue-json-tree-view": "2.1.4",
 		"vue-loader": "15.3.0",
 		"vue-router": "3.0.1",

From 92befbb4cc2af8503019799f17881bc81873eb0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 00:16:39 +0900
Subject: [PATCH 027/134] Fix #2177

Resolves #2177
---
 .../app/desktop/views/components/ui.header.vue     | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/client/app/desktop/views/components/ui.header.vue b/src/client/app/desktop/views/components/ui.header.vue
index 83df4b2fbc..c1f9a45f51 100644
--- a/src/client/app/desktop/views/components/ui.header.vue
+++ b/src/client/app/desktop/views/components/ui.header.vue
@@ -155,6 +155,10 @@ root(isDark)
 				max-width 1300px
 				margin 0 auto
 
+				> *
+					position absolute
+					height 48px
+
 				> .center
 					margin auto
 
@@ -169,11 +173,13 @@ root(isDark)
 						opacity 0.3
 						cursor pointer
 
-				> .left
-					height 48px
+				> .left,
+				> .center
+					left 0
 
-				> .right
-					height 48px
+				> .right,
+				> .center
+					right 0
 
 					> *
 						display inline-block

From 6ddb6bc160bc837dd3feaeac440544b50f9bdc58 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Mon, 13 Aug 2018 00:12:30 +0900
Subject: [PATCH 028/134] Add .vsls.json

---
 .vsls.json | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 .vsls.json

diff --git a/.vsls.json b/.vsls.json
new file mode 100644
index 0000000000..3fff862442
--- /dev/null
+++ b/.vsls.json
@@ -0,0 +1,4 @@
+{
+	"$schema": "http://json.schemastore.org/vsls",
+	"gitignore": "exclude"
+}

From 2cb39a88824bc60c7b6b47985d3475a380ba8645 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 13 Aug 2018 00:59:36 +0900
Subject: [PATCH 029/134] Fix #2097

---
 src/client/app/desktop/views/components/drive.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/client/app/desktop/views/components/drive.vue b/src/client/app/desktop/views/components/drive.vue
index 6bad7c78a2..d919e4a5ea 100644
--- a/src/client/app/desktop/views/components/drive.vue
+++ b/src/client/app/desktop/views/components/drive.vue
@@ -567,6 +567,7 @@ export default Vue.extend({
 			// ファイル一覧取得
 			(this as any).api('drive/files', {
 				folderId: this.folder ? this.folder.id : null,
+				untilId: this.files[this.files.length - 1].id,
 				limit: max + 1
 			}).then(files => {
 				if (files.length == max + 1) {

From 01f28b21dd9db4cffac18da0135dbd2f68ca39a0 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Mon, 13 Aug 2018 01:25:50 +0900
Subject: [PATCH 030/134] #1211

---
 locales/ja.yml                                              | 3 +++
 .../common/views/components/games/reversi/reversi.game.vue  | 6 ++++++
 2 files changed, 9 insertions(+)

diff --git a/locales/ja.yml b/locales/ja.yml
index 98397212d4..15f3d936de 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -182,6 +182,9 @@ common/views/components/games/reversi/reversi.vue:
 common/views/components/games/reversi/reversi.game.vue:
   surrender: "投了"
   surrendered: "投了により"
+  is-llotheo: "石の少ない方が勝ち(ロセオ)"
+  looped-map: "ループマップ"
+  can-put-everywhere: "どこでも置けるモード"
 
 common/views/components/games/reversi/reversi.index.vue:
   title: "Misskey Reversi"
diff --git a/src/client/app/common/views/components/games/reversi/reversi.game.vue b/src/client/app/common/views/components/games/reversi/reversi.game.vue
index d1809d741f..389934af97 100644
--- a/src/client/app/common/views/components/games/reversi/reversi.game.vue
+++ b/src/client/app/common/views/components/games/reversi/reversi.game.vue
@@ -60,6 +60,12 @@
 			<el-button type="primary" @click="logPos = logs.length" :disabled="logPos == logs.length">%fa:angle-double-right%</el-button>
 		</el-button-group>
 	</div>
+
+	<div class="info">
+		<p v-if="game.settings.isLlotheo">%i18n:@is-llotheo%</p>
+		<p v-if="game.settings.loopedBoard">%i18n:@looped-map%</p>
+		<p v-if="game.settings.canPutEverywhere">%i18n:@can-put-everywhere%</p>
+	</div>
 </div>
 </template>
 

From 4da8cc478f287a36c9017dc32c5103b45fbc0c91 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 01:58:15 +0900
Subject: [PATCH 031/134] Update ui.header.vue

---
 src/client/app/desktop/views/components/ui.header.vue | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/client/app/desktop/views/components/ui.header.vue b/src/client/app/desktop/views/components/ui.header.vue
index c1f9a45f51..91b51a457b 100644
--- a/src/client/app/desktop/views/components/ui.header.vue
+++ b/src/client/app/desktop/views/components/ui.header.vue
@@ -159,10 +159,8 @@ root(isDark)
 					position absolute
 					height 48px
 
-				> .center
-					margin auto
-
 					> .icon
+						margin auto
 						display block
 						width 48px
 						height 48px

From 8384efc8c74a00b35ccca946d8ec4ca69d57e4d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 03:23:36 +0900
Subject: [PATCH 032/134] Create whitelist

---
 .../common/views/components/url-preview.vue   | 77 ++++++++++++++++++-
 1 file changed, 76 insertions(+), 1 deletion(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 57b441a328..50194b69b7 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -83,8 +83,83 @@ export default Vue.extend({
 					this.thumbnail = info.thumbnail;
 					this.icon = info.icon;
 					this.sitename = info.sitename;
-					this.player = info.player;
 					this.fetching = false;
+					if ([ // THIS IS THE WHITELIST FOR THE EMBED PLAYER
+						'afreecatv.com',
+						'aparat.com',
+						'applemusic.com',
+						'amazon.com',
+						'awa.fm',
+						'bandcamp.com',
+						'bbc.co.uk',
+						'beatport.com',
+						'bilibili.com',
+						'boomstream.com',
+						'breakers.tv',
+						'cam4.com',
+						'cavelis.net',
+						'chaturbate.com',
+						'cnn.com',
+						'cybergame.tv',
+						'dailymotion.com',
+						'deezer.com',
+						'djlive.pl',
+						'e-onkyo.com',
+						'eventials.com',
+						'facebook.com',
+						'fc2.com',
+						'gameplank.tv',
+						'goodgame.ru',
+						'google.com',
+						'hardtunes.com',
+						'instagram.com',
+						'johnnylooch.com',
+						'kexp.org',
+						'lahzenegar.com',
+						'liveedu.tv',
+						'livetube.cc',
+						'livestream.com',
+						'meridix.com',
+						'mixcloud.com',
+						'mixer.com',
+						'mobcrush.com',
+						'mylive.in.th',
+						'myspace.com',
+						'netflix.com',
+						'newretrowave.com',
+						'nhk.or.jp',
+						'nicovideo.jp',
+						'noisetrade.com',
+						'nood.tv',
+						'npr.org',
+						'openrec.tv',
+						'pandora.com',
+						'pandora.tv',
+						'picarto.tv',
+						'pscp.tv',
+						'restream.io',
+						'reverbnation.com',
+						'sermonaudio.com',
+						'smashcast.tv',
+						'songkick.com',
+						'soundcloud.com',
+						'spinninrecords.com',
+						'stitcher.com',
+						'stream.me',
+						'switchboard.live',
+						'tunein.com',
+						'twitcasting.tv',
+						'twitch.tv',
+						'twitter.com',
+						'vaughnlive.tv',
+						'veoh.com',
+						'vimeo.com',
+						'watchpeoplecode.com',
+						'web.tv',
+						'youtube.com',
+						'youtu.be'
+					].some(x => x == url.hostname || url.hostname.endsWith(`.${x}`))))
+						this.player = info.player;
 				}
 			});
 		}

From 80d343bb0ba2ee8299dd194e5b95fbf55664c540 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 03:36:42 +0900
Subject: [PATCH 033/134] Update avatar.vue

---
 .../app/common/views/components/avatar.vue    | 50 ++++++++++++++++---
 1 file changed, 43 insertions(+), 7 deletions(-)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 6685296c16..5339c8999f 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -1,8 +1,16 @@
 <template>
-	<span class="mk-avatar" :title="user | acct" :style="style" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick"></span>
-	<span class="mk-avatar" :title="user | acct" :style="style" v-else-if="disableLink && disablePreview" @click="onClick"></span>
-	<router-link class="mk-avatar" :to="user | userPage" :title="user | acct" :target="target" :style="style" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id"></router-link>
-	<router-link class="mk-avatar" :to="user | userPage" :title="user | acct" :target="target" :style="style" v-else-if="!disableLink && disablePreview"></router-link>
+	<span :class="{ mk-avatar: true, cat: cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
+		<span class="inner" :style="style"></span>
+	</span>
+	<span :class="{ mk-avatar: true, cat: cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
+		<span class="inner" :style="style"></span>
+	</span>
+	<router-link :class="{ mk-avatar: true, cat: cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
+		<span class="inner" :style="style"></span>
+	</router-link>
+	<router-link :class="{ mk-avatar: true, cat: cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
+		<span class="inner" :style="style"></span>
+	</router-link>
 </template>
 
 <script lang="ts">
@@ -30,6 +38,9 @@ export default Vue.extend({
 		lightmode(): boolean {
 			return this.$store.state.device.lightmode;
 		},
+		cat(): boolean {
+			return this.user.isCat && this.$store.state.settings.circleIcons;
+		}
 		style(): any {
 			return {
 				backgroundColor: this.lightmode
@@ -54,7 +65,32 @@ export default Vue.extend({
 .mk-avatar
 	display inline-block
 	vertical-align bottom
-	background-size cover
-	background-position center center
-	transition border-radius 1s ease
+
+	&::before,
+	&::after
+		background #df548f
+		border solid 4px #ffffff
+		box-sizing border-box
+		content ''
+		display inline-block
+		height 50%
+		width 50%
+
+	&::before
+		border-radius 0 75% 75%
+		transform rotate(37.5deg) skew(30deg)
+
+	&::after
+		border-radius 75% 0 75% 75%
+		transform rotate(-37.5deg) skew(-30deg)
+
+	.inner
+		background-position center center
+		background-size cover
+		bottom 0
+		left 0
+		position absolute
+		right 0
+		top 0
+		transition border-radius 1s ease
 </style>

From 70f927ea43c4cbe1ff20a9a89474207e3619b092 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 03:39:32 +0900
Subject: [PATCH 034/134] Update avatar.vue

---
 src/client/app/common/views/components/avatar.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 5339c8999f..133276b266 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -66,8 +66,8 @@ export default Vue.extend({
 	display inline-block
 	vertical-align bottom
 
-	&::before,
-	&::after
+	&.cat::before,
+	&.cat::after
 		background #df548f
 		border solid 4px #ffffff
 		box-sizing border-box
@@ -76,11 +76,11 @@ export default Vue.extend({
 		height 50%
 		width 50%
 
-	&::before
+	&.cat::before
 		border-radius 0 75% 75%
 		transform rotate(37.5deg) skew(30deg)
 
-	&::after
+	&.cat::after
 		border-radius 75% 0 75% 75%
 		transform rotate(-37.5deg) skew(-30deg)
 

From e1f460f90fe0da3160fa505717a7890a8cd37d2b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 03:40:50 +0900
Subject: [PATCH 035/134] Update user.header.vue

---
 src/client/app/desktop/views/pages/user/user.header.vue | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/client/app/desktop/views/pages/user/user.header.vue b/src/client/app/desktop/views/pages/user/user.header.vue
index 00545723e2..d8f4656ed0 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -176,6 +176,10 @@ root(isDark)
 		height 120px
 		box-shadow 1px 1px 3px rgba(#000, 0.2)
 
+		> &.cat::before,
+		> &.cat::after
+			border-width 8px
+
 	> .body
 		padding 16px 16px 16px 154px
 		color isDark ? #c5ced6 : #555

From 2913c7ccfbb153eba6054672958c571e8c0ad969 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 03:42:12 +0900
Subject: [PATCH 036/134] Update avatar.vue

---
 src/client/app/common/views/components/avatar.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 133276b266..097bea8436 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -93,4 +93,5 @@ export default Vue.extend({
 		right 0
 		top 0
 		transition border-radius 1s ease
+		z-index 1
 </style>

From 4edd9efc0b76c394b9a781a30f1c5e85b2294eb0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 03:47:56 +0900
Subject: [PATCH 037/134] Update avatar.vue

refs: https://github.com/syuilo/misskey/pull/2182#discussion_r209464350
---
 src/client/app/common/views/components/avatar.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 097bea8436..d59c486f16 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -40,7 +40,7 @@ export default Vue.extend({
 		},
 		cat(): boolean {
 			return this.user.isCat && this.$store.state.settings.circleIcons;
-		}
+		},
 		style(): any {
 			return {
 				backgroundColor: this.lightmode

From f079041827b97849b3b5907980672686091277b4 Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Mon, 13 Aug 2018 03:49:17 +0900
Subject: [PATCH 038/134] ActivityPub visibility on send

---
 src/remote/activitypub/renderer/note.ts   | 20 ++++++++---
 src/remote/activitypub/renderer/person.ts |  2 ++
 src/server/activitypub.ts                 | 42 +++++++++++++++++++++++
 3 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts
index 7cee2be22c..209e743927 100644
--- a/src/remote/activitypub/renderer/note.ts
+++ b/src/remote/activitypub/renderer/note.ts
@@ -50,9 +50,21 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
 		? note.mentionedRemoteUsers.map(x => x.uri)
 		: [];
 
-	const cc = ['public', 'home', 'followers'].includes(note.visibility)
-		? [`${attributedTo}/followers`].concat(mentions)
-		: [];
+	let to: string[] = [];
+	let cc: string[] = [];
+
+	if (note.visibility == 'public') {
+		to = ['https://www.w3.org/ns/activitystreams#Public'];
+		cc = [`${attributedTo}/followers`].concat(mentions);
+	} else if (note.visibility == 'home') {
+		to = [`${attributedTo}/followers`];
+		cc = ['https://www.w3.org/ns/activitystreams#Public'].concat(mentions);
+	} else if (note.visibility == 'followers') {
+		to = [`${attributedTo}/followers`];
+		cc = mentions;
+	} else {
+		to = mentions;
+	}
 
 	const mentionedUsers = note.mentions ? await User.find({
 		_id: {
@@ -74,7 +86,7 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
 		summary: note.cw,
 		content: toHtml(note),
 		published: note.createdAt.toISOString(),
-		to: 'https://www.w3.org/ns/activitystreams#Public',
+		to,
 		cc,
 		inReplyTo,
 		attachment: (await promisedFiles).map(renderDocument),
diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts
index 7d828f97ae..0d227303c0 100644
--- a/src/remote/activitypub/renderer/person.ts
+++ b/src/remote/activitypub/renderer/person.ts
@@ -19,6 +19,8 @@ export default async (user: ILocalUser) => {
 		id,
 		inbox: `${id}/inbox`,
 		outbox: `${id}/outbox`,
+		followers: `${id}/followers`,
+		following: `${id}/following`,
 		sharedInbox: `${config.url}/inbox`,
 		url: `${config.url}/@${user.username}`,
 		preferredUsername: user.username,
diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts
index 2d9a4746c6..7d6fe09269 100644
--- a/src/server/activitypub.ts
+++ b/src/server/activitypub.ts
@@ -89,6 +89,48 @@ router.get('/users/:user/outbox', async ctx => {
 	ctx.body = pack(rendered);
 });
 
+// followers
+router.get('/users/:user/followers', async ctx => {
+	const userId = new mongo.ObjectID(ctx.params.user);
+
+	const user = await User.findOne({
+		_id: userId,
+		host: null
+	});
+
+	if (user === null) {
+		ctx.status = 404;
+		return;
+	}
+
+	// TODO: Implement fetch and render
+
+	const rendered = renderOrderedCollection(`${config.url}/users/${userId}/followers`, 0, []);
+
+	ctx.body = pack(rendered);
+});
+
+// following
+router.get('/users/:user/following', async ctx => {
+	const userId = new mongo.ObjectID(ctx.params.user);
+
+	const user = await User.findOne({
+		_id: userId,
+		host: null
+	});
+
+	if (user === null) {
+		ctx.status = 404;
+		return;
+	}
+
+	// TODO: Implement fetch and render
+
+	const rendered = renderOrderedCollection(`${config.url}/users/${userId}/following`, 0, []);
+
+	ctx.body = pack(rendered);
+});
+
 // publickey
 router.get('/users/:user/publickey', async ctx => {
 	const userId = new mongo.ObjectID(ctx.params.user);

From 979efee412c795140b22613122d1334b2938239b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 04:41:12 +0900
Subject: [PATCH 039/134] Update url-preview.vue

---
 src/client/app/common/views/components/url-preview.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 50194b69b7..4f3b3ae197 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -1,5 +1,5 @@
 <template>
-<iframe v-if="player" :src="player" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
+<iframe v-if="player" :src="player" heigth="250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen />
 <div v-else-if="tweetUrl && detail" class="twitter">
 	<blockquote ref="tweet" class="twitter-tweet" :data-theme="$store.state.device.darkmode ? 'dark' : null">
 		<a :href="url"></a>

From fda1ab3e05823a5ee61125636d76e06e2778938a Mon Sep 17 00:00:00 2001
From: Tosuke <tasukeprg@gmail.com>
Date: Mon, 13 Aug 2018 12:30:32 +0900
Subject: [PATCH 040/134] Create apps without authentication(#2025)

---
 package.json                           | 1 +
 src/models/app.ts                      | 2 +-
 src/server/api/endpoints/app/create.ts | 4 ++--
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/package.json b/package.json
index 315f455061..7424a0cecf 100644
--- a/package.json
+++ b/package.json
@@ -145,6 +145,7 @@
 		"koa-slow": "2.1.0",
 		"koa-views": "6.1.4",
 		"loader-utils": "1.1.0",
+		"lodash.assign": "4.2.0",
 		"mecab-async": "0.1.2",
 		"minio": "6.0.0",
 		"mkdirp": "0.5.1",
diff --git a/src/models/app.ts b/src/models/app.ts
index 8dc7fe01d9..01cc946c6e 100644
--- a/src/models/app.ts
+++ b/src/models/app.ts
@@ -13,7 +13,7 @@ export default App;
 export type IApp = {
 	_id: mongo.ObjectID;
 	createdAt: Date;
-	userId: mongo.ObjectID;
+	userId: mongo.ObjectID | null;
 	secret: string;
 	name: string;
 	nameId: string;
diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts
index 5df8bd2f25..b2a5fb73c1 100644
--- a/src/server/api/endpoints/app/create.ts
+++ b/src/server/api/endpoints/app/create.ts
@@ -4,7 +4,7 @@ import App, { isValidNameId, pack } from '../../../../models/app';
 import { ILocalUser } from '../../../../models/user';
 
 export const meta = {
-	requireCredential: true
+	requireCredential: false
 };
 
 /**
@@ -38,7 +38,7 @@ export default async (params: any, user: ILocalUser) => new Promise(async (res,
 	// Create account
 	const app = await App.insert({
 		createdAt: new Date(),
-		userId: user._id,
+		userId: user && user._id,
 		name: name,
 		nameId: nameId,
 		nameIdLower: nameId.toLowerCase(),

From 33469ff87a525366e2b3a7099fada8aea1adb82b Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Mon, 13 Aug 2018 22:08:59 +0900
Subject: [PATCH 041/134] Hide Unimplemented button

---
 .../app/desktop/views/components/drive.file.vue      | 12 ++++++------
 .../app/desktop/views/components/drive.folder.vue    | 12 ++++++------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/client/app/desktop/views/components/drive.file.vue b/src/client/app/desktop/views/components/drive.file.vue
index 6541a8f21f..55218625c1 100644
--- a/src/client/app/desktop/views/components/drive.file.vue
+++ b/src/client/app/desktop/views/components/drive.file.vue
@@ -99,7 +99,7 @@ export default Vue.extend({
 					text: '%i18n:@contextmenu.set-as-banner%',
 					action: this.setAsBanner
 				}]
-			}, {
+			}, /*{
 				type: 'nest',
 				text: '%i18n:@contextmenu.open-in-app%',
 				menu: [{
@@ -107,11 +107,11 @@ export default Vue.extend({
 					text: '%i18n:@contextmenu.add-app%...',
 					action: this.addApp
 				}]
-			}], {
-				closed: () => {
-					this.isContextmenuShowing = false;
-				}
-			});
+			}*/], {
+					closed: () => {
+						this.isContextmenuShowing = false;
+					}
+				});
 		},
 
 		onDragstart(e) {
diff --git a/src/client/app/desktop/views/components/drive.folder.vue b/src/client/app/desktop/views/components/drive.folder.vue
index e8077f9e3d..83880fef5c 100644
--- a/src/client/app/desktop/views/components/drive.folder.vue
+++ b/src/client/app/desktop/views/components/drive.folder.vue
@@ -67,16 +67,16 @@ export default Vue.extend({
 				text: '%i18n:@contextmenu.rename%',
 				icon: '%fa:i-cursor%',
 				action: this.rename
-			}, null, {
+			}/*, null, {
 				type: 'item',
 				text: '%i18n:common.delete%',
 				icon: '%fa:R trash-alt%',
 				action: this.deleteFolder
-			}], {
-				closed: () => {
-					this.isContextmenuShowing = false;
-				}
-			});
+			}*/], {
+					closed: () => {
+						this.isContextmenuShowing = false;
+					}
+				});
 		},
 
 		onMouseover() {

From f15878cc6f0d274dbe6183a84a103fce022b6a73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Mon, 13 Aug 2018 22:49:32 +0900
Subject: [PATCH 042/134] Update avatar.vue

refs: https://github.com/syuilo/misskey/pull/2182#discussion_r209609541
---
 src/client/app/common/views/components/avatar.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index d59c486f16..77d4d5492a 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -1,14 +1,14 @@
 <template>
-	<span :class="{ mk-avatar: true, cat: cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
+	<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
 		<span class="inner" :style="style"></span>
 	</span>
-	<span :class="{ mk-avatar: true, cat: cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
+	<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
 		<span class="inner" :style="style"></span>
 	</span>
-	<router-link :class="{ mk-avatar: true, cat: cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
+	<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
 		<span class="inner" :style="style"></span>
 	</router-link>
-	<router-link :class="{ mk-avatar: true, cat: cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
+	<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
 		<span class="inner" :style="style"></span>
 	</router-link>
 </template>

From 9021bb5694bbe31cbff09d25d5d610fef0cfdda9 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 01:05:58 +0900
Subject: [PATCH 043/134] wip

---
 locales/ja.yml                                | 18 ++++++++
 .../views/pages/admin/admin.dashboard.vue     | 27 +++++++++++
 .../views/pages/admin/admin.suspend-user.vue  | 39 ++++++++++++++++
 .../app/desktop/views/pages/admin/admin.vue   | 35 ++++++++++++++
 src/server/api/call.ts                        |  8 +++-
 src/server/api/endpoints.ts                   |  5 ++
 .../api/endpoints/admin/suspend-user.ts       | 46 +++++++++++++++++++
 7 files changed, 176 insertions(+), 2 deletions(-)
 create mode 100644 src/client/app/desktop/views/pages/admin/admin.dashboard.vue
 create mode 100644 src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
 create mode 100644 src/client/app/desktop/views/pages/admin/admin.vue
 create mode 100644 src/server/api/endpoints/admin/suspend-user.ts

diff --git a/locales/ja.yml b/locales/ja.yml
index 15f3d936de..580ade0ed3 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -897,6 +897,24 @@ desktop/views/components/window.vue:
   popout: "ポップアウト"
   close: "閉じる"
 
+desktop/views/pages/admin/admin.vue:
+  dashboard: "ダッシュボード"
+  drive: "ドライブ"
+  users: "ユーザー"
+  update: "更新"
+
+desktop/views/paages/admin/admin.dashboard.vue:
+  dashboard: "ダッシュボード"
+  all-users: "全てのユーザー"
+  original-users: "このインスタンスのユーザー"
+  all-notes: "全てのノート"
+  original-notes: "このインスタンスのノート"
+
+desktop/views/pages/admin/admin.suspend-user.vue:
+  suspend-user: "ユーザーの凍結"
+  suspend: "凍結"
+  suspended: "凍結しました"
+
 desktop/views/pages/deck/deck.tl-column.vue:
   is-media-only: "メディア投稿のみ"
   is-media-view: "メディアビュー"
diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
new file mode 100644
index 0000000000..ec43b93840
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -0,0 +1,27 @@
+<template>
+<div>
+	<header>%i18n:@dashboard%</header>
+
+	<p><b>%i18n:@all-users%</b><span>{ stats.usersCount | number }</span></p>
+	<p><b>%i18n:@original-users%</b><span>{ stats.originalUsersCount | number }</span></p>
+	<p><b>%i18n:@all-notes%</b><span>{ stats.notesCount | number }</span></p>
+	<p><b>%i18n:@original-notes%</b><span>{ stats.originalNotesCount | number }</span></p>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from "vue";
+
+export default Vue.extend({
+	data() {
+		return {
+			stats: null
+		};
+	},
+	created() {
+		(this as any).api('stats').then(stats => {
+			this.stats = stats;
+		});
+	}
+});
+</script>
diff --git a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
new file mode 100644
index 0000000000..d47a4795ee
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
@@ -0,0 +1,39 @@
+<template>
+<div>
+	<header>%i18n:@suspend-user%</header>
+	<input v-model="username"/>
+	<button @click="suspendUser" :disabled="suspending">%i18n:@suspend%</button>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from "vue";
+import parseAcct from "../../../../../../misc/acct/parse";
+
+export default Vue.extend({
+  data() {
+    return {
+      username: null,
+      suspending: false
+    };
+  },
+  methods: {
+    async suspendUser() {
+      this.suspending = true;
+
+      const user = await (this as any).os.api(
+        "users/show",
+        parseAcct(this.username)
+      );
+
+      await (this as any).os.api("admin/suspend-user", {
+        userId: user.id
+      });
+
+      this.suspending = false;
+
+      (this as any).os.apis.dialog("%i18n:@suspended%");
+    }
+  }
+});
+</script>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
new file mode 100644
index 0000000000..03a356c4a0
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -0,0 +1,35 @@
+<template>
+<div>
+	<nav>
+		<ul>
+			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%i18n:@dashborad%</li>
+			<li @click="nav('drive')" :class="{ active: page == 'drive' }">%i18n:@drive%</li>
+			<li @click="nav('users')" :class="{ active: page == 'users' }">%i18n:@users%</li>
+			<li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li>
+		</ul>
+	</nav>
+	<main>
+		<div v-if="page == 'dashboard'">
+			<x-dashboard/>
+		</div>
+		<div v-if="page == 'drive'"></div>
+		<div v-if="page == 'users'">
+			<x-suspend-user/>
+		</div>
+		<div v-if="page == 'update'"></div>
+	</main>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from "vue";
+import XDashboard from "./admin.dashboard.vue";
+import XSuspendUser from "./admin.suspend-user.vue";
+
+export default Vue.extend({
+  components: {
+		XDashboard,
+    XSuspendUser
+  }
+});
+</script>
diff --git a/src/server/api/call.ts b/src/server/api/call.ts
index 1d0e858762..e4bb30b695 100644
--- a/src/server/api/call.ts
+++ b/src/server/api/call.ts
@@ -1,6 +1,6 @@
 import { performance } from 'perf_hooks';
 import limitter from './limitter';
-import { IUser } from '../../models/user';
+import { IUser, isLocalUser } from '../../models/user';
 import { IApp } from '../../models/app';
 import endpoints from './endpoints';
 
@@ -21,6 +21,10 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
 		return rej('YOUR_ACCOUNT_HAS_BEEN_SUSPENDED');
 	}
 
+	if (ep.meta.requireAdmin && !(isLocalUser(user) && user.isAdmin)) {
+		return rej('YOU_ARE_NOT_ADMIN');
+	}
+
 	if (app && ep.meta.kind) {
 		if (!app.permission.some(p => p === ep.meta.kind)) {
 			return rej('PERMISSION_DENIED');
@@ -53,7 +57,7 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
 		const time = after - before;
 
 		if (time > 1000) {
-			console.warn(`SLOW API CALL DETECTED: ${ep.name} (${ time }ms)`);
+			console.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`);
 		}
 	} catch (e) {
 		rej(e);
diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts
index 332a051ae1..d4a44070e6 100644
--- a/src/server/api/endpoints.ts
+++ b/src/server/api/endpoints.ts
@@ -14,6 +14,11 @@ export interface IEndpointMeta {
 	 */
 	requireCredential?: boolean;
 
+	/**
+	 * 管理者のみ使えるエンドポイントか否か
+	 */
+	requireAdmin?: boolean;
+
 	/**
 	 * エンドポイントのリミテーションに関するやつ
 	 * 省略した場合はリミテーションは無いものとして解釈されます。
diff --git a/src/server/api/endpoints/admin/suspend-user.ts b/src/server/api/endpoints/admin/suspend-user.ts
new file mode 100644
index 0000000000..8698120cdb
--- /dev/null
+++ b/src/server/api/endpoints/admin/suspend-user.ts
@@ -0,0 +1,46 @@
+import $ from 'cafy';
+import ID from '../../../../misc/cafy-id';
+import getParams from '../../get-params';
+import User from '../../../../models/user';
+
+export const meta = {
+  desc: {
+    ja: '指定したユーザーを凍結します。',
+    en: 'Suspend a user.'
+  },
+
+  requireCredential: true,
+  requireAdmin: true,
+
+  params: {
+    userId: $.type(ID).note({
+      desc: {
+        ja: '対象のユーザーID',
+        en: 'The user ID which you want to suspend'
+      }
+    }),
+  }
+};
+
+export default (params: any) => new Promise(async (res, rej) => {
+  const [ps, psErr] = getParams(meta, params);
+  if (psErr) return rej(psErr);
+
+  const user = await User.findOne({
+    _id: ps.userId
+  });
+
+  if (user == null) {
+    return rej('user not found');
+  }
+
+  await User.findOneAndUpdate({
+    _id: user._id
+  }, {
+      $set: {
+        isSuspended: true
+      }
+    });
+
+  res();
+});

From 00119328f28d22c9d267de94ae53fa32f3d8b8c9 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 01:19:05 +0900
Subject: [PATCH 044/134] wip

---
 src/client/app/desktop/script.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index 8175ce9b66..8dc0482191 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -24,6 +24,7 @@ import updateBanner from './api/update-banner';
 
 import MkIndex from './views/pages/index.vue';
 import MkDeck from './views/pages/deck/deck.vue';
+import MkAdmin from './views/pages/admin/admin.vue';
 import MkUser from './views/pages/user/user.vue';
 import MkFavorites from './views/pages/favorites.vue';
 import MkSelectDrive from './views/pages/selectdrive.vue';
@@ -55,6 +56,7 @@ init(async (launch) => {
 		routes: [
 			{ path: '/', name: 'index', component: MkIndex },
 			{ path: '/deck', name: 'deck', component: MkDeck },
+			{ path: '/admin', name: 'admin', component: MkAdmin },
 			{ path: '/i/customize-home', component: MkHomeCustomize },
 			{ path: '/i/favorites', component: MkFavorites },
 			{ path: '/i/messaging/:user', component: MkMessagingRoom },

From ba9340a26b2c907deaac0b3feeceb3c4b1c58d15 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 01:24:46 +0900
Subject: [PATCH 045/134] wip

---
 src/client/app/desktop/views/pages/admin/admin.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index 03a356c4a0..c96ebade0c 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -2,7 +2,7 @@
 <div>
 	<nav>
 		<ul>
-			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%i18n:@dashborad%</li>
+			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%i18n:@dashboard%</li>
 			<li @click="nav('drive')" :class="{ active: page == 'drive' }">%i18n:@drive%</li>
 			<li @click="nav('users')" :class="{ active: page == 'users' }">%i18n:@users%</li>
 			<li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li>
@@ -27,9 +27,9 @@ import XDashboard from "./admin.dashboard.vue";
 import XSuspendUser from "./admin.suspend-user.vue";
 
 export default Vue.extend({
-  components: {
+	components: {
 		XDashboard,
-    XSuspendUser
-  }
+		XSuspendUser
+	}
 });
 </script>

From b24e32e14e362e3707b6957617b5d163e95679cc Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 01:32:49 +0900
Subject: [PATCH 046/134] 5.23.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 7424a0cecf..48ddb832ce 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.22.1",
-	"clientVersion": "1.0.8127",
+	"version": "5.23.0",
+	"clientVersion": "1.0.8226",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From 92e5cff285ef1968c168a10719477804e37858d3 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 01:35:36 +0900
Subject: [PATCH 047/134] wip

---
 locales/ja.yml                                     |  2 +-
 src/client/app/desktop/views/pages/admin/admin.vue | 10 ++++++++++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/locales/ja.yml b/locales/ja.yml
index 580ade0ed3..08e437fbf7 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -903,7 +903,7 @@ desktop/views/pages/admin/admin.vue:
   users: "ユーザー"
   update: "更新"
 
-desktop/views/paages/admin/admin.dashboard.vue:
+desktop/views/pages/admin/admin.dashboard.vue:
   dashboard: "ダッシュボード"
   all-users: "全てのユーザー"
   original-users: "このインスタンスのユーザー"
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index c96ebade0c..91f6a23cdc 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -30,6 +30,16 @@ export default Vue.extend({
 	components: {
 		XDashboard,
 		XSuspendUser
+	},
+	data() {
+		return {
+			page: 'dashboard'
+		};
+	},
+	methods: {
+		nav(page: string) {
+			this.page = page;
+		}
 	}
 });
 </script>

From fb1e2efbddfcfe895be79c81d71e3f9b08b8a103 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 01:37:23 +0900
Subject: [PATCH 048/134] wip

---
 .../app/desktop/views/pages/admin/admin.dashboard.vue     | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
index ec43b93840..8624ce32b6 100644
--- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -2,10 +2,10 @@
 <div>
 	<header>%i18n:@dashboard%</header>
 
-	<p><b>%i18n:@all-users%</b><span>{ stats.usersCount | number }</span></p>
-	<p><b>%i18n:@original-users%</b><span>{ stats.originalUsersCount | number }</span></p>
-	<p><b>%i18n:@all-notes%</b><span>{ stats.notesCount | number }</span></p>
-	<p><b>%i18n:@original-notes%</b><span>{ stats.originalNotesCount | number }</span></p>
+	<p><b>%i18n:@all-users%</b><span>{{ stats.usersCount | number }}</span></p>
+	<p><b>%i18n:@original-users%</b><span>{{ stats.originalUsersCount | number }}</span></p>
+	<p><b>%i18n:@all-notes%</b><span>{{ stats.notesCount | number }}</span></p>
+	<p><b>%i18n:@original-notes%</b><span>{{ stats.originalNotesCount | number }}</span></p>
 </div>
 </template>
 

From c29cb5bfb999b678e6db0e9399600b43ebf47293 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 01:38:29 +0900
Subject: [PATCH 049/134] Update package.json

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 48ddb832ce..7204019648 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.23.0",
-	"clientVersion": "1.0.8226",
+	"version": "5.23.1",
+	"clientVersion": "1.0.8229",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From ba08d1aa53be1060088e93abcd9b8f3786f50287 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 01:48:11 +0900
Subject: [PATCH 050/134] wip

---
 .../views/pages/admin/admin.suspend-user.vue  | 40 +++++++++----------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
index d47a4795ee..01d0301eb1 100644
--- a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
@@ -11,29 +11,29 @@ import Vue from "vue";
 import parseAcct from "../../../../../../misc/acct/parse";
 
 export default Vue.extend({
-  data() {
-    return {
-      username: null,
-      suspending: false
-    };
-  },
-  methods: {
-    async suspendUser() {
-      this.suspending = true;
+	data() {
+		return {
+			username: null,
+			suspending: false
+		};
+	},
+	methods: {
+		async suspendUser() {
+			this.suspending = true;
 
-      const user = await (this as any).os.api(
-        "users/show",
-        parseAcct(this.username)
-      );
+			const user = await (this as any).os.api(
+				"users/show",
+				parseAcct(this.username)
+			);
 
-      await (this as any).os.api("admin/suspend-user", {
-        userId: user.id
-      });
+			await (this as any).os.api("admin/suspend-user", {
+				userId: user.id
+			});
 
-      this.suspending = false;
+			this.suspending = false;
 
-      (this as any).os.apis.dialog("%i18n:@suspended%");
-    }
-  }
+			(this as any).os.apis.dialog({ text: "%i18n:@suspended%" });
+		}
+	}
 });
 </script>

From 04257db9384084864d924e2f75d7cdb605e9c2f7 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Mon, 13 Aug 2018 16:54:58 +0000
Subject: [PATCH 051/134] fix(package): update parse5 to version 5.1.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 7204019648..6284ff0416 100644
--- a/package.json
+++ b/package.json
@@ -161,7 +161,7 @@
 		"object-assign-deep": "0.4.0",
 		"on-build-webpack": "0.1.0",
 		"os-utils": "0.0.14",
-		"parse5": "5.0.0",
+		"parse5": "5.1.0",
 		"portscanner": "2.2.0",
 		"progress-bar-webpack-plugin": "1.11.0",
 		"promise-sequential": "1.1.1",

From 1fbe5365f7ca8b913bc2f19c5e1fcdf0263ca606 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 01:57:52 +0900
Subject: [PATCH 052/134] Update example.yml

---
 .config/example.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.config/example.yml b/.config/example.yml
index b84a50c525..a4f3c0036e 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -64,7 +64,7 @@ drive:
   # config:
   #   endPoint:
   #   port:
-  #   secure:
+  #   useSSL:
   #   accessKey:
   #   secretKey:
 
@@ -75,7 +75,7 @@ drive:
   # config:
   #   endPoint: s3-us-west-2.amazonaws.com
   #   region: us-west-2
-  #   secure: true
+  #   useSSL: true
   #   accessKey: XXX
   #   secretKey: YYY
 
@@ -87,7 +87,7 @@ drive:
   # config:
   #   endPoint: s3-us-west-2.amazonaws.com
   #   region: us-west-2
-  #   secure: true
+  #   useSSL: true
   #   accessKey: XXX
   #   secretKey: YYY
 

From 38b75ad977828802cd84968d793166afd658a36a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Tue, 14 Aug 2018 02:10:06 +0900
Subject: [PATCH 053/134] Update avatar.vue

---
 .../app/common/views/components/avatar.vue       | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 77d4d5492a..7f6a555ad0 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -1,14 +1,14 @@
 <template>
-	<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
+	<span class="mk-avatar" :class="{ cat, white }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
 		<span class="inner" :style="style"></span>
 	</span>
-	<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
+	<span class="mk-avatar" :class="{ cat, white }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
 		<span class="inner" :style="style"></span>
 	</span>
-	<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
+	<router-link class="mk-avatar" :class="{ cat, white }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
 		<span class="inner" :style="style"></span>
 	</router-link>
-	<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
+	<router-link class="mk-avatar" :class="{ cat, white }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
 		<span class="inner" :style="style"></span>
 	</router-link>
 </template>
@@ -41,6 +41,9 @@ export default Vue.extend({
 		cat(): boolean {
 			return this.user.isCat && this.$store.state.settings.circleIcons;
 		},
+		white():boolean {
+			return this.$store.state.device.darkmode;
+		},
 		style(): any {
 			return {
 				backgroundColor: this.lightmode
@@ -69,13 +72,16 @@ export default Vue.extend({
 	&.cat::before,
 	&.cat::after
 		background #df548f
-		border solid 4px #ffffff
+		border solid 4px #202224
 		box-sizing border-box
 		content ''
 		display inline-block
 		height 50%
 		width 50%
 
+		&.white
+			border-color #e0eefd
+ 
 	&.cat::before
 		border-radius 0 75% 75%
 		transform rotate(37.5deg) skew(30deg)

From a2931d6f7e428bf896d8829707341b69112f4454 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Tue, 14 Aug 2018 02:26:58 +0900
Subject: [PATCH 054/134] Update ui.header.vue

---
 src/client/app/desktop/views/components/ui.header.vue | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/client/app/desktop/views/components/ui.header.vue b/src/client/app/desktop/views/components/ui.header.vue
index 91b51a457b..edd9829c1c 100644
--- a/src/client/app/desktop/views/components/ui.header.vue
+++ b/src/client/app/desktop/views/components/ui.header.vue
@@ -159,6 +159,9 @@ root(isDark)
 					position absolute
 					height 48px
 
+				> .center
+					right 0
+
 					> .icon
 						margin auto
 						display block
@@ -175,8 +178,7 @@ root(isDark)
 				> .center
 					left 0
 
-				> .right,
-				> .center
+				> .right
 					right 0
 
 					> *

From 58d0ed1a2ec545d5343fe13eadbc85b3121e4cf9 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 02:36:06 +0900
Subject: [PATCH 055/134] 5.23.2

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 6284ff0416..67ec827b5f 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.23.1",
-	"clientVersion": "1.0.8229",
+	"version": "5.23.2",
+	"clientVersion": "1.0.8235",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From 0fd8c86c2420e39db4b1b86d353924f27645f91e Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Mon, 13 Aug 2018 18:03:45 +0000
Subject: [PATCH 056/134] fix(package): update mongodb to version 3.1.2

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 67ec827b5f..9f49a572bf 100644
--- a/package.json
+++ b/package.json
@@ -151,7 +151,7 @@
 		"mkdirp": "0.5.1",
 		"mocha": "5.2.0",
 		"moji": "0.5.1",
-		"mongodb": "3.1.1",
+		"mongodb": "3.1.2",
 		"monk": "6.0.6",
 		"ms": "2.1.1",
 		"nan": "2.10.0",

From 3b38979a347c4e21c76156c1fefe95d74477c511 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 04:30:42 +0900
Subject: [PATCH 057/134] wip

---
 .../views/pages/admin/admin.dashboard.vue     | 20 +++++--
 .../app/desktop/views/pages/admin/admin.vue   | 57 +++++++++++++++++--
 2 files changed, 65 insertions(+), 12 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
index 8624ce32b6..4e6ada6cdb 100644
--- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -1,11 +1,10 @@
 <template>
 <div>
-	<header>%i18n:@dashboard%</header>
-
-	<p><b>%i18n:@all-users%</b><span>{{ stats.usersCount | number }}</span></p>
-	<p><b>%i18n:@original-users%</b><span>{{ stats.originalUsersCount | number }}</span></p>
-	<p><b>%i18n:@all-notes%</b><span>{{ stats.notesCount | number }}</span></p>
-	<p><b>%i18n:@original-notes%</b><span>{{ stats.originalNotesCount | number }}</span></p>
+	<h1>%i18n:@dashboard%</h1>
+	<p><b>%i18n:@all-users%</b>: <span>{{ stats.usersCount | number }}</span></p>
+	<p><b>%i18n:@original-users%</b>: <span>{{ stats.originalUsersCount | number }}</span></p>
+	<p><b>%i18n:@all-notes%</b>: <span>{{ stats.notesCount | number }}</span></p>
+	<p><b>%i18n:@original-notes%</b>: <span>{{ stats.originalNotesCount | number }}</span></p>
 </div>
 </template>
 
@@ -25,3 +24,12 @@ export default Vue.extend({
 	}
 });
 </script>
+
+<style lang="stylus" scoped>
+h1
+	margin 0 0 1em 0
+	padding 0 0 8px 0
+	font-size 1em
+	color #555
+	border-bottom solid 1px #eee
+</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index 91f6a23cdc..36b2b8792d 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -1,21 +1,21 @@
 <template>
-<div>
+<div class="mk-admin">
 	<nav>
 		<ul>
-			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%i18n:@dashboard%</li>
-			<li @click="nav('drive')" :class="{ active: page == 'drive' }">%i18n:@drive%</li>
-			<li @click="nav('users')" :class="{ active: page == 'users' }">%i18n:@users%</li>
-			<li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li>
+			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:chalkboard .fw%%i18n:@dashboard%</li>
+			<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
+			<li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:@drive%</li>
+			<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 		</ul>
 	</nav>
 	<main>
 		<div v-if="page == 'dashboard'">
 			<x-dashboard/>
 		</div>
-		<div v-if="page == 'drive'"></div>
 		<div v-if="page == 'users'">
 			<x-suspend-user/>
 		</div>
+		<div v-if="page == 'drive'"></div>
 		<div v-if="page == 'update'"></div>
 	</main>
 </div>
@@ -43,3 +43,48 @@ export default Vue.extend({
 	}
 });
 </script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.mk-admin
+	display flex
+	height 100%
+	margin 32px
+
+	> nav
+		flex 0 0 250px
+		width 100%
+		height 100%
+		padding 16px 0 0 0
+		overflow auto
+		border-right solid 1px #ddd
+
+		> ul
+			list-style none
+
+			> li
+				display block
+				padding 10px 16px
+				margin 0
+				color #666
+				cursor pointer
+				user-select none
+				transition margin-left 0.2s ease
+
+				> [data-fa]
+					margin-right 4px
+
+
+				&:hover
+					color #555
+
+				&.active
+					margin-left 8px
+					color $theme-color !important
+
+	> main
+		width 100%
+		padding 16px 32px
+
+</style>

From 09b8e81a77ba0c70b2e5c60a461db3f29e0d6aac Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 04:39:37 +0900
Subject: [PATCH 058/134] wip

---
 src/client/app/desktop/views/pages/admin/admin.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index 36b2b8792d..a2b8d47ff6 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -3,8 +3,8 @@
 	<nav>
 		<ul>
 			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:chalkboard .fw%%i18n:@dashboard%</li>
-			<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
-			<li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:@drive%</li>
+			<!-- <li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li> -->
+			<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:@drive%</li> -->
 			<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 		</ul>
 	</nav>

From cc5c32b4d20d780ae7a837094e38998d7d963eb4 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 05:24:51 +0900
Subject: [PATCH 059/134] Clean up

---
 .../app/common/views/components/avatar.vue    | 31 ++++++++++---------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 7f6a555ad0..13334daeba 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -1,14 +1,14 @@
 <template>
-	<span class="mk-avatar" :class="{ cat, white }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
+	<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick">
 		<span class="inner" :style="style"></span>
 	</span>
-	<span class="mk-avatar" :class="{ cat, white }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
+	<span class="mk-avatar" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick">
 		<span class="inner" :style="style"></span>
 	</span>
-	<router-link class="mk-avatar" :class="{ cat, white }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
+	<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id">
 		<span class="inner" :style="style"></span>
 	</router-link>
-	<router-link class="mk-avatar" :class="{ cat, white }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
+	<router-link class="mk-avatar" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview">
 		<span class="inner" :style="style"></span>
 	</router-link>
 </template>
@@ -41,17 +41,14 @@ export default Vue.extend({
 		cat(): boolean {
 			return this.user.isCat && this.$store.state.settings.circleIcons;
 		},
-		white():boolean {
-			return this.$store.state.device.darkmode;
-		},
 		style(): any {
 			return {
 				backgroundColor: this.lightmode
-					? `rgb(${ this.user.avatarColor.slice(0, 3).join(',') })`
+					? `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`
 					: this.user.avatarColor && this.user.avatarColor.length == 3
-						? `rgb(${ this.user.avatarColor.join(',') })`
+						? `rgb(${this.user.avatarColor.join(',')})`
 						: null,
-				backgroundImage: this.lightmode ? null : `url(${ this.user.avatarUrl })`,
+				backgroundImage: this.lightmode ? null : `url(${this.user.avatarUrl})`,
 				borderRadius: this.$store.state.settings.circleIcons ? '100%' : null
 			};
 		}
@@ -65,23 +62,21 @@ export default Vue.extend({
 </script>
 
 <style lang="stylus" scoped>
-.mk-avatar
+
+root(isDark)
 	display inline-block
 	vertical-align bottom
 
 	&.cat::before,
 	&.cat::after
 		background #df548f
-		border solid 4px #202224
+		border solid 4px isDark ? #e0eefd : #202224
 		box-sizing border-box
 		content ''
 		display inline-block
 		height 50%
 		width 50%
 
-		&.white
-			border-color #e0eefd
- 
 	&.cat::before
 		border-radius 0 75% 75%
 		transform rotate(37.5deg) skew(30deg)
@@ -100,4 +95,10 @@ export default Vue.extend({
 		top 0
 		transition border-radius 1s ease
 		z-index 1
+
+.mk-avatar[data-darkmode]
+	root(true)
+
+.mk-avatar:not([data-darkmode])
+	root(false)
 </style>

From f443d36dbb6e819626d3feac59d3afd570f0e52f Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 07:49:59 +0900
Subject: [PATCH 060/134] Resolve #2176

---
 src/daemons/notes-stats.ts  |  7 ++++---
 src/daemons/server-stats.ts |  7 ++++---
 src/misc/queue.ts           | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 41 insertions(+), 6 deletions(-)
 create mode 100644 src/misc/queue.ts

diff --git a/src/daemons/notes-stats.ts b/src/daemons/notes-stats.ts
index 136ccb60c2..3bc0269dde 100644
--- a/src/daemons/notes-stats.ts
+++ b/src/daemons/notes-stats.ts
@@ -1,21 +1,22 @@
 import * as childProcess from 'child_process';
 import Xev from 'xev';
+import Queue from '../misc/queue';
 
 const ev = new Xev();
 
 export default function() {
-	const log: any[] = [];
+	const log = new Queue<any>();
 
 	const p = childProcess.fork(__dirname + '/notes-stats-child.js');
 
 	p.on('message', stats => {
 		ev.emit('notesStats', stats);
 		log.push(stats);
-		if (log.length > 100) log.shift();
+		if (log.length > 100) log.pop();
 	});
 
 	ev.on('requestNotesStatsLog', id => {
-		ev.emit('notesStatsLog:' + id, log);
+		ev.emit('notesStatsLog:' + id, log.toArray());
 	});
 
 	process.on('exit', code => {
diff --git a/src/daemons/server-stats.ts b/src/daemons/server-stats.ts
index 0c0a72f747..b435c12e55 100644
--- a/src/daemons/server-stats.ts
+++ b/src/daemons/server-stats.ts
@@ -2,6 +2,7 @@ import * as os from 'os';
 import * as sysUtils from 'systeminformation';
 import * as diskusage from 'diskusage';
 import Xev from 'xev';
+import Queue from '../misc/queue';
 const osUtils = require('os-utils');
 
 const ev = new Xev();
@@ -12,10 +13,10 @@ const interval = 1000;
  * Report server stats regularly
  */
 export default function() {
-	const log: any[] = [];
+	const log = new Queue<any>();
 
 	ev.on('requestServerStatsLog', id => {
-		ev.emit('serverStatsLog:' + id, log);
+		ev.emit('serverStatsLog:' + id, log.toArray());
 	});
 
 	async function tick() {
@@ -36,7 +37,7 @@ export default function() {
 		};
 		ev.emit('serverStats', stats);
 		log.push(stats);
-		if (log.length > 50) log.shift();
+		if (log.length > 50) log.pop();
 	}
 
 	tick();
diff --git a/src/misc/queue.ts b/src/misc/queue.ts
new file mode 100644
index 0000000000..410878ba8b
--- /dev/null
+++ b/src/misc/queue.ts
@@ -0,0 +1,33 @@
+type Node<T> = { value: T, next: Node<T> };
+
+export default class Queue<T> {
+	private top: Node<T> = null;
+	private rear: Node<T> = null;
+	public length: number = 0;
+
+	public push(value: T): void {
+		const node: Node<T> = { value, next: null };
+		if (this.top === null) {
+			this.top = node;
+			this.rear = node;
+		} else {
+			this.rear.next = node;
+			this.rear = node;
+		}
+		this.length++;
+	}
+
+	public pop(): void {
+		this.top = this.top.next;
+		if (this.top == null) this.rear = null;
+		this.length--;
+	}
+
+	public toArray(): T[] {
+		const arr: T[] = Array<T>(this.length);
+		for (let node = this.top, i = 0; node !== null; node = node.next, i++) {
+			arr[i] = node.value;
+		}
+		return arr;
+	}
+}

From fec988bb79ef003204a966b65cb84c12b5a4b975 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 08:16:21 +0900
Subject: [PATCH 061/134] Provide isFirstNote flag

---
 src/services/note/create.ts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 85a03fdc07..b219d1ca51 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -95,6 +95,8 @@ type Option = {
 };
 
 export default async (user: IUser, data: Option, silent = false) => new Promise<INote>(async (res, rej) => {
+	const isFirstNote = user.notesCount === 0;
+
 	if (data.createdAt == null) data.createdAt = new Date();
 	if (data.visibility == null) data.visibility = 'public';
 	if (data.viaMobile == null) data.viaMobile = false;
@@ -164,6 +166,10 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 	// Pack the note
 	const noteObj = await pack(note);
 
+	if (isFirstNote) {
+		noteObj.isFirstNote = true;
+	}
+
 	const nm = new NotificationManager(user, note);
 	const nmRelatedPromises = [];
 

From bde20a1a651fca49cd2fe1806cba758602d7626c Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Tue, 14 Aug 2018 08:21:25 +0900
Subject: [PATCH 062/134] Use deque instead of linked list

---
 package.json                |  2 ++
 src/daemons/notes-stats.ts  |  4 ++--
 src/daemons/server-stats.ts |  4 ++--
 src/misc/queue.ts           | 33 ---------------------------------
 4 files changed, 6 insertions(+), 37 deletions(-)
 delete mode 100644 src/misc/queue.ts

diff --git a/package.json b/package.json
index 9f49a572bf..80c0bc53c2 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
 		"@types/dateformat": "1.0.1",
 		"@types/debug": "0.0.30",
 		"@types/deep-equal": "1.0.1",
+		"@types/double-ended-queue": "2.1.0",
 		"@types/elasticsearch": "5.0.25",
 		"@types/file-type": "5.2.1",
 		"@types/gulp": "3.8.36",
@@ -97,6 +98,7 @@
 		"deepcopy": "0.6.3",
 		"diskusage": "0.2.4",
 		"dompurify": "1.0.5",
+		"double-ended-queue": "2.1.0-0",
 		"elasticsearch": "15.1.1",
 		"element-ui": "2.4.6",
 		"emojilib": "2.3.0",
diff --git a/src/daemons/notes-stats.ts b/src/daemons/notes-stats.ts
index 3bc0269dde..1de7a98523 100644
--- a/src/daemons/notes-stats.ts
+++ b/src/daemons/notes-stats.ts
@@ -1,11 +1,11 @@
 import * as childProcess from 'child_process';
+import * as Deque from 'double-ended-queue';
 import Xev from 'xev';
-import Queue from '../misc/queue';
 
 const ev = new Xev();
 
 export default function() {
-	const log = new Queue<any>();
+	const log = new Deque<any>();
 
 	const p = childProcess.fork(__dirname + '/notes-stats-child.js');
 
diff --git a/src/daemons/server-stats.ts b/src/daemons/server-stats.ts
index b435c12e55..31560935b2 100644
--- a/src/daemons/server-stats.ts
+++ b/src/daemons/server-stats.ts
@@ -1,8 +1,8 @@
 import * as os from 'os';
 import * as sysUtils from 'systeminformation';
 import * as diskusage from 'diskusage';
+import * as Deque from 'double-ended-queue';
 import Xev from 'xev';
-import Queue from '../misc/queue';
 const osUtils = require('os-utils');
 
 const ev = new Xev();
@@ -13,7 +13,7 @@ const interval = 1000;
  * Report server stats regularly
  */
 export default function() {
-	const log = new Queue<any>();
+	const log = new Deque<any>();
 
 	ev.on('requestServerStatsLog', id => {
 		ev.emit('serverStatsLog:' + id, log.toArray());
diff --git a/src/misc/queue.ts b/src/misc/queue.ts
deleted file mode 100644
index 410878ba8b..0000000000
--- a/src/misc/queue.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-type Node<T> = { value: T, next: Node<T> };
-
-export default class Queue<T> {
-	private top: Node<T> = null;
-	private rear: Node<T> = null;
-	public length: number = 0;
-
-	public push(value: T): void {
-		const node: Node<T> = { value, next: null };
-		if (this.top === null) {
-			this.top = node;
-			this.rear = node;
-		} else {
-			this.rear.next = node;
-			this.rear = node;
-		}
-		this.length++;
-	}
-
-	public pop(): void {
-		this.top = this.top.next;
-		if (this.top == null) this.rear = null;
-		this.length--;
-	}
-
-	public toArray(): T[] {
-		const arr: T[] = Array<T>(this.length);
-		for (let node = this.top, i = 0; node !== null; node = node.next, i++) {
-			arr[i] = node.value;
-		}
-		return arr;
-	}
-}

From ec07112f94d3e00a0b5c0fdfcb6e56f46b7e1adb Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Tue, 14 Aug 2018 16:53:57 +0900
Subject: [PATCH 063/134] Fix bug

---
 .../app/desktop/views/pages/admin/admin.dashboard.vue  | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
index 4e6ada6cdb..b10e829965 100644
--- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -1,10 +1,12 @@
 <template>
 <div>
 	<h1>%i18n:@dashboard%</h1>
-	<p><b>%i18n:@all-users%</b>: <span>{{ stats.usersCount | number }}</span></p>
-	<p><b>%i18n:@original-users%</b>: <span>{{ stats.originalUsersCount | number }}</span></p>
-	<p><b>%i18n:@all-notes%</b>: <span>{{ stats.notesCount | number }}</span></p>
-	<p><b>%i18n:@original-notes%</b>: <span>{{ stats.originalNotesCount | number }}</span></p>
+	<div v-if="stats">
+		<p><b>%i18n:@all-users%</b>: <span>{{ stats.usersCount | number }}</span></p>
+		<p><b>%i18n:@original-users%</b>: <span>{{ stats.originalUsersCount | number }}</span></p>
+		<p><b>%i18n:@all-notes%</b>: <span>{{ stats.notesCount | number }}</span></p>
+		<p><b>%i18n:@original-notes%</b>: <span>{{ stats.originalNotesCount | number }}</span></p>
+	</div>
 </div>
 </template>
 

From 3b37bdc0b999b7dc152dbac19742a6446142d320 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 00:04:37 +0000
Subject: [PATCH 064/134] fix(package): update vue-style-loader to version
 4.1.2

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 80c0bc53c2..6b03c645d5 100644
--- a/package.json
+++ b/package.json
@@ -210,7 +210,7 @@
 		"vue-json-tree-view": "2.1.4",
 		"vue-loader": "15.3.0",
 		"vue-router": "3.0.1",
-		"vue-style-loader": "4.1.1",
+		"vue-style-loader": "4.1.2",
 		"vue-template-compiler": "2.5.17",
 		"vuedraggable": "2.16.0",
 		"vuex": "3.0.1",

From cfcaf77e2155d8316935fb3ba533a19a2ffc4f87 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 00:15:17 +0000
Subject: [PATCH 065/134] fix(package): update mongodb to version 3.1.3

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 6b03c645d5..86b5986c3c 100644
--- a/package.json
+++ b/package.json
@@ -153,7 +153,7 @@
 		"mkdirp": "0.5.1",
 		"mocha": "5.2.0",
 		"moji": "0.5.1",
-		"mongodb": "3.1.2",
+		"mongodb": "3.1.3",
 		"monk": "6.0.6",
 		"ms": "2.1.1",
 		"nan": "2.10.0",

From 01a0a54a2ccb4fdb50d516eee257a648a9d3d9e2 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 01:22:41 +0000
Subject: [PATCH 066/134] fix(package): update @types/mongodb to version 3.1.4

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 86b5986c3c..25b5d37b39 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,7 @@
 		"@types/minio": "6.0.2",
 		"@types/mkdirp": "0.5.2",
 		"@types/mocha": "5.2.3",
-		"@types/mongodb": "3.1.3",
+		"@types/mongodb": "3.1.4",
 		"@types/ms": "0.7.30",
 		"@types/node": "10.5.8",
 		"@types/portscanner": "2.1.0",

From 6009be34dcebaf33b9b64d8e0f6bfc0e2c9865cf Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 01:36:09 +0000
Subject: [PATCH 067/134] fix(package): update @types/node to version 10.7.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 25b5d37b39..f7d59636a5 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
 		"@types/mocha": "5.2.3",
 		"@types/mongodb": "3.1.4",
 		"@types/ms": "0.7.30",
-		"@types/node": "10.5.8",
+		"@types/node": "10.7.0",
 		"@types/portscanner": "2.1.0",
 		"@types/pug": "2.0.4",
 		"@types/qrcode": "1.2.0",

From fe418d8d9ab724470164265c7c804ec96d655d83 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 02:17:00 +0000
Subject: [PATCH 068/134] fix(package): update @types/ws to version 6.0.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index f7d59636a5..61820c76f0 100644
--- a/package.json
+++ b/package.json
@@ -80,7 +80,7 @@
 		"@types/webpack": "4.4.9",
 		"@types/webpack-stream": "3.2.10",
 		"@types/websocket": "0.0.39",
-		"@types/ws": "5.1.2",
+		"@types/ws": "6.0.0",
 		"animejs": "2.2.0",
 		"autosize": "4.0.2",
 		"autwh": "0.1.0",

From 0986301788a40663ced0aa9d6d8a97b099b622b1 Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Tue, 14 Aug 2018 20:13:32 +0900
Subject: [PATCH 069/134] Implement ActivityPub Followers/Following/Outbox

---
 .../activitypub/renderer/follow-user.ts       | 16 ++++
 .../renderer/ordered-collection-page.ts       | 23 +++++
 .../renderer/ordered-collection.ts            | 25 +++--
 src/server/activitypub.ts                     | 71 ++------------
 src/server/activitypub/followers.ts           | 80 ++++++++++++++++
 src/server/activitypub/following.ts           | 80 ++++++++++++++++
 src/server/activitypub/outbox.ts              | 96 +++++++++++++++++++
 7 files changed, 321 insertions(+), 70 deletions(-)
 create mode 100644 src/remote/activitypub/renderer/follow-user.ts
 create mode 100644 src/remote/activitypub/renderer/ordered-collection-page.ts
 create mode 100644 src/server/activitypub/followers.ts
 create mode 100644 src/server/activitypub/following.ts
 create mode 100644 src/server/activitypub/outbox.ts

diff --git a/src/remote/activitypub/renderer/follow-user.ts b/src/remote/activitypub/renderer/follow-user.ts
new file mode 100644
index 0000000000..9a488d392b
--- /dev/null
+++ b/src/remote/activitypub/renderer/follow-user.ts
@@ -0,0 +1,16 @@
+import config from '../../../config';
+import * as mongo from 'mongodb';
+import User, { isLocalUser } from '../../../models/user';
+
+/**
+ * Convert (local|remote)(Follower|Followee)ID to URL
+ * @param id Follower|Followee ID
+ */
+export default async function renderFollowUser(id: mongo.ObjectID): Promise<any> {
+
+	const user = await User.findOne({
+		_id: id
+	});
+
+	return isLocalUser(user) ? `${config.url}/users/${user._id}` : user.uri;
+}
diff --git a/src/remote/activitypub/renderer/ordered-collection-page.ts b/src/remote/activitypub/renderer/ordered-collection-page.ts
new file mode 100644
index 0000000000..83af07870e
--- /dev/null
+++ b/src/remote/activitypub/renderer/ordered-collection-page.ts
@@ -0,0 +1,23 @@
+/**
+ * Render OrderedCollectionPage
+ * @param id URL of self
+ * @param totalItems Number of total items
+ * @param orderedItems Items
+ * @param partOf URL of base
+ * @param prev URL of prev page (optional)
+ * @param next URL of next page (optional)
+ */
+export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev: string, next: string) {
+	const page = {
+		id,
+		partOf,
+		type: 'OrderedCollectionPage',
+		totalItems,
+		orderedItems
+	} as any;
+
+	if (prev) page.prev = prev;
+	if (next) page.next = next;
+
+	return page;
+}
diff --git a/src/remote/activitypub/renderer/ordered-collection.ts b/src/remote/activitypub/renderer/ordered-collection.ts
index 9d543b1e1b..3c448cf873 100644
--- a/src/remote/activitypub/renderer/ordered-collection.ts
+++ b/src/remote/activitypub/renderer/ordered-collection.ts
@@ -1,6 +1,19 @@
-export default (id: string, totalItems: any, orderedItems: any) => ({
-	id,
-	type: 'OrderedCollection',
-	totalItems,
-	orderedItems
-});
+/**
+ * Render OrderedCollection
+ * @param id URL of self
+ * @param totalItems Total number of items
+ * @param first URL of first page (optional)
+ * @param last URL of last page (optional)
+ */
+export default function(id: string, totalItems: any, first: string, last: string) {
+	const page: any = {
+		id,
+		type: 'OrderedCollection',
+		totalItems,
+	};
+
+	if (first) page.first = first;
+	if (last) page.last = last;
+
+	return page;
+}
diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts
index 7d6fe09269..c2dec2b997 100644
--- a/src/server/activitypub.ts
+++ b/src/server/activitypub.ts
@@ -10,8 +10,9 @@ import User, { isLocalUser, ILocalUser, IUser } from '../models/user';
 import renderNote from '../remote/activitypub/renderer/note';
 import renderKey from '../remote/activitypub/renderer/key';
 import renderPerson from '../remote/activitypub/renderer/person';
-import renderOrderedCollection from '../remote/activitypub/renderer/ordered-collection';
-import config from '../config';
+import Outbox from './activitypub/outbox';
+import Followers from './activitypub/followers';
+import Following from './activitypub/following';
 
 // Init router
 const router = new Router();
@@ -64,72 +65,14 @@ router.get('/notes/:note', async (ctx, next) => {
 	ctx.body = pack(await renderNote(note));
 });
 
-// outbot
-router.get('/users/:user/outbox', async ctx => {
-	const userId = new mongo.ObjectID(ctx.params.user);
-
-	const user = await User.findOne({
-		_id: userId,
-		host: null
-	});
-
-	if (user === null) {
-		ctx.status = 404;
-		return;
-	}
-
-	const notes = await Note.find({ userId: user._id }, {
-		limit: 10,
-		sort: { _id: -1 }
-	});
-
-	const renderedNotes = await Promise.all(notes.map(note => renderNote(note)));
-	const rendered = renderOrderedCollection(`${config.url}/users/${userId}/inbox`, user.notesCount, renderedNotes);
-
-	ctx.body = pack(rendered);
-});
+// outbox
+router.get('/users/:user/outbox', Outbox);
 
 // followers
-router.get('/users/:user/followers', async ctx => {
-	const userId = new mongo.ObjectID(ctx.params.user);
-
-	const user = await User.findOne({
-		_id: userId,
-		host: null
-	});
-
-	if (user === null) {
-		ctx.status = 404;
-		return;
-	}
-
-	// TODO: Implement fetch and render
-
-	const rendered = renderOrderedCollection(`${config.url}/users/${userId}/followers`, 0, []);
-
-	ctx.body = pack(rendered);
-});
+router.get('/users/:user/followers', Followers);
 
 // following
-router.get('/users/:user/following', async ctx => {
-	const userId = new mongo.ObjectID(ctx.params.user);
-
-	const user = await User.findOne({
-		_id: userId,
-		host: null
-	});
-
-	if (user === null) {
-		ctx.status = 404;
-		return;
-	}
-
-	// TODO: Implement fetch and render
-
-	const rendered = renderOrderedCollection(`${config.url}/users/${userId}/following`, 0, []);
-
-	ctx.body = pack(rendered);
-});
+router.get('/users/:user/following', Following);
 
 // publickey
 router.get('/users/:user/publickey', async ctx => {
diff --git a/src/server/activitypub/followers.ts b/src/server/activitypub/followers.ts
new file mode 100644
index 0000000000..d51d45b1c7
--- /dev/null
+++ b/src/server/activitypub/followers.ts
@@ -0,0 +1,80 @@
+import * as mongo from 'mongodb';
+import * as Koa from 'koa';
+import config from '../../config';
+import $ from 'cafy'; import ID from '../../misc/cafy-id';
+import User from '../../models/user';
+import Following from '../../models/following';
+import pack from '../../remote/activitypub/renderer';
+import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
+import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
+import renderFollowUser from '../../remote/activitypub/renderer/follow-user';
+
+export default async (ctx: Koa.Context) => {
+	const userId = new mongo.ObjectID(ctx.params.user);
+
+	// Get 'cursor' parameter
+	const [cursor = null, cursorErr] = $.type(ID).optional.get(ctx.request.query.cursor);
+
+	// Get 'page' parameter
+	const pageErr = !$.str.optional.or(['true', 'false']).ok(ctx.request.query.page);
+	const page: boolean = ctx.request.query.page === 'true';
+
+	// Validate parameters
+	if (cursorErr || pageErr) {
+		ctx.status = 400;
+		return;
+	}
+
+	// Verify user
+	const user = await User.findOne({
+		_id: userId,
+		host: null
+	});
+
+	if (user === null) {
+		ctx.status = 404;
+		return;
+	}
+
+	const limit = 10;
+	const partOf = `${config.url}/users/${userId}/followers`;
+
+	if (page) {
+		// Construct query
+		const query = {
+			followeeId: user._id
+		} as any;
+
+		// カーソルが指定されている場合
+		if (cursor) {
+			query._id = {
+				$lt: cursor
+			};
+		}
+
+		// Get followers
+		const followings = await Following
+			.find(query, {
+				limit: limit + 1,
+				sort: { _id: -1 }
+			});
+
+		// 「次のページ」があるかどうか
+		const inStock = followings.length === limit + 1;
+		if (inStock) followings.pop();
+
+		const renderedFollowers = await Promise.all(followings.map(following => renderFollowUser(following.followerId)));
+		const rendered = renderOrderedCollectionPage(
+			`${partOf}?page=true${cursor ? `&cursor=${cursor}` : ''}`,
+			user.followersCount, renderedFollowers, partOf,
+			null,
+			inStock ? `${partOf}?page=true&cursor=${followings[followings.length - 1]._id}` : null
+		);
+
+		ctx.body = pack(rendered);
+	} else {
+		// index page
+		const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`, null);
+		ctx.body = pack(rendered);
+	}
+};
diff --git a/src/server/activitypub/following.ts b/src/server/activitypub/following.ts
new file mode 100644
index 0000000000..7e496f590d
--- /dev/null
+++ b/src/server/activitypub/following.ts
@@ -0,0 +1,80 @@
+import * as mongo from 'mongodb';
+import * as Koa from 'koa';
+import config from '../../config';
+import $ from 'cafy'; import ID from '../../misc/cafy-id';
+import User from '../../models/user';
+import Following from '../../models/following';
+import pack from '../../remote/activitypub/renderer';
+import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
+import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
+import renderFollowUser from '../../remote/activitypub/renderer/follow-user';
+
+export default async (ctx: Koa.Context) => {
+	const userId = new mongo.ObjectID(ctx.params.user);
+
+	// Get 'cursor' parameter
+	const [cursor = null, cursorErr] = $.type(ID).optional.get(ctx.request.query.cursor);
+
+	// Get 'page' parameter
+	const pageErr = !$.str.optional.or(['true', 'false']).ok(ctx.request.query.page);
+	const page: boolean = ctx.request.query.page === 'true';
+
+	// Validate parameters
+	if (cursorErr || pageErr) {
+		ctx.status = 400;
+		return;
+	}
+
+	// Verify user
+	const user = await User.findOne({
+		_id: userId,
+		host: null
+	});
+
+	if (user === null) {
+		ctx.status = 404;
+		return;
+	}
+
+	const limit = 10;
+	const partOf = `${config.url}/users/${userId}/following`;
+
+	if (page) {
+		// Construct query
+		const query = {
+			followerId: user._id
+		} as any;
+
+		// カーソルが指定されている場合
+		if (cursor) {
+			query._id = {
+				$lt: cursor
+			};
+		}
+
+		// Get followings
+		const followings = await Following
+			.find(query, {
+				limit: limit + 1,
+				sort: { _id: -1 }
+			});
+
+		// 「次のページ」があるかどうか
+		const inStock = followings.length === limit + 1;
+		if (inStock) followings.pop();
+
+		const renderedFollowees = await Promise.all(followings.map(following => renderFollowUser(following.followeeId)));
+		const rendered = renderOrderedCollectionPage(
+			`${partOf}?page=true${cursor ? `&cursor=${cursor}` : ''}`,
+			user.followingCount, renderedFollowees, partOf,
+			null,
+			inStock ? `${partOf}?page=true&cursor=${followings[followings.length - 1]._id}` : null
+		);
+
+		ctx.body = pack(rendered);
+	} else {
+		// index page
+		const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`, null);
+		ctx.body = pack(rendered);
+	}
+};
diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts
new file mode 100644
index 0000000000..e441e3dc4e
--- /dev/null
+++ b/src/server/activitypub/outbox.ts
@@ -0,0 +1,96 @@
+import * as mongo from 'mongodb';
+import * as Koa from 'koa';
+import config from '../../config';
+import $ from 'cafy'; import ID from '../../misc/cafy-id';
+import User from '../../models/user';
+import pack from '../../remote/activitypub/renderer';
+import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection';
+import renderOrderedCollectionPage from '../../remote/activitypub/renderer/ordered-collection-page';
+
+import Note from '../../models/note';
+import renderNote from '../../remote/activitypub/renderer/note';
+
+export default async (ctx: Koa.Context) => {
+	const userId = new mongo.ObjectID(ctx.params.user);
+
+	// Get 'sinceId' parameter
+	const [sinceId, sinceIdErr] = $.type(ID).optional.get(ctx.request.query.since_id);
+
+	// Get 'untilId' parameter
+	const [untilId, untilIdErr] = $.type(ID).optional.get(ctx.request.query.until_id);
+
+	// Get 'page' parameter
+	const pageErr = !$.str.optional.or(['true', 'false']).ok(ctx.request.query.page);
+	const page: boolean = ctx.request.query.page === 'true';
+
+	// Validate parameters
+	if (sinceIdErr || untilIdErr || pageErr || [sinceId, untilId].filter(x => x != null).length > 1) {
+		ctx.status = 400;
+		return;
+	}
+
+	// Verify user
+	const user = await User.findOne({
+		_id: userId,
+		host: null
+	});
+
+	if (user === null) {
+		ctx.status = 404;
+		return;
+	}
+
+	const limit = 20;
+	const partOf = `${config.url}/users/${userId}/outbox`;
+
+	if (page) {
+		//#region Construct query
+		const sort = {
+			_id: -1
+		};
+
+		const query = {
+			userId: user._id,
+			$or: [ { visibility: 'public' }, { visibility: 'home' } ],
+			text: { $ne: null }	// exclude renote, but include quote
+		} as any;
+
+		if (sinceId) {
+			sort._id = 1;
+			query._id = {
+				$gt: sinceId
+			};
+		} else if (untilId) {
+			query._id = {
+				$lt: untilId
+			};
+		}
+		//#endregion
+
+		// Issue query
+		const notes = await Note
+			.find(query, {
+				limit: limit,
+				sort: sort
+			});
+
+		if (sinceId) notes.reverse();
+
+		const renderedNotes = await Promise.all(notes.map(note => renderNote(note)));
+		const rendered = renderOrderedCollectionPage(
+			`${partOf}?page=true${sinceId ? `&since_id=${sinceId}` : ''}${untilId ? `&until_id=${untilId}` : ''}`,
+			user.notesCount, renderedNotes, partOf,
+			notes.length > 0 ? `${partOf}?page=true&since_id=${notes[0]._id}` : null,
+			notes.length > 0 ? `${partOf}?page=true&until_id=${notes[notes.length - 1]._id}` : null
+		);
+
+		ctx.body = pack(rendered);
+	} else {
+		// index page
+		const rendered = renderOrderedCollection(partOf, user.notesCount,
+			`${partOf}?page=true`,
+			`${partOf}?page=true&since_id=000000000000000000000000`
+		);
+		ctx.body = pack(rendered);
+	}
+};

From f085ecedb34c5acaaa5ec2b8e95acd6630d9c766 Mon Sep 17 00:00:00 2001
From: Skid <skid@tuto-craft.com>
Date: Tue, 14 Aug 2018 15:41:55 +0200
Subject: [PATCH 070/134] Update setup.en.md

---
 docs/setup.en.md | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/docs/setup.en.md b/docs/setup.en.md
index 56632cc361..73ea636390 100644
--- a/docs/setup.en.md
+++ b/docs/setup.en.md
@@ -62,6 +62,13 @@ npm install web-push -g
 web-push generate-vapid-keys
 ```
 
+*(optional)* Create a twitter application
+----------------------------------------------------------------
+If you want to enable the twitter integration, you need to create a twitter app at [apps.twitter.com](https://apps.twitter.com/).
+
+In the app you need to set the oauth callback url as : https://misskey-instance/api/tw/cb
+
+
 *5.* Make configuration file
 ----------------------------------------------------------------
 1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.

From f04526bacad59b3e6947a93459f1067e94cb9322 Mon Sep 17 00:00:00 2001
From: Skid <skid@tuto-craft.com>
Date: Tue, 14 Aug 2018 15:45:36 +0200
Subject: [PATCH 071/134] Update example.yml

---
 .config/example.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.config/example.yml b/.config/example.yml
index a4f3c0036e..ce092a08f3 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -123,6 +123,7 @@ drive:
 # google_maps_api_key: example-google-maps-api-key
 
 # Twitter integration
+# You need to set the oauth callback url as : https://<your-misskey-instance>/api/tw/cb
 # twitter:
 #   consumer_key: example-twitter-consumer-key
 #   consumer_secret: example-twitter-consumer-secret-key

From 175f6303bc1740cb959d2df42bd39434d327f4b2 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 01:51:43 +0900
Subject: [PATCH 072/134] Update theme color

---
 src/const.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/const.json b/src/const.json
index b4f3b63ac7..b93226b2d2 100644
--- a/src/const.json
+++ b/src/const.json
@@ -1,5 +1,5 @@
 {
 	"copyright": "Copyright (c) 2014-2018 syuilo",
-	"themeColor": "#f66e4f",
+	"themeColor": "#f6584f",
 	"themeColorForeground": "#fff"
 }

From 177c5494939fc77b48a6faadc53a9611f5d11d86 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 15:32:14 +0000
Subject: [PATCH 073/134] fix(package): update url-loader to version 1.1.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 61820c76f0..3b7408684f 100644
--- a/package.json
+++ b/package.json
@@ -201,7 +201,7 @@
 		"typescript": "2.9.2",
 		"typescript-eslint-parser": "18.0.0",
 		"uglify-es": "3.3.9",
-		"url-loader": "1.0.1",
+		"url-loader": "1.1.0",
 		"uuid": "3.3.2",
 		"v-animate-css": "0.0.2",
 		"vue": "2.5.17",

From 90768d30aab359a87f8d63e28309a49c2db03f55 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Tue, 14 Aug 2018 10:52:57 +0000
Subject: [PATCH 074/134] fix(package): update seedrandom to version 2.4.4

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 3b7408684f..00eb23458a 100644
--- a/package.json
+++ b/package.json
@@ -180,7 +180,7 @@
 		"rndstr": "1.0.0",
 		"s-age": "1.1.2",
 		"sass-loader": "7.1.0",
-		"seedrandom": "2.4.3",
+		"seedrandom": "2.4.4",
 		"sharp": "0.20.5",
 		"showdown": "1.8.6",
 		"showdown-highlightjs-extension": "0.1.2",

From e0bc0d283073081ffd63deb431506b119e4fc599 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 02:01:49 +0900
Subject: [PATCH 075/134] Add new kao

---
 src/client/app/common/scripts/get-kao.ts | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts
index 645196132b..ca7cebaebc 100644
--- a/src/client/app/common/scripts/get-kao.ts
+++ b/src/client/app/common/scripts/get-kao.ts
@@ -1,5 +1,6 @@
 export default () => [
 	'(=^・・^=)',
 	'v(\'ω\')v',
-	'🐡( \'-\' 🐡 )フグパンチ!!!!'
-][Math.floor(Math.random() * 3)];
+	'🐡( \'-\' 🐡 )フグパンチ!!!!',
+	'🖕(´・_・`)🖕'
+][Math.floor(Math.random() * 4)];

From 4a9fc0c8ed27c261a49223fc0598acec384d33d9 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 02:08:18 +0900
Subject: [PATCH 076/134] Better query

---
 src/server/activitypub/outbox.ts | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts
index e441e3dc4e..91473b77e3 100644
--- a/src/server/activitypub/outbox.ts
+++ b/src/server/activitypub/outbox.ts
@@ -51,8 +51,15 @@ export default async (ctx: Koa.Context) => {
 
 		const query = {
 			userId: user._id,
-			$or: [ { visibility: 'public' }, { visibility: 'home' } ],
-			text: { $ne: null }	// exclude renote, but include quote
+			$and: [{
+				$or: [ { visibility: 'public' }, { visibility: 'home' } ]
+			}, { // exclude renote, but include quote
+				$or: [{
+					text: { $ne: null }
+				}, {
+					mediaIds: { $ne: [] }
+				}]
+			}]
 		} as any;
 
 		if (sinceId) {

From cd6a1d34467fc1093c95fdf2e3dc007182c71eff Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 02:14:57 +0900
Subject: [PATCH 077/134] 5.24.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 00eb23458a..de5e6786b7 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.23.2",
-	"clientVersion": "1.0.8235",
+	"version": "5.24.0",
+	"clientVersion": "1.0.8279",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From 9c22b1a68a5ee72fd3c65bec5c02d56cd6022516 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 02:35:25 +0900
Subject: [PATCH 078/134] Fix

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index de5e6786b7..ac9d584727 100644
--- a/package.json
+++ b/package.json
@@ -153,7 +153,7 @@
 		"mkdirp": "0.5.1",
 		"mocha": "5.2.0",
 		"moji": "0.5.1",
-		"mongodb": "3.1.3",
+		"mongodb": "3.1.1",
 		"monk": "6.0.6",
 		"ms": "2.1.1",
 		"nan": "2.10.0",

From 83ba951bf97a83ad155372c0ab21b89c1f5279a0 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 02:38:47 +0900
Subject: [PATCH 079/134] 5.24.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index ac9d584727..177846f810 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.24.0",
+	"version": "5.24.1",
 	"clientVersion": "1.0.8279",
 	"codename": "nighthike",
 	"main": "./built/index.js",

From 259fac224edf137c0e44cd5b02bf861d9c8d153b Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Wed, 15 Aug 2018 04:44:39 +0900
Subject: [PATCH 080/134] Resolve #2201

---
 .../views/pages/admin/admin.suspend-user.vue     | 16 ++++++++++++++--
 .../app/desktop/views/pages/admin/admin.vue      | 11 ++++++++++-
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
index 01d0301eb1..6eb82f0a51 100644
--- a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
@@ -1,8 +1,8 @@
 <template>
 <div>
 	<header>%i18n:@suspend-user%</header>
-	<input v-model="username"/>
-	<button @click="suspendUser" :disabled="suspending">%i18n:@suspend%</button>
+	<input v-model="username" type="text" class="ui"/>
+	<button class="ui" @click="suspendUser" :disabled="suspending">%i18n:@suspend%</button>
 </div>
 </template>
 
@@ -37,3 +37,15 @@ export default Vue.extend({
 	}
 });
 </script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+header
+	margin 10px 0
+
+
+button
+	margin 16px 0
+
+</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index a2b8d47ff6..12858cc6d0 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -3,7 +3,7 @@
 	<nav>
 		<ul>
 			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:chalkboard .fw%%i18n:@dashboard%</li>
-			<!-- <li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li> -->
+			<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
 			<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:@drive%</li> -->
 			<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 		</ul>
@@ -87,4 +87,13 @@ export default Vue.extend({
 		width 100%
 		padding 16px 32px
 
+header
+	margin 10px 0
+
+
+button
+	margin 16px 0
+	position absolute
+	right 0
+
 </style>

From c53cb942507a791b098638e085fa1246b5b63f40 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Wed, 15 Aug 2018 06:50:49 +0900
Subject: [PATCH 081/134] Resolve #2215

---
 locales/ja.yml                                |  5 ++
 .../pages/admin/admin.unsuspend-user.vue      | 51 +++++++++++++++++++
 .../app/desktop/views/pages/admin/admin.vue   |  5 +-
 .../api/endpoints/admin/unsuspend-user.ts     | 46 +++++++++++++++++
 4 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue
 create mode 100644 src/server/api/endpoints/admin/unsuspend-user.ts

diff --git a/locales/ja.yml b/locales/ja.yml
index 08e437fbf7..9c3daf450f 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -915,6 +915,11 @@ desktop/views/pages/admin/admin.suspend-user.vue:
   suspend: "凍結"
   suspended: "凍結しました"
 
+desktop/views/pages/admin/admin.unsuspend-user.vue:
+  unsuspend-user: "ユーザーの凍結の解除"
+  unsuspend: "凍結の解除"
+  unsuspended: "凍結を解除しました"
+
 desktop/views/pages/deck/deck.tl-column.vue:
   is-media-only: "メディア投稿のみ"
   is-media-view: "メディアビュー"
diff --git a/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue b/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue
new file mode 100644
index 0000000000..8c6f63ce88
--- /dev/null
+++ b/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue
@@ -0,0 +1,51 @@
+<template>
+<div>
+	<header>%i18n:@unsuspend-user%</header>
+	<input v-model="username" type="text" class="ui"/>
+	<button class="ui" @click="unsuspendUser" :disabled="unsuspending">%i18n:@unsuspend%</button>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from "vue";
+import parseAcct from "../../../../../../misc/acct/parse";
+
+export default Vue.extend({
+	data() {
+		return {
+			username: null,
+			unsuspending: false
+		};
+	},
+	methods: {
+		async unsuspendUser() {
+			this.unsuspending = true;
+
+			const user = await (this as any).os.api(
+				"users/show",
+				parseAcct(this.username)
+			);
+
+			await (this as any).os.api("admin/unsuspend-user", {
+				userId: user.id
+			});
+
+			this.unsuspending = false;
+
+			(this as any).os.apis.dialog({ text: "%i18n:@unsuspended%" });
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+header
+	margin 10px 0
+
+
+button
+	margin 16px 0
+
+</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index 12858cc6d0..b581bea465 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -14,6 +14,7 @@
 		</div>
 		<div v-if="page == 'users'">
 			<x-suspend-user/>
+			<x-unsuspend-user/>
 		</div>
 		<div v-if="page == 'drive'"></div>
 		<div v-if="page == 'update'"></div>
@@ -25,11 +26,13 @@
 import Vue from "vue";
 import XDashboard from "./admin.dashboard.vue";
 import XSuspendUser from "./admin.suspend-user.vue";
+import XUnsuspendUser from "./admin.unsuspend-user.vue";
 
 export default Vue.extend({
 	components: {
 		XDashboard,
-		XSuspendUser
+		XSuspendUser,
+		XUnsuspendUser
 	},
 	data() {
 		return {
diff --git a/src/server/api/endpoints/admin/unsuspend-user.ts b/src/server/api/endpoints/admin/unsuspend-user.ts
new file mode 100644
index 0000000000..8409bd1b76
--- /dev/null
+++ b/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -0,0 +1,46 @@
+import $ from 'cafy';
+import ID from '../../../../misc/cafy-id';
+import getParams from '../../get-params';
+import User from '../../../../models/user';
+
+export const meta = {
+	desc: {
+		ja: '指定したユーザーの凍結を解除します。',
+		en: 'Unsuspend a user.'
+	},
+
+	requireCredential: true,
+	requireAdmin: true,
+
+	params: {
+		userId: $.type(ID).note({
+			desc: {
+				ja: '対象のユーザーID',
+				en: 'The user ID which you want to unsuspend'
+			}
+		}),
+	}
+};
+
+export default (params: any) => new Promise(async (res, rej) => {
+	const [ps, psErr] = getParams(meta, params);
+	if (psErr) return rej(psErr);
+
+	const user = await User.findOne({
+		_id: ps.userId
+	});
+
+	if (user == null) {
+		return rej('user not found');
+	}
+
+	await User.findOneAndUpdate({
+		_id: user._id
+	}, {
+			$set: {
+				isSuspended: false
+			}
+		});
+
+	res();
+});

From b351b3fae5e91b0f99855588dabbaa38054109d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Wed, 15 Aug 2018 18:49:05 +0900
Subject: [PATCH 082/134] Update url-preview.vue

---
 src/client/app/common/views/components/url-preview.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 50194b69b7..b45c9e2ecc 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -144,6 +144,7 @@ export default Vue.extend({
 						'songkick.com',
 						'soundcloud.com',
 						'spinninrecords.com',
+						'spotify.com',
 						'stitcher.com',
 						'stream.me',
 						'switchboard.live',

From a860479e88d144c693f2cd432224867259fc78fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Wed, 15 Aug 2018 19:00:44 +0900
Subject: [PATCH 083/134] Update url-preview.vue

---
 src/client/app/common/views/components/url-preview.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index b45c9e2ecc..af5d8397af 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -129,6 +129,7 @@ export default Vue.extend({
 						'newretrowave.com',
 						'nhk.or.jp',
 						'nicovideo.jp',
+						'nico.ms',
 						'noisetrade.com',
 						'nood.tv',
 						'npr.org',

From 78563ef9a0294ee5a95cd3471666c274f2208ef5 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 20:09:56 +0900
Subject: [PATCH 084/134] Fix #2228

---
 src/client/app/common/views/components/avatar.vue | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index 13334daeba..c5ac74e537 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -67,6 +67,10 @@ root(isDark)
 	display inline-block
 	vertical-align bottom
 
+	&:not(.cat)
+		overflow hidden
+		border-radius 8px
+
 	&.cat::before,
 	&.cat::after
 		background #df548f

From 9ccf9a2496649e11dbf557b93e11d5fd9df33162 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 20:20:46 +0900
Subject: [PATCH 085/134] Fix #2229

---
 src/daemons/notes-stats.ts  | 2 +-
 src/daemons/server-stats.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/daemons/notes-stats.ts b/src/daemons/notes-stats.ts
index 1de7a98523..3d2c4820a6 100644
--- a/src/daemons/notes-stats.ts
+++ b/src/daemons/notes-stats.ts
@@ -12,7 +12,7 @@ export default function() {
 	p.on('message', stats => {
 		ev.emit('notesStats', stats);
 		log.push(stats);
-		if (log.length > 100) log.pop();
+		if (log.length > 100) log.shift();
 	});
 
 	ev.on('requestNotesStatsLog', id => {
diff --git a/src/daemons/server-stats.ts b/src/daemons/server-stats.ts
index 31560935b2..af935d35b2 100644
--- a/src/daemons/server-stats.ts
+++ b/src/daemons/server-stats.ts
@@ -37,7 +37,7 @@ export default function() {
 		};
 		ev.emit('serverStats', stats);
 		log.push(stats);
-		if (log.length > 50) log.pop();
+		if (log.length > 50) log.shift();
 	}
 
 	tick();

From 69ac7b739f5579cfbdc392ad548c29f3cbfe7dde Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 20:23:50 +0900
Subject: [PATCH 086/134] Improve MFM

---
 src/mfm/parse/elements/inline-code.ts | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/mfm/parse/elements/inline-code.ts b/src/mfm/parse/elements/inline-code.ts
index 1dd5affa51..e4ab499584 100644
--- a/src/mfm/parse/elements/inline-code.ts
+++ b/src/mfm/parse/elements/inline-code.ts
@@ -14,11 +14,12 @@ export type TextElementInlineCode = {
 export default function(text: string) {
 	const match = text.match(/^`(.+?)`/);
 	if (!match) return null;
+	if (match[1].includes('´')) return null;
 	const code = match[0];
 	return {
 		type: 'inline-code',
 		content: code,
-		code: code.substr(1, code.length - 2).trim(),
-		html: genHtml(code.substr(1, code.length - 2).trim())
+		code: match[1],
+		html: genHtml(match[1])
 	} as TextElementInlineCode;
 }

From 83dcfec053b19e1fd947c4d41cc075571a276c65 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 20:27:36 +0900
Subject: [PATCH 087/134] Improve MFM

---
 src/mfm/parse/elements/search.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/mfm/parse/elements/search.ts b/src/mfm/parse/elements/search.ts
index 9c4b7ffbe5..2fb0f93f2c 100644
--- a/src/mfm/parse/elements/search.ts
+++ b/src/mfm/parse/elements/search.ts
@@ -9,7 +9,7 @@ export type TextElementSearch = {
 };
 
 export default function(text: string) {
-	const match = text.match(/^(.+?) (検索|Search)(\n|$)/i);
+	const match = text.match(/^(.+?)( | )(検索|\[検索\]|Search|\[Search\])(\n|$)/i);
 	if (!match) return null;
 	return {
 		type: 'search',

From 08b431723a3b6b48570dd65c43b1e23355b76e32 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 15 Aug 2018 20:27:49 +0900
Subject: [PATCH 088/134] Refactoring

---
 src/mfm/parse/elements/bold.ts  | 2 +-
 src/mfm/parse/elements/code.ts  | 4 ++--
 src/mfm/parse/elements/emoji.ts | 4 ++--
 src/mfm/parse/elements/quote.ts | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/mfm/parse/elements/bold.ts b/src/mfm/parse/elements/bold.ts
index cf615cd3cc..c8c3c80a39 100644
--- a/src/mfm/parse/elements/bold.ts
+++ b/src/mfm/parse/elements/bold.ts
@@ -15,6 +15,6 @@ export default function(text: string) {
 	return {
 		type: 'bold',
 		content: bold,
-		bold: bold.substr(2, bold.length - 4)
+		bold: match[1]
 	} as TextElementBold;
 }
diff --git a/src/mfm/parse/elements/code.ts b/src/mfm/parse/elements/code.ts
index f48e945048..04ce692ce1 100644
--- a/src/mfm/parse/elements/code.ts
+++ b/src/mfm/parse/elements/code.ts
@@ -18,7 +18,7 @@ export default function(text: string) {
 	return {
 		type: 'code',
 		content: code,
-		code: code.substr(3, code.length - 6).trim(),
-		html: genHtml(code.substr(3, code.length - 6).trim())
+		code: match[1],
+		html: genHtml(match[1])
 	} as TextElementCode;
 }
diff --git a/src/mfm/parse/elements/emoji.ts b/src/mfm/parse/elements/emoji.ts
index 83d3effef5..cd9a3d032c 100644
--- a/src/mfm/parse/elements/emoji.ts
+++ b/src/mfm/parse/elements/emoji.ts
@@ -9,12 +9,12 @@ export type TextElementEmoji = {
 };
 
 export default function(text: string) {
-	const match = text.match(/^:[a-zA-Z0-9+-_]+:/);
+	const match = text.match(/^:([a-zA-Z0-9+-_]+):/);
 	if (!match) return null;
 	const emoji = match[0];
 	return {
 		type: 'emoji',
 		content: emoji,
-		emoji: emoji.substr(1, emoji.length - 2)
+		emoji: match[1]
 	} as TextElementEmoji;
 }
diff --git a/src/mfm/parse/elements/quote.ts b/src/mfm/parse/elements/quote.ts
index bef9ad4988..ea99240d5f 100644
--- a/src/mfm/parse/elements/quote.ts
+++ b/src/mfm/parse/elements/quote.ts
@@ -15,6 +15,6 @@ export default function(text: string) {
 	return {
 		type: 'quote',
 		content: quote,
-		quote: quote.substr(1, quote.length - 2).trim(),
+		quote: match[1].trim(),
 	} as TextElementQuote;
 }

From 4bdef3720c80da90982f401ffe407827bbf464c0 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Wed, 15 Aug 2018 23:55:30 +0900
Subject: [PATCH 089/134] Update setup.en.md

---
 docs/setup.en.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/setup.en.md b/docs/setup.en.md
index 73ea636390..6a54817a78 100644
--- a/docs/setup.en.md
+++ b/docs/setup.en.md
@@ -64,7 +64,7 @@ web-push generate-vapid-keys
 
 *(optional)* Create a twitter application
 ----------------------------------------------------------------
-If you want to enable the twitter integration, you need to create a twitter app at [apps.twitter.com](https://apps.twitter.com/).
+If you want to enable the twitter integration, you need to create a twitter app at [https://developer.twitter.com/en/apply/user](https://developer.twitter.com/en/apply/user).
 
 In the app you need to set the oauth callback url as : https://misskey-instance/api/tw/cb
 

From ab3f8fd10c9a0b579fb5ecca507601263cd9f943 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Thu, 16 Aug 2018 00:13:24 +0900
Subject: [PATCH 090/134] Enhance log message

---
 src/index.ts | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index 0dda8b05b7..c2994c606c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -48,7 +48,7 @@ main();
  * Init process
  */
 function main() {
-	process.title = `Misskey (${ cluster.isMaster ? 'master' : 'worker' })`;
+	process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`;
 
 	if (cluster.isMaster || program.disableClustering) {
 		masterMain();
@@ -154,11 +154,10 @@ async function init(): Promise<Config> {
 
 function checkMongoDb(config: Config) {
 	const mongoDBLogger = new Logger('MongoDB');
-	mongoDBLogger.info(`Host: ${config.mongodb.host}`);
-	mongoDBLogger.info(`Port: ${config.mongodb.port}`);
-	mongoDBLogger.info(`DB: ${config.mongodb.db}`);
-	if (config.mongodb.user) mongoDBLogger.info(`User: ${config.mongodb.user}`);
-	if (config.mongodb.pass) mongoDBLogger.info(`Pass: ****`);
+	const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null;
+	const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null;
+	const uri = `mongodb://${u && p ? `${u}:****@` : ''}${config.mongodb.host}:${config.mongodb.port}/${config.mongodb.db}`;
+	mongoDBLogger.info(`Connecting to ${uri}`);
 	require('./db/mongodb');
 	mongoDBLogger.succ('Connectivity confirmed');
 }

From 1ae51df74a51fcec31c1d2b895cb71b33d5ff258 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 00:42:20 +0900
Subject: [PATCH 091/134] Update .travis.yml

---
 .travis.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index f52fe7e3f5..cdb6edc965 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,7 +22,6 @@ addons:
       - ubuntu-toolchain-r-test
     packages:
       - g++-4.8
-      - graphicsmagick
 
 cache:
   directories:

From d2ef95a8c3340babd13692c5d23bf79badbedd97 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Thu, 16 Aug 2018 00:49:40 +0900
Subject: [PATCH 092/134] Make text unclickable

---
 src/client/app/desktop/views/components/user-preview.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue
index aa17890682..7ef8dff5be 100644
--- a/src/client/app/desktop/views/components/user-preview.vue
+++ b/src/client/app/desktop/views/components/user-preview.vue
@@ -10,13 +10,13 @@
 		<div class="description">{{ u.description }}</div>
 		<div class="status">
 			<div>
-				<p>%i18n:@notes%</p><a>{{ u.notesCount }}</a>
+				<p>%i18n:@notes%</p><span>{{ u.notesCount }}</span>
 			</div>
 			<div>
-				<p>%i18n:@following%</p><a>{{ u.followingCount }}</a>
+				<p>%i18n:@following%</p><span>{{ u.followingCount }}</span>
 			</div>
 			<div>
-				<p>%i18n:@followers%</p><a>{{ u.followersCount }}</a>
+				<p>%i18n:@followers%</p><span>{{ u.followersCount }}</span>
 			</div>
 		</div>
 		<mk-follow-button v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u"/>
@@ -149,7 +149,7 @@ root(isDark)
 				font-size 0.7em
 				color #aaa
 
-			> a
+			> span
 				font-size 1em
 				color $theme-color
 

From 6aff7375f69f27dd312ce20c7dcb98a91220dc88 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 00:59:48 +0900
Subject: [PATCH 093/134] Update backers

---
 README.md | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 9e1c834b40..a52ead65a7 100644
--- a/README.md
+++ b/README.md
@@ -43,9 +43,34 @@ If you want to...
 
 :heart: Backers & Sponsors
 ----------------------------------------------------------------
-| <img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=tB1e_r8RlZ5sFL0KV_e8dugapxatNBRK1Z3h67TO1g8%3D"> | <img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12378075/0156f769e20f412594fa6b87d85fe228/1?token-time=2145916800&token-hash=IsIJRUXszzoD6-7pDnRY8I05T9nSznc4GTaxj7C9SwU%3D"> | <img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D"> | <img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D"> |
-|:-:|:-:|:-:|:-:|
-| [Gargron](https://www.patreon.com/mastodon) | [39ff](https://www.patreon.com/user/creators?u=12378075) | [dansup](https://www.patreon.com/dansup) | [Takashi Shibuya](https://www.patreon.com/user/creators?u=12531784) |
+<table>
+  <tr>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1?token-time=2145916800&token-hash=tB1e_r8RlZ5sFL0KV_e8dugapxatNBRK1Z3h67TO1g8%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12378075/0156f769e20f412594fa6b87d85fe228/1?token-time=2145916800&token-hash=IsIJRUXszzoD6-7pDnRY8I05T9nSznc4GTaxj7C9SwU%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1?token-time=2145916800&token-hash=S1zP0QyLU52Dqq6dtc9qNYyWfW86XrYHiR4NMbeOrnA%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1?token-time=2145916800&token-hash=tMosUojzUYJCH_3t--tvYA-SMCyrS__hzSndyaRSnbo%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12959468/c249e15aebec4424b5c0f427173671b6/1?token-time=2145916800&token-hash=lubpCEdxAkxPlpR2O6bvZ7BIh8Q4nGf-U_mE1qpjVAQ%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/1?token-time=2145916800&token-hash=f03BFb4S2FUx9YEt87TnEmifb4h33OywGBW2akQVtQY%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/2?token-time=2145916800&token-hash=zElv7ZcPL3viGsXbNG_KWiKrbV0vvw1gk0panx8DJoo%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12731202/0995c46cdcb54153ab5f073f5869b70a/1?token-time=2145916800&token-hash=Yd60FK_SWfQO56SeiJpy1tDHOnCV4xdEywQe8gn5_Wo%3D"></td>
+    <td><img src="https://c8.patreon.com/2/100/12718187"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12931605/ead494101f364dffa90efe49e36fb494/1?token-time=2145916800&token-hash=NzSFPjIlodXyv41rwK61aZWVZWfI4surJaNj8vWKvqM%3D"></td>
+    <td><img src="https://c10.patreonusercontent.com/3/eyJoIjoxMDAsInciOjEwMH0%3D/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1?token-time=2145916800&token-hash=GgJ_NmUB6_nnRNLVGUWjV-WX91On7BOu59LKncYV9fE%3D"></td>
+  </tr>
+  <tr>
+    <td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
+    <td><a href="https://www.patreon.com/user/creators?u=12378075">39ff</a></td>
+    <td><a href="https://www.patreon.com/dansup">dansup</a></td>
+    <td><a href="https://www.patreon.com/user/creators?u=12531784">Takashi Shibuya</a></td>
+    <td><a href="https://www.patreon.com/fujishan">fujishan</a></td>
+    <td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
+    <td><a href="https://www.patreon.com/user?u=5881381">Naoki Kosaka</a></td>
+    <td><a href="https://www.patreon.com/user?u=12731202">negao</a></td>
+    <td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
+    <td><a href="https://www.patreon.com/user?u=12931605">Reiju</a></td>
+    <td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
+  </tr>
+</table>
 
 :four_leaf_clover: Copyright
 ----------------------------------------------------------------

From e8a7e95c6549438e471ff06ef147bf21d656a232 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 01:17:01 +0900
Subject: [PATCH 094/134] Update summaly

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 177846f810..3a3fef9696 100644
--- a/package.json
+++ b/package.json
@@ -190,7 +190,7 @@
 		"style-loader": "0.22.1",
 		"stylus": "0.54.5",
 		"stylus-loader": "3.0.2",
-		"summaly": "2.1.2",
+		"summaly": "2.1.3",
 		"systeminformation": "3.42.9",
 		"syuilo-password-strength": "0.0.1",
 		"textarea-caret": "3.1.0",

From 8e75f8a1251e1f4304da43fdb225c310fe0a4480 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 01:20:52 +0900
Subject: [PATCH 095/134] Fix

---
 .config/example.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.config/example.yml b/.config/example.yml
index ce092a08f3..a24bd3aed8 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -51,7 +51,7 @@ remoteDriveCapacityMb: 8
 #  Server will not cache remote files (Using direct link instead).
 #  You can save your storage.
 #  Users cannot see remote images when they turn off "Show media from a remote server" setting.
-preventCache: false
+preventCacheRemoteFiles: false
 
 drive:
   storage: 'db'

From 73c396cb39e9277d60fca60788dc390058c7cf6d Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 01:25:35 +0900
Subject: [PATCH 096/134] :star:

---
 src/client/app/common/views/components/note-header.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/common/views/components/note-header.vue b/src/client/app/common/views/components/note-header.vue
index 9bba6990db..d25bd430f2 100644
--- a/src/client/app/common/views/components/note-header.vue
+++ b/src/client/app/common/views/components/note-header.vue
@@ -2,7 +2,7 @@
 <header class="bvonvjxbwzaiskogyhbwgyxvcgserpmu">
 	<mk-avatar class="avatar" :user="note.user" v-if="$store.state.device.postStyle == 'smart'"/>
 	<router-link class="name" :to="note.user | userPage" v-user-preview="note.user.id">{{ note.user | userName }}</router-link>
-	<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:bookmark%</span>
+	<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
 	<span class="is-admin" v-if="note.user.isAdmin">admin</span>
 	<span class="is-bot" v-if="note.user.isBot">bot</span>
 	<span class="is-cat" v-if="note.user.isCat">cat</span>

From 0efbeb36df9a547f61c8087f71a42d8f31d49beb Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 01:29:11 +0900
Subject: [PATCH 097/134] 5.25.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 3a3fef9696..f71308dfd9 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.24.1",
-	"clientVersion": "1.0.8279",
+	"version": "5.25.0",
+	"clientVersion": "1.0.8348",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From 3c3d3e4c0c187815b47c9bf01b699a5f6518808a Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Thu, 16 Aug 2018 03:05:28 +0900
Subject: [PATCH 098/134] Fix url-preview.vue

---
 src/client/app/common/views/components/url-preview.vue | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index 4466cf7165..95dafa8f4c 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -160,12 +160,12 @@ export default Vue.extend({
 						'web.tv',
 						'youtube.com',
 						'youtu.be'
-					].some(x => x == url.hostname || url.hostname.endsWith(`.${x}`))))
+					].some(x => x == url.hostname || url.hostname.endsWith(`.${x}`)))
 						this.player = info.player;
-				}
-			});
-		}
-	}
+				}	// info.url
+			})	// json
+		});	// fetch
+	}	// created
 });
 </script>
 

From bf077da72f4ffbee423dc681363a3c910e7b4721 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 04:42:44 +0900
Subject: [PATCH 099/134] Trim code

---
 src/mfm/parse/elements/code.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/mfm/parse/elements/code.ts b/src/mfm/parse/elements/code.ts
index 04ce692ce1..3c97fc1d99 100644
--- a/src/mfm/parse/elements/code.ts
+++ b/src/mfm/parse/elements/code.ts
@@ -19,6 +19,6 @@ export default function(text: string) {
 		type: 'code',
 		content: code,
 		code: match[1],
-		html: genHtml(match[1])
+		html: genHtml(match[1].trim())
 	} as TextElementCode;
 }

From 0d33cbbbbb4711f3c47d2aa67d39035bcd60ac27 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Thu, 16 Aug 2018 04:57:09 +0900
Subject: [PATCH 100/134] Make reactions in reactions-viewer clickable

---
 .../views/components/reactions-viewer.vue     | 34 +++++++++++++------
 1 file changed, 24 insertions(+), 10 deletions(-)

diff --git a/src/client/app/common/views/components/reactions-viewer.vue b/src/client/app/common/views/components/reactions-viewer.vue
index 23f4b59d62..c30fa2a1dc 100644
--- a/src/client/app/common/views/components/reactions-viewer.vue
+++ b/src/client/app/common/views/components/reactions-viewer.vue
@@ -1,16 +1,16 @@
 <template>
 <div class="mk-reactions-viewer">
 	<template v-if="reactions">
-		<span v-if="reactions.like"><mk-reaction-icon reaction="like"/><span>{{ reactions.like }}</span></span>
-		<span v-if="reactions.love"><mk-reaction-icon reaction="love"/><span>{{ reactions.love }}</span></span>
-		<span v-if="reactions.laugh"><mk-reaction-icon reaction="laugh"/><span>{{ reactions.laugh }}</span></span>
-		<span v-if="reactions.hmm"><mk-reaction-icon reaction="hmm"/><span>{{ reactions.hmm }}</span></span>
-		<span v-if="reactions.surprise"><mk-reaction-icon reaction="surprise"/><span>{{ reactions.surprise }}</span></span>
-		<span v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span>
-		<span v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span>
-		<span v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span>
-		<span v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span>
-		<span v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span>
+		<span :class="{notReacted}" @click="react('like')" v-if="reactions.like"><mk-reaction-icon reaction="like"/><span>{{ reactions.like }}</span></span>
+		<span :class="{notReacted}" @click="react('love')" v-if="reactions.love"><mk-reaction-icon reaction="love"/><span>{{ reactions.love }}</span></span>
+		<span :class="{notReacted}" @click="react('laugh')" v-if="reactions.laugh"><mk-reaction-icon reaction="laugh"/><span>{{ reactions.laugh }}</span></span>
+		<span :class="{notReacted}" @click="react('hmm')" v-if="reactions.hmm"><mk-reaction-icon reaction="hmm"/><span>{{ reactions.hmm }}</span></span>
+		<span :class="{notReacted}" @click="react('surprise')" v-if="reactions.surprise"><mk-reaction-icon reaction="surprise"/><span>{{ reactions.surprise }}</span></span>
+		<span :class="{notReacted}" @click="react('congrats')" v-if="reactions.congrats"><mk-reaction-icon reaction="congrats"/><span>{{ reactions.congrats }}</span></span>
+		<span :class="{notReacted}" @click="react('angry')" v-if="reactions.angry"><mk-reaction-icon reaction="angry"/><span>{{ reactions.angry }}</span></span>
+		<span :class="{notReacted}" @click="react('confused')" v-if="reactions.confused"><mk-reaction-icon reaction="confused"/><span>{{ reactions.confused }}</span></span>
+		<span :class="{notReacted}" @click="react('rip')" v-if="reactions.rip"><mk-reaction-icon reaction="rip"/><span>{{ reactions.rip }}</span></span>
+		<span :class="{notReacted}" @click="react('pudding')" v-if="reactions.pudding"><mk-reaction-icon reaction="pudding"/><span>{{ reactions.pudding }}</span></span>
 	</template>
 </div>
 </template>
@@ -22,6 +22,17 @@ export default Vue.extend({
 	computed: {
 		reactions(): number {
 			return this.note.reactionCounts;
+		},
+		notReacted(): boolean {
+			return this.note.myReaction == null;
+		}
+	},
+	methods: {
+		react(reaction: string) {
+			(this as any).api('notes/reactions/create', {
+				noteId: this.note.id,
+				reaction: reaction
+			});
 		}
 	}
 });
@@ -40,6 +51,9 @@ root(isDark)
 	> span
 		margin-right 8px
 
+		&.notReacted
+			cursor pointer
+
 		> .mk-reaction-icon
 			font-size 1.4em
 

From fa95641f88df107abd17d8218272b40888e8535c Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Thu, 16 Aug 2018 05:15:06 +0900
Subject: [PATCH 101/134] Use Array.prototype.length to avoid magic number

---
 src/client/app/common/scripts/get-kao.ts | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts
index ca7cebaebc..58857edfe1 100644
--- a/src/client/app/common/scripts/get-kao.ts
+++ b/src/client/app/common/scripts/get-kao.ts
@@ -1,6 +1,8 @@
-export default () => [
+const kaos = [
 	'(=^・・^=)',
 	'v(\'ω\')v',
 	'🐡( \'-\' 🐡 )フグパンチ!!!!',
-	'🖕(´・_・`)🖕'
-][Math.floor(Math.random() * 4)];
+	'🖕(´・_・`)🖕',
+];
+
+export default () => kaos[Math.floor(Math.random() * kaos.length)];

From 270c7997c6c5ecf9d1e1491bf6729c2640bd4159 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Thu, 16 Aug 2018 05:15:51 +0900
Subject: [PATCH 102/134] Remove trailing comma

---
 src/client/app/common/scripts/get-kao.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts
index 58857edfe1..dff7092064 100644
--- a/src/client/app/common/scripts/get-kao.ts
+++ b/src/client/app/common/scripts/get-kao.ts
@@ -2,7 +2,7 @@ const kaos = [
 	'(=^・・^=)',
 	'v(\'ω\')v',
 	'🐡( \'-\' 🐡 )フグパンチ!!!!',
-	'🖕(´・_・`)🖕',
+	'🖕(´・_・`)🖕'
 ];
 
 export default () => kaos[Math.floor(Math.random() * kaos.length)];

From afa62d3d445be1d4dc4b2a0a50c3885523a04562 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Thu, 16 Aug 2018 05:17:00 +0900
Subject: [PATCH 103/134] Add new kao

---
 src/client/app/common/scripts/get-kao.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts
index dff7092064..ca83153b96 100644
--- a/src/client/app/common/scripts/get-kao.ts
+++ b/src/client/app/common/scripts/get-kao.ts
@@ -2,7 +2,8 @@ const kaos = [
 	'(=^・・^=)',
 	'v(\'ω\')v',
 	'🐡( \'-\' 🐡 )フグパンチ!!!!',
-	'🖕(´・_・`)🖕'
+	'🖕(´・_・`)🖕',
+	'(。>﹏<。)'
 ];
 
 export default () => kaos[Math.floor(Math.random() * kaos.length)];

From 286e15b967257fac312b6c2846b7958fe885283d Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Wed, 15 Aug 2018 20:48:36 +0000
Subject: [PATCH 104/134] fix(package): update @types/node to version 10.7.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index f71308dfd9..ed7b8b5f99 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
 		"@types/mocha": "5.2.3",
 		"@types/mongodb": "3.1.4",
 		"@types/ms": "0.7.30",
-		"@types/node": "10.7.0",
+		"@types/node": "10.7.1",
 		"@types/portscanner": "2.1.0",
 		"@types/pug": "2.0.4",
 		"@types/qrcode": "1.2.0",

From 1c7a1949501973014dfa6efa79a9d33d1a292a7e Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 06:27:35 +0900
Subject: [PATCH 105/134] wip

---
 src/services/drive/add-file.ts | 61 ++++++++++++++++++++++++++++++----
 1 file changed, 54 insertions(+), 7 deletions(-)

diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index 701d547776..277d628ac9 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -17,30 +17,52 @@ import { publishUserStream, publishDriveStream } from '../../stream';
 import { isLocalUser, IUser, IRemoteUser } from '../../models/user';
 import delFile from './delete-file';
 import config from '../../config';
+import { getDriveFileThumbnailBucket } from '../../models/drive-file-thumbnail';
 
 const log = debug('misskey:drive:add-file');
 
-async function save(readable: stream.Readable, name: string, type: string, hash: string, size: number, metadata: any): Promise<IDriveFile> {
+async function save(path: string, name: string, type: string, hash: string, size: number, metadata: any): Promise<IDriveFile> {
+	let thumbnail: Buffer;
+
+	if (['image/jpeg', 'image/png', 'image/webp'].includes(type)) {
+		thumbnail = await sharp(path)
+			.resize(500)
+			.jpeg({
+				quality: 70,
+				progressive: true
+			})
+			.toBuffer();
+	}
+
 	if (config.drive && config.drive.storage == 'minio') {
 		const minio = new Minio.Client(config.drive.config);
 		const id = uuid.v4();
 		const obj = `${config.drive.prefix}/${id}`;
+		const thumbnailObj = `${obj}-thumbnail`;
 
 		const baseUrl = config.drive.baseUrl
 			|| `${ config.drive.config.secure ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
 
-		await minio.putObject(config.drive.bucket, obj, readable, size, {
+		await minio.putObject(config.drive.bucket, obj, fs.createReadStream(path), size, {
 			'Content-Type': type,
 			'Cache-Control': 'max-age=31536000, immutable'
 		});
 
+		if (thumbnail) {
+			await minio.putObject(config.drive.bucket, thumbnailObj, fs.createReadStream(path), size, {
+				'Content-Type': 'image/jpeg',
+				'Cache-Control': 'max-age=31536000, immutable'
+			});
+		}
+
 		Object.assign(metadata, {
 			withoutChunks: true,
 			storage: 'minio',
 			storageProps: {
 				id: id
 			},
-			url: `${ baseUrl }/${ obj }`
+			url: `${ baseUrl }/${ obj }`,
+			thumbnailUrl: thumbnail ? `${ baseUrl }/${ thumbnailObj }` : null
 		});
 
 		const file = await DriveFile.insert({
@@ -57,12 +79,37 @@ async function save(readable: stream.Readable, name: string, type: string, hash:
 		// Get MongoDB GridFS bucket
 		const bucket = await getDriveFileBucket();
 
-		return new Promise<IDriveFile>((resolve, reject) => {
-			const writeStream = bucket.openUploadStream(name, { contentType: type, metadata });
+		const file = await new Promise<IDriveFile>((resolve, reject) => {
+			const writeStream = bucket.openUploadStream(name, {
+				contentType: type,
+				metadata
+			});
+
 			writeStream.once('finish', resolve);
 			writeStream.on('error', reject);
-			readable.pipe(writeStream);
+
+			fs.createReadStream(path).pipe(writeStream);
 		});
+
+		if (thumbnail) {
+			const thumbnailBucket = await getDriveFileThumbnailBucket();
+
+			await new Promise<IDriveFile>((resolve, reject) => {
+				const writeStream = thumbnailBucket.openUploadStream(name, {
+					contentType: 'image/jpeg',
+					metadata: {
+						originalId: file._id
+					}
+				});
+
+				writeStream.once('finish', resolve);
+				writeStream.on('error', reject);
+
+				fs.createReadStream(path).pipe(writeStream);
+			});
+		}
+
+		return file;
 	}
 }
 
@@ -321,7 +368,7 @@ export default async function(
 			}
 		}
 	} else {
-		driveFile = await (save(fs.createReadStream(path), detectedName, mime, hash, size, metadata));
+		driveFile = await (save(path, detectedName, mime, hash, size, metadata));
 	}
 
 	log(`drive file has been created ${driveFile._id}`);

From 61832620375cac0c942fe0c29f4c823eb83b5561 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 07:17:04 +0900
Subject: [PATCH 106/134] wip

---
 .../desktop/views/components/drive.file.vue   |  2 +-
 .../desktop/views/components/media-image.vue  |  2 +-
 .../mobile/views/components/media-image.vue   |  2 +-
 src/models/drive-file.ts                      |  2 ++
 src/server/file/send-drive-file.ts            | 27 +++++++------------
 src/services/drive/add-file.ts                |  8 +++---
 src/services/drive/delete-file.ts             |  6 +++++
 7 files changed, 23 insertions(+), 26 deletions(-)

diff --git a/src/client/app/desktop/views/components/drive.file.vue b/src/client/app/desktop/views/components/drive.file.vue
index 55218625c1..3b5be19dcf 100644
--- a/src/client/app/desktop/views/components/drive.file.vue
+++ b/src/client/app/desktop/views/components/drive.file.vue
@@ -16,7 +16,7 @@
 		<p>%i18n:@banner%</p>
 	</div>
 	<div class="thumbnail" ref="thumbnail" :style="`background-color: ${ background }`">
-		<img :src="file.url" alt="" @load="onThumbnailLoaded"/>
+		<img :src="file.thumbnailUrl" alt="" @load="onThumbnailLoaded"/>
 	</div>
 	<p class="name">
 		<span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
diff --git a/src/client/app/desktop/views/components/media-image.vue b/src/client/app/desktop/views/components/media-image.vue
index 74bb03f4ed..8b68f260fa 100644
--- a/src/client/app/desktop/views/components/media-image.vue
+++ b/src/client/app/desktop/views/components/media-image.vue
@@ -37,7 +37,7 @@ export default Vue.extend({
 		style(): any {
 			return {
 				'background-color': this.image.properties.avgColor && this.image.properties.avgColor.length == 3 ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
-				'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.url})`
+				'background-image': this.raw ? `url(${this.image.url})` : `url(${this.image.thumbnailUrl})`
 			};
 		}
 	},
diff --git a/src/client/app/mobile/views/components/media-image.vue b/src/client/app/mobile/views/components/media-image.vue
index d9d68fa7ba..e40069bbe3 100644
--- a/src/client/app/mobile/views/components/media-image.vue
+++ b/src/client/app/mobile/views/components/media-image.vue
@@ -27,7 +27,7 @@ export default Vue.extend({
 	},
 	computed: {
 		style(): any {
-			let url = `url(${this.image.url})`;
+			let url = `url(${this.image.thumbnailUrl})`;
 
 			if (this.$store.state.device.loadRemoteMedia || this.$store.state.device.lightmode) {
 				url = null;
diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index 38e1bf549c..358dd89441 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -31,6 +31,7 @@ export type IMetadata = {
 	comment: string;
 	uri?: string;
 	url?: string;
+	thumbnailUrl?: string;
 	src?: string;
 	deletedAt?: Date;
 	withoutChunks?: boolean;
@@ -164,6 +165,7 @@ export const pack = (
 	_target = Object.assign(_target, _file.metadata);
 
 	_target.url = _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
+	_target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
 	_target.isRemote = _file.metadata.isRemote;
 
 	if (_target.properties == null) _target.properties = {};
diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts
index 1a76b0e41f..b904bda91b 100644
--- a/src/server/file/send-drive-file.ts
+++ b/src/server/file/send-drive-file.ts
@@ -1,5 +1,3 @@
-import * as fs from 'fs';
-
 import * as Koa from 'koa';
 import * as send from 'koa-send';
 import * as mongodb from 'mongodb';
@@ -51,23 +49,16 @@ export default async function(ctx: Koa.Context) {
 	};
 
 	if ('thumbnail' in ctx.query) {
-		// 画像以外
-		if (!file.contentType.startsWith('image/')) {
-			const readable = fs.createReadStream(`${__dirname}/assets/thumbnail-not-available.png`);
-			ctx.set('Content-Type', 'image/png');
-			ctx.body = readable;
-		} else if (file.contentType == 'image/gif') {
-			// GIF
-			await sendRaw();
+		const thumb = await DriveFileThumbnail.findOne({
+			'metadata.originalId': fileId
+		});
+
+		if (thumb != null) {
+			ctx.set('Content-Type', 'image/jpeg');
+			const bucket = await getDriveFileThumbnailBucket();
+			ctx.body = bucket.openDownloadStream(thumb._id);
 		} else {
-			const thumb = await DriveFileThumbnail.findOne({ 'metadata.originalId': fileId });
-			if (thumb != null) {
-				ctx.set('Content-Type', 'image/jpeg');
-				const bucket = await getDriveFileThumbnailBucket();
-				ctx.body = bucket.openDownloadStream(thumb._id);
-			} else {
-				await sendRaw();
-			}
+			await sendRaw();
 		}
 	} else {
 		if ('download' in ctx.query) {
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index 277d628ac9..b8a2a33da4 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -1,6 +1,5 @@
 import { Buffer } from 'buffer';
 import * as fs from 'fs';
-import * as stream from 'stream';
 
 import * as mongodb from 'mongodb';
 import * as crypto from 'crypto';
@@ -26,9 +25,9 @@ async function save(path: string, name: string, type: string, hash: string, size
 
 	if (['image/jpeg', 'image/png', 'image/webp'].includes(type)) {
 		thumbnail = await sharp(path)
-			.resize(500)
+			.resize(300)
 			.jpeg({
-				quality: 70,
+				quality: 50,
 				progressive: true
 			})
 			.toBuffer();
@@ -104,8 +103,7 @@ async function save(path: string, name: string, type: string, hash: string, size
 
 				writeStream.once('finish', resolve);
 				writeStream.on('error', reject);
-
-				fs.createReadStream(path).pipe(writeStream);
+				writeStream.end(thumbnail);
 			});
 		}
 
diff --git a/src/services/drive/delete-file.ts b/src/services/drive/delete-file.ts
index 5494023f46..a417d260fa 100644
--- a/src/services/drive/delete-file.ts
+++ b/src/services/drive/delete-file.ts
@@ -6,8 +6,14 @@ import config from '../../config';
 export default async function(file: IDriveFile, isExpired = false) {
 	if (file.metadata.storage == 'minio') {
 		const minio = new Minio.Client(config.drive.config);
+
 		const obj = `${config.drive.prefix}/${file.metadata.storageProps.id}`;
 		await minio.removeObject(config.drive.bucket, obj);
+
+		if (file.metadata.thumbnailUrl) {
+			const thumbnailObj = `${config.drive.prefix}/${file.metadata.storageProps.id}-thumbnail`;
+			await minio.removeObject(config.drive.bucket, thumbnailObj);
+		}
 	}
 
 	// チャンクをすべて削除

From f91cccb6b11df6b3040411dc9f7e7a24c36f8b62 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Wed, 15 Aug 2018 22:12:19 +0000
Subject: [PATCH 107/134] fix(package): update @types/webpack to version 4.4.10

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index ed7b8b5f99..2141925f11 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,7 @@
 		"@types/systeminformation": "3.23.0",
 		"@types/tmp": "0.0.33",
 		"@types/uuid": "3.4.3",
-		"@types/webpack": "4.4.9",
+		"@types/webpack": "4.4.10",
 		"@types/webpack-stream": "3.2.10",
 		"@types/websocket": "0.0.39",
 		"@types/ws": "6.0.0",

From 3345733d007dce928382e12f7855585c80c8f941 Mon Sep 17 00:00:00 2001
From: "greenkeeper[bot]" <greenkeeper[bot]@users.noreply.github.com>
Date: Wed, 15 Aug 2018 22:26:17 +0000
Subject: [PATCH 108/134] fix(package): update minio to version 7.0.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 2141925f11..b1e722fd05 100644
--- a/package.json
+++ b/package.json
@@ -149,7 +149,7 @@
 		"loader-utils": "1.1.0",
 		"lodash.assign": "4.2.0",
 		"mecab-async": "0.1.2",
-		"minio": "6.0.0",
+		"minio": "7.0.0",
 		"mkdirp": "0.5.1",
 		"mocha": "5.2.0",
 		"moji": "0.5.1",

From ce16884587a6ce1baebdf5efe8b64cea12ca87d9 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 07:31:58 +0900
Subject: [PATCH 109/134] Update example.yml

---
 .config/example.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.config/example.yml b/.config/example.yml
index a24bd3aed8..9ea048f70b 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -50,7 +50,10 @@ remoteDriveCapacityMb: 8
 # If enabled:
 #  Server will not cache remote files (Using direct link instead).
 #  You can save your storage.
-#  Users cannot see remote images when they turn off "Show media from a remote server" setting.
+#
+#  NOTE:
+#  * Users cannot see remote images when they turn off "Show media from a remote server" setting.
+#  * Since thumbnails are not provided, traffic increases.
 preventCacheRemoteFiles: false
 
 drive:

From e7d9018944bdf32d179c19a96e645eb298b3732c Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 07:56:53 +0900
Subject: [PATCH 110/134] :v:

---
 src/client/app/mobile/views/components/drive.file.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/mobile/views/components/drive.file.vue b/src/client/app/mobile/views/components/drive.file.vue
index 776e11ecf8..c337629cb6 100644
--- a/src/client/app/mobile/views/components/drive.file.vue
+++ b/src/client/app/mobile/views/components/drive.file.vue
@@ -43,7 +43,7 @@ export default Vue.extend({
 		thumbnail(): any {
 			return {
 				'background-color': this.file.properties.avgColor && this.file.properties.avgColor.length == 3 ? `rgb(${this.file.properties.avgColor.join(',')})` : 'transparent',
-				'background-image': `url(${this.file.url})`
+				'background-image': `url(${this.file.thumbnailUrl})`
 			};
 		}
 	},

From 1fc9206c6db13d7d467ae2a1f927f8dad9a8a8e8 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 08:02:50 +0900
Subject: [PATCH 111/134] 6.0.0

---
 CHANGELOG.md | 9 +++++++++
 package.json | 4 ++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08ad99977f..9b5d8c8307 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ ChangeLog
 
 This document describes breaking changes only.
 
+6.0.0
+-----
+
+### Migration
+
+オブジェクトストレージを使用している場合、設定ファイルの`drive.config.secure`を`drive.config.useSSL`にリネームしてください。
+
+If you use object storage, please rename `drive.config.secure` to `drive.config.useSSL` in config.
+
 5.0.0
 -----
 
diff --git a/package.json b/package.json
index b1e722fd05..ed935cbb34 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "5.25.0",
-	"clientVersion": "1.0.8348",
+	"version": "6.0.0",
+	"clientVersion": "1.0.8367",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From ac15c2e71f7037fd251f1d36992cf566f0458a9a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 08:11:21 +0900
Subject: [PATCH 112/134] Fix bug

---
 package.json                   | 2 +-
 src/services/drive/add-file.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index ed935cbb34..9d0cb1cc2c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "6.0.0",
+	"version": "6.0.1",
 	"clientVersion": "1.0.8367",
 	"codename": "nighthike",
 	"main": "./built/index.js",
diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index b8a2a33da4..03bb1468bd 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -48,7 +48,7 @@ async function save(path: string, name: string, type: string, hash: string, size
 		});
 
 		if (thumbnail) {
-			await minio.putObject(config.drive.bucket, thumbnailObj, fs.createReadStream(path), size, {
+			await minio.putObject(config.drive.bucket, thumbnailObj, thumbnail, size, {
 				'Content-Type': 'image/jpeg',
 				'Cache-Control': 'max-age=31536000, immutable'
 			});

From 9d6641be3acdfbf434c77cd6e1127bb98c8d2846 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 08:18:41 +0900
Subject: [PATCH 113/134] Fix bug

---
 package.json             | 2 +-
 src/models/drive-file.ts | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 9d0cb1cc2c..52af4aa867 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "6.0.1",
+	"version": "6.0.2",
 	"clientVersion": "1.0.8367",
 	"codename": "nighthike",
 	"main": "./built/index.js",
diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts
index 358dd89441..2b9efc404d 100644
--- a/src/models/drive-file.ts
+++ b/src/models/drive-file.ts
@@ -165,7 +165,7 @@ export const pack = (
 	_target = Object.assign(_target, _file.metadata);
 
 	_target.url = _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}`;
-	_target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
+	_target.thumbnailUrl = _file.metadata.thumbnailUrl ? _file.metadata.thumbnailUrl : _file.metadata.url ? _file.metadata.url : `${config.drive_url}/${_target.id}/${encodeURIComponent(_target.name)}?thumbnail`;
 	_target.isRemote = _file.metadata.isRemote;
 
 	if (_target.properties == null) _target.properties = {};

From 7f9a35d7acf33c3c64ed451e10054729d25973cc Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 21:23:31 +0900
Subject: [PATCH 114/134] Improve object storage key

---
 src/services/drive/add-file.ts    | 16 ++++++++--------
 src/services/drive/delete-file.ts |  8 ++++++--
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/services/drive/add-file.ts b/src/services/drive/add-file.ts
index 03bb1468bd..da0d3fd82f 100644
--- a/src/services/drive/add-file.ts
+++ b/src/services/drive/add-file.ts
@@ -35,20 +35,19 @@ async function save(path: string, name: string, type: string, hash: string, size
 
 	if (config.drive && config.drive.storage == 'minio') {
 		const minio = new Minio.Client(config.drive.config);
-		const id = uuid.v4();
-		const obj = `${config.drive.prefix}/${id}`;
-		const thumbnailObj = `${obj}-thumbnail`;
+		const key = `${config.drive.prefix}/${uuid.v4()}/${name}`;
+		const thumbnailKey = `${config.drive.prefix}/${uuid.v4()}/${name}.thumbnail.jpg`;
 
 		const baseUrl = config.drive.baseUrl
 			|| `${ config.drive.config.secure ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
 
-		await minio.putObject(config.drive.bucket, obj, fs.createReadStream(path), size, {
+		await minio.putObject(config.drive.bucket, key, fs.createReadStream(path), size, {
 			'Content-Type': type,
 			'Cache-Control': 'max-age=31536000, immutable'
 		});
 
 		if (thumbnail) {
-			await minio.putObject(config.drive.bucket, thumbnailObj, thumbnail, size, {
+			await minio.putObject(config.drive.bucket, thumbnailKey, thumbnail, size, {
 				'Content-Type': 'image/jpeg',
 				'Cache-Control': 'max-age=31536000, immutable'
 			});
@@ -58,10 +57,11 @@ async function save(path: string, name: string, type: string, hash: string, size
 			withoutChunks: true,
 			storage: 'minio',
 			storageProps: {
-				id: id
+				key: key,
+				thumbnailKey: thumbnailKey
 			},
-			url: `${ baseUrl }/${ obj }`,
-			thumbnailUrl: thumbnail ? `${ baseUrl }/${ thumbnailObj }` : null
+			url: `${ baseUrl }/${ key }`,
+			thumbnailUrl: thumbnail ? `${ baseUrl }/${ thumbnailKey }` : null
 		});
 
 		const file = await DriveFile.insert({
diff --git a/src/services/drive/delete-file.ts b/src/services/drive/delete-file.ts
index a417d260fa..445d231d66 100644
--- a/src/services/drive/delete-file.ts
+++ b/src/services/drive/delete-file.ts
@@ -7,11 +7,15 @@ export default async function(file: IDriveFile, isExpired = false) {
 	if (file.metadata.storage == 'minio') {
 		const minio = new Minio.Client(config.drive.config);
 
-		const obj = `${config.drive.prefix}/${file.metadata.storageProps.id}`;
+		// 後方互換性のため、file.metadata.storageProps.key があるかどうかチェックしています。
+		// 将来的には const obj = file.metadata.storageProps.key; とします。
+		const obj = file.metadata.storageProps.key ? file.metadata.storageProps.key : `${config.drive.prefix}/${file.metadata.storageProps.id}`;
 		await minio.removeObject(config.drive.bucket, obj);
 
 		if (file.metadata.thumbnailUrl) {
-			const thumbnailObj = `${config.drive.prefix}/${file.metadata.storageProps.id}-thumbnail`;
+			// 後方互換性のため、file.metadata.storageProps.thumbnailKey があるかどうかチェックしています。
+			// 将来的には const thumbnailObj = file.metadata.storageProps.thumbnailKey; とします。
+			const thumbnailObj = file.metadata.storageProps.thumbnailKey ? file.metadata.storageProps.thumbnailKey : `${config.drive.prefix}/${file.metadata.storageProps.id}-thumbnail`;
 			await minio.removeObject(config.drive.bucket, thumbnailObj);
 		}
 	}

From e5c350d7405c04238883b23f5cbfa2c16fe3c51f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 23:33:11 +0900
Subject: [PATCH 115/134] Fix bug

---
 src/server/api/endpoints/users/notes.ts | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index c60050d3cd..ff7855bde0 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -16,17 +16,13 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
 	if (usernameErr) return rej('invalid username param');
 
 	if (userId === undefined && username === undefined) {
-		return rej('userId or pair of username and host is required');
+		return rej('userId or username is required');
 	}
 
 	// Get 'host' parameter
 	const [host, hostErr] = $.str.optional.get(params.host);
 	if (hostErr) return rej('invalid host param');
 
-	if (userId === undefined && host === undefined) {
-		return rej('userId or pair of username and host is required');
-	}
-
 	// Get 'includeReplies' parameter
 	const [includeReplies = true, includeRepliesErr] = $.bool.optional.get(params.includeReplies);
 	if (includeRepliesErr) return rej('invalid includeReplies param');

From 22e79675ad0bb8722864ceb683a260addb909f9d Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Thu, 16 Aug 2018 23:59:22 +0900
Subject: [PATCH 116/134] #2263

---
 locales/ja.yml                                |   2 +
 .../app/desktop/views/components/notes.vue    |   6 +
 .../app/desktop/views/components/settings.vue |   7 +
 .../views/components/timeline.core.vue        |   6 +-
 .../views/components/user-list-timeline.vue   |   6 +-
 .../desktop/views/pages/deck/deck.list-tl.vue |   6 +-
 .../desktop/views/pages/deck/deck.notes.vue   |   6 +
 .../app/desktop/views/pages/deck/deck.tl.vue  |   6 +-
 .../app/mobile/views/components/notes.vue     |   6 +
 .../views/components/user-list-timeline.vue   |   6 +-
 .../app/mobile/views/pages/home.timeline.vue  |   6 +-
 .../app/mobile/views/pages/settings.vue       |   8 +
 src/client/app/store.ts                       |   1 +
 .../api/endpoints/notes/hybrid-timeline.ts    |  23 +++
 src/server/api/endpoints/notes/timeline.ts    |  23 +++
 .../api/endpoints/notes/user-list-timeline.ts | 153 +++++++++++-------
 16 files changed, 205 insertions(+), 66 deletions(-)

diff --git a/locales/ja.yml b/locales/ja.yml
index 9c3daf450f..112b4b194c 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -710,6 +710,7 @@ desktop/views/components/settings.vue:
   show-reply-target: "リプライ先を表示する"
   show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
   show-renoted-my-notes: "Renoteされた自分の投稿をタイムラインに表示する"
+  show-local-renotes: "Renoteされたローカルの投稿の投稿をタイムラインに表示する"
   show-maps: "マップの自動展開"
   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。"
 
@@ -1294,6 +1295,7 @@ mobile/views/pages/settings.vue:
   show-reply-target: "リプライ先を表示する"
   show-my-renotes: "自分の行ったRenoteを表示する"
   show-renoted-my-notes: "Renoteされた自分の投稿を表示する"
+  show-local-renotes: "Renoteされたローカルの投稿の投稿をタイムラインに表示する"
   post-style: "投稿の表示スタイル"
   post-style-standard: "標準"
   post-style-smart: "スマート"
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 02167ef85c..efb9db70fa 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -135,6 +135,12 @@ export default Vue.extend({
 					return;
 				}
 			}
+
+			if (this.$store.state.settings.showLocalRenotes === false) {
+				if (isPureRenote && (note.renote.user.host == null)) {
+					return;
+				}
+			}
 			//#endregion
 
 			// 投稿が自分のものではないかつ、タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index 84ea768a5c..fa21410d39 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -51,6 +51,7 @@
 			<mk-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget" text="%i18n:@show-reply-target%"/>
 			<mk-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes" text="%i18n:@show-my-renotes%"/>
 			<mk-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
+			<mk-switch v-model="$store.state.settings.showLocalRenotes" @change="onChangeShowLocalRenotes" text="%i18n:@show-local-renotes%"/>
 			<mk-switch v-model="$store.state.settings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
 				<span>%i18n:@show-maps-desc%</span>
 			</mk-switch>
@@ -353,6 +354,12 @@ export default Vue.extend({
 				value: v
 			});
 		},
+		onChangeShowLocalRenotes(v) {
+			this.$store.dispatch('settings/set', {
+				key: 'showLocalRenotes',
+				value: v
+			});
+		},
 		onChangeShowMaps(v) {
 			this.$store.dispatch('settings/set', {
 				key: 'showMaps',
diff --git a/src/client/app/desktop/views/components/timeline.core.vue b/src/client/app/desktop/views/components/timeline.core.vue
index 15e188be67..25fd5d36ac 100644
--- a/src/client/app/desktop/views/components/timeline.core.vue
+++ b/src/client/app/desktop/views/components/timeline.core.vue
@@ -100,7 +100,8 @@ export default Vue.extend({
 					limit: fetchLimit + 1,
 					untilDate: this.date ? this.date.getTime() : undefined,
 					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 				}).then(notes => {
 					if (notes.length == fetchLimit + 1) {
 						notes.pop();
@@ -122,7 +123,8 @@ export default Vue.extend({
 				limit: fetchLimit + 1,
 				untilId: (this.$refs.timeline as any).tail().id,
 				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 			});
 
 			promise.then(notes => {
diff --git a/src/client/app/desktop/views/components/user-list-timeline.vue b/src/client/app/desktop/views/components/user-list-timeline.vue
index 03ac81a4a1..0a6f758763 100644
--- a/src/client/app/desktop/views/components/user-list-timeline.vue
+++ b/src/client/app/desktop/views/components/user-list-timeline.vue
@@ -47,7 +47,8 @@ export default Vue.extend({
 					listId: this.list.id,
 					limit: fetchLimit + 1,
 					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 				}).then(notes => {
 					if (notes.length == fetchLimit + 1) {
 						notes.pop();
@@ -67,7 +68,8 @@ export default Vue.extend({
 				limit: fetchLimit + 1,
 				untilId: (this.$refs.timeline as any).tail().id,
 				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 			});
 
 			promise.then(notes => {
diff --git a/src/client/app/desktop/views/pages/deck/deck.list-tl.vue b/src/client/app/desktop/views/pages/deck/deck.list-tl.vue
index d2f46bd8be..70048f99e3 100644
--- a/src/client/app/desktop/views/pages/deck/deck.list-tl.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.list-tl.vue
@@ -70,7 +70,8 @@ export default Vue.extend({
 					limit: fetchLimit + 1,
 					mediaOnly: this.mediaOnly,
 					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 				}).then(notes => {
 					if (notes.length == fetchLimit + 1) {
 						notes.pop();
@@ -91,7 +92,8 @@ export default Vue.extend({
 				untilId: (this.$refs.timeline as any).tail().id,
 				mediaOnly: this.mediaOnly,
 				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 			});
 
 			promise.then(notes => {
diff --git a/src/client/app/desktop/views/pages/deck/deck.notes.vue b/src/client/app/desktop/views/pages/deck/deck.notes.vue
index 3578e17287..f7fca5de92 100644
--- a/src/client/app/desktop/views/pages/deck/deck.notes.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.notes.vue
@@ -140,6 +140,12 @@ export default Vue.extend({
 					return;
 				}
 			}
+
+			if (this.$store.state.settings.showLocalRenotes === false) {
+				if (isPureRenote && (note.renote.user.host == null)) {
+					return;
+				}
+			}
 			//#endregion
 
 			if (this.isScrollTop()) {
diff --git a/src/client/app/desktop/views/pages/deck/deck.tl.vue b/src/client/app/desktop/views/pages/deck/deck.tl.vue
index d402ee6a8b..a9e4d489c3 100644
--- a/src/client/app/desktop/views/pages/deck/deck.tl.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.tl.vue
@@ -98,7 +98,8 @@ export default Vue.extend({
 					limit: fetchLimit + 1,
 					mediaOnly: this.mediaOnly,
 					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 				}).then(notes => {
 					if (notes.length == fetchLimit + 1) {
 						notes.pop();
@@ -119,7 +120,8 @@ export default Vue.extend({
 				mediaOnly: this.mediaOnly,
 				untilId: (this.$refs.timeline as any).tail().id,
 				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 			});
 
 			promise.then(notes => {
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index aed372d9a2..cce81d1b78 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -139,6 +139,12 @@ export default Vue.extend({
 					return;
 				}
 			}
+
+			if (this.$store.state.settings.showLocalRenotes === false) {
+				if (isPureRenote && (note.renote.user.host == null)) {
+					return;
+				}
+			}
 			//#endregion
 
 			// 投稿が自分のものではないかつ、タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
diff --git a/src/client/app/mobile/views/components/user-list-timeline.vue b/src/client/app/mobile/views/components/user-list-timeline.vue
index 2c1564b7ed..9b3f11f5c2 100644
--- a/src/client/app/mobile/views/components/user-list-timeline.vue
+++ b/src/client/app/mobile/views/components/user-list-timeline.vue
@@ -59,7 +59,8 @@ export default Vue.extend({
 					listId: this.list.id,
 					limit: fetchLimit + 1,
 					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 				}).then(notes => {
 					if (notes.length == fetchLimit + 1) {
 						notes.pop();
@@ -82,7 +83,8 @@ export default Vue.extend({
 				limit: fetchLimit + 1,
 				untilId: (this.$refs.timeline as any).tail().id,
 				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 			});
 
 			promise.then(notes => {
diff --git a/src/client/app/mobile/views/pages/home.timeline.vue b/src/client/app/mobile/views/pages/home.timeline.vue
index 93d1364e09..416b006cd8 100644
--- a/src/client/app/mobile/views/pages/home.timeline.vue
+++ b/src/client/app/mobile/views/pages/home.timeline.vue
@@ -95,7 +95,8 @@ export default Vue.extend({
 					limit: fetchLimit + 1,
 					untilDate: this.date ? this.date.getTime() : undefined,
 					includeMyRenotes: this.$store.state.settings.showMyRenotes,
-					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+					includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+					includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 				}).then(notes => {
 					if (notes.length == fetchLimit + 1) {
 						notes.pop();
@@ -117,7 +118,8 @@ export default Vue.extend({
 				limit: fetchLimit + 1,
 				untilId: (this.$refs.timeline as any).tail().id,
 				includeMyRenotes: this.$store.state.settings.showMyRenotes,
-				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes
+				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
+				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 			});
 
 			promise.then(notes => {
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index f74b734b6e..7636a0370a 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -21,6 +21,7 @@
 					<ui-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget">%i18n:@show-reply-target%</ui-switch>
 					<ui-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes">%i18n:@show-my-renotes%</ui-switch>
 					<ui-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes">%i18n:@show-renoted-my-notes%</ui-switch>
+					<ui-switch v-model="$store.state.settings.showLocalRenotes" @change="onChangeShowLocalRenotes">%i18n:@show-local-renotes%</ui-switch>
 				</div>
 
 				<div>
@@ -221,6 +222,13 @@ export default Vue.extend({
 			});
 		},
 
+		onChangeShowLocalRenotes(v) {
+			this.$store.dispatch('settings/set', {
+				key: 'showLocalRenotes',
+				value: v
+			});
+		},
+
 		checkForUpdate() {
 			this.checkingForUpdate = true;
 			checkForUpdate((this as any).os, true, true).then(newer => {
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index dfb24bb5f4..487b12df8b 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -16,6 +16,7 @@ const defaultSettings = {
 	showReplyTarget: true,
 	showMyRenotes: true,
 	showRenotedMyNotes: true,
+	showLocalRenotes: true,
 	loadRemoteMedia: true,
 	disableViaMobile: false,
 	memo: null,
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 036f84b54b..3fce4fb9af 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -59,6 +59,13 @@ export const meta = {
 			}
 		}),
 
+		includeLocalRenotes: $.bool.optional.note({
+			default: true,
+			desc: {
+				ja: 'Renoteされたローカルの投稿を含めるかどうか'
+			}
+		}),
+
 		mediaOnly: $.bool.optional.note({
 			desc: {
 				ja: 'true にすると、メディアが添付された投稿だけ取得します'
@@ -180,6 +187,22 @@ export default async (params: any, user: ILocalUser) => {
 		});
 	}
 
+	if (ps.includeLocalRenotes === false) {
+		query.$and.push({
+			$or: [{
+				'_renote.user.host': { $ne: null }
+			}, {
+				renoteId: null
+			}, {
+				text: { $ne: null }
+			}, {
+				mediaIds: { $ne: [] }
+			}, {
+				poll: { $ne: null }
+			}]
+		});
+	}
+
 	if (ps.mediaOnly) {
 		query.$and.push({
 			mediaIds: { $exists: true, $ne: [] }
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index c1b8644e4d..3e3fa8c4aa 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -60,6 +60,13 @@ export const meta = {
 			}
 		}),
 
+		includeLocalRenotes: $.bool.optional.note({
+			default: true,
+			desc: {
+				ja: 'Renoteされたローカルの投稿を含めるかどうか'
+			}
+		}),
+
 		mediaOnly: $.bool.optional.note({
 			desc: {
 				ja: 'true にすると、メディアが添付された投稿だけ取得します'
@@ -170,6 +177,22 @@ export default async (params: any, user: ILocalUser) => {
 		});
 	}
 
+	if (ps.includeLocalRenotes === false) {
+		query.$and.push({
+			$or: [{
+				'_renote.user.host': { $ne: null }
+			}, {
+				renoteId: null
+			}, {
+				text: { $ne: null }
+			}, {
+				mediaIds: { $ne: [] }
+			}, {
+				poll: { $ne: null }
+			}]
+		});
+	}
+
 	if (ps.mediaOnly) {
 		query.$and.push({
 			mediaIds: { $exists: true, $ne: [] }
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index 5837a9a301..dcef548666 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -4,6 +4,7 @@ import Mute from '../../../../models/mute';
 import { pack } from '../../../../models/note';
 import UserList from '../../../../models/user-list';
 import { ILocalUser } from '../../../../models/user';
+import getParams from '../../get-params';
 
 export const meta = {
 	desc: {
@@ -11,56 +12,84 @@ export const meta = {
 		en: 'Get timeline of a user list.'
 	},
 
-	requireCredential: true
+	requireCredential: true,
+
+	params: {
+		listId: $.type(ID).note({
+			desc: {
+				ja: 'リストのID'
+			}
+		}),
+
+		limit: $.num.optional.range(1, 100).note({
+			default: 10,
+			desc: {
+				ja: '最大数'
+			}
+		}),
+
+		sinceId: $.type(ID).optional.note({
+			desc: {
+				ja: '指定すると、この投稿を基点としてより新しい投稿を取得します'
+			}
+		}),
+
+		untilId: $.type(ID).optional.note({
+			desc: {
+				ja: '指定すると、この投稿を基点としてより古い投稿を取得します'
+			}
+		}),
+
+		sinceDate: $.num.optional.note({
+			desc: {
+				ja: '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+			}
+		}),
+
+		untilDate: $.num.optional.note({
+			desc: {
+				ja: '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
+			}
+		}),
+
+		includeMyRenotes: $.bool.optional.note({
+			default: true,
+			desc: {
+				ja: '自分の行ったRenoteを含めるかどうか'
+			}
+		}),
+
+		includeRenotedMyNotes: $.bool.optional.note({
+			default: true,
+			desc: {
+				ja: 'Renoteされた自分の投稿を含めるかどうか'
+			}
+		}),
+
+		includeLocalRenotes: $.bool.optional.note({
+			default: true,
+			desc: {
+				ja: 'Renoteされたローカルの投稿を含めるかどうか'
+			}
+		}),
+
+		mediaOnly: $.bool.optional.note({
+			desc: {
+				ja: 'true にすると、メディアが添付された投稿だけ取得します'
+			}
+		}),
+	}
 };
 
 export default async (params: any, user: ILocalUser) => {
-	// Get 'limit' parameter
-	const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
-	if (limitErr) throw 'invalid limit param';
-
-	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
-	if (sinceIdErr) throw 'invalid sinceId param';
-
-	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
-	if (untilIdErr) throw 'invalid untilId param';
-
-	// Get 'sinceDate' parameter
-	const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
-	if (sinceDateErr) throw 'invalid sinceDate param';
-
-	// Get 'untilDate' parameter
-	const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
-	if (untilDateErr) throw 'invalid untilDate param';
-
-	// Check if only one of sinceId, untilId, sinceDate, untilDate specified
-	if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) {
-		throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
-	}
-
-	// Get 'includeMyRenotes' parameter
-	const [includeMyRenotes = true, includeMyRenotesErr] = $.bool.optional.get(params.includeMyRenotes);
-	if (includeMyRenotesErr) throw 'invalid includeMyRenotes param';
-
-	// Get 'includeRenotedMyNotes' parameter
-	const [includeRenotedMyNotes = true, includeRenotedMyNotesErr] = $.bool.optional.get(params.includeRenotedMyNotes);
-	if (includeRenotedMyNotesErr) throw 'invalid includeRenotedMyNotes param';
-
-	// Get 'mediaOnly' parameter
-	const [mediaOnly, mediaOnlyErr] = $.bool.optional.get(params.mediaOnly);
-	if (mediaOnlyErr) throw 'invalid mediaOnly param';
-
-	// Get 'listId' parameter
-	const [listId, listIdErr] = $.type(ID).get(params.listId);
-	if (listIdErr) throw 'invalid listId param';
+	const [ps, psErr] = getParams(meta, params);
+	if (psErr) throw psErr;
 
 	const [list, mutedUserIds] = await Promise.all([
 		// リストを取得
 		// Fetch the list
 		UserList.findOne({
-			_id: listId,
+			_id: ps.listId,
 			userId: user._id
 		}),
 
@@ -122,7 +151,7 @@ export default async (params: any, user: ILocalUser) => {
 	// つまり、「『自分の投稿かつRenote』ではない」を「『自分の投稿ではない』または『Renoteではない』」と表現します。
 	// for details: https://en.wikipedia.org/wiki/De_Morgan%27s_laws
 
-	if (includeMyRenotes === false) {
+	if (ps.includeMyRenotes === false) {
 		query.$and.push({
 			$or: [{
 				userId: { $ne: user._id }
@@ -138,7 +167,7 @@ export default async (params: any, user: ILocalUser) => {
 		});
 	}
 
-	if (includeRenotedMyNotes === false) {
+	if (ps.includeRenotedMyNotes === false) {
 		query.$and.push({
 			$or: [{
 				'_renote.userId': { $ne: user._id }
@@ -154,29 +183,45 @@ export default async (params: any, user: ILocalUser) => {
 		});
 	}
 
-	if (mediaOnly) {
+	if (ps.includeLocalRenotes === false) {
+		query.$and.push({
+			$or: [{
+				'_renote.user.host': { $ne: null }
+			}, {
+				renoteId: null
+			}, {
+				text: { $ne: null }
+			}, {
+				mediaIds: { $ne: [] }
+			}, {
+				poll: { $ne: null }
+			}]
+		});
+	}
+
+	if (ps.mediaOnly) {
 		query.$and.push({
 			mediaIds: { $exists: true, $ne: [] }
 		});
 	}
 
-	if (sinceId) {
+	if (ps.sinceId) {
 		sort._id = 1;
 		query._id = {
-			$gt: sinceId
+			$gt: ps.sinceId
 		};
-	} else if (untilId) {
+	} else if (ps.untilId) {
 		query._id = {
-			$lt: untilId
+			$lt: ps.untilId
 		};
-	} else if (sinceDate) {
+	} else if (ps.sinceDate) {
 		sort._id = 1;
 		query.createdAt = {
-			$gt: new Date(sinceDate)
+			$gt: new Date(ps.sinceDate)
 		};
-	} else if (untilDate) {
+	} else if (ps.untilDate) {
 		query.createdAt = {
-			$lt: new Date(untilDate)
+			$lt: new Date(ps.untilDate)
 		};
 	}
 	//#endregion
@@ -184,7 +229,7 @@ export default async (params: any, user: ILocalUser) => {
 	// Issue query
 	const timeline = await Note
 		.find(query, {
-			limit: limit,
+			limit: ps.limit,
 			sort: sort
 		});
 

From d5379e2b36dc4dd566a6c08dfee7d51cf6d83b64 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:04:07 +0900
Subject: [PATCH 117/134] =?UTF-8?q?autoWatch=E3=82=92=E3=83=87=E3=83=95?=
 =?UTF-8?q?=E3=82=A9=E3=83=AB=E3=83=88=E3=81=A7false=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/server/api/private/signup.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts
index 563b6ca845..16ec33bcbf 100644
--- a/src/server/api/private/signup.ts
+++ b/src/server/api/private/signup.ts
@@ -92,7 +92,7 @@ export default async (ctx: Koa.Context) => {
 			weight: null
 		},
 		settings: {
-			autoWatch: true
+			autoWatch: false
 		}
 	});
 

From b78d24be1eb0def86c70c236576206bcf2967e86 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:05:57 +0900
Subject: [PATCH 118/134] Fix bug

---
 src/services/note/create.ts | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index b219d1ca51..eccc4a7fcb 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -328,8 +328,18 @@ async function insertNote(user: IUser, data: Option, tokens: ReturnType<typeof p
 			: [],
 
 		// 以下非正規化データ
-		_reply: data.reply ? { userId: data.reply.userId } : null,
-		_renote: data.renote ? { userId: data.renote.userId } : null,
+		_reply: data.reply ? {
+			userId: data.reply.userId,
+			user: {
+				host: data.reply._user.host
+			}
+		} : null,
+		_renote: data.renote ? {
+			userId: data.renote.userId,
+			user: {
+				host: data.reply._user.host
+			}
+		} : null,
 		_user: {
 			host: user.host,
 			inbox: isRemoteUser(user) ? user.inbox : undefined

From ee12d887ae351ed93101f03075f5eaf11e127cb2 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:07:23 +0900
Subject: [PATCH 119/134] typo

---
 src/services/note/create.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index eccc4a7fcb..3745450e15 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -337,7 +337,7 @@ async function insertNote(user: IUser, data: Option, tokens: ReturnType<typeof p
 		_renote: data.renote ? {
 			userId: data.renote.userId,
 			user: {
-				host: data.reply._user.host
+				host: data.renote._user.host
 			}
 		} : null,
 		_user: {

From c5d734f9ad18ab5ec158480c5194fb0bba080cae Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:09:01 +0900
Subject: [PATCH 120/134] 6.1.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 52af4aa867..ce9b0db985 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "6.0.2",
-	"clientVersion": "1.0.8367",
+	"version": "6.1.0",
+	"clientVersion": "1.0.8377",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From a6e0471f8c401bf75dbc78a2891638ea877da236 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:13:52 +0900
Subject: [PATCH 121/134] :v:

---
 locales/ja.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/locales/ja.yml b/locales/ja.yml
index 112b4b194c..07cc0e9a1c 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -709,8 +709,8 @@ desktop/views/components/settings.vue:
   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する"
   show-reply-target: "リプライ先を表示する"
   show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
-  show-renoted-my-notes: "Renoteされた自分の投稿をタイムラインに表示する"
-  show-local-renotes: "Renoteされたローカルの投稿の投稿をタイムラインに表示する"
+  show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する"
+  show-local-renotes: "ローカルの投稿のRenoteをタイムラインに表示する"
   show-maps: "マップの自動展開"
   show-maps-desc: "位置情報が添付された投稿のマップを自動的に展開します。"
 
@@ -1294,8 +1294,8 @@ mobile/views/pages/settings.vue:
   timeline: "タイムライン"
   show-reply-target: "リプライ先を表示する"
   show-my-renotes: "自分の行ったRenoteを表示する"
-  show-renoted-my-notes: "Renoteされた自分の投稿を表示する"
-  show-local-renotes: "Renoteされたローカルの投稿の投稿をタイムラインに表示する"
+  show-renoted-my-notes: "自分の投稿のRenoteを表示する"
+  show-local-renotes: "ローカルの投稿のRenoteを表示する"
   post-style: "投稿の表示スタイル"
   post-style-standard: "標準"
   post-style-smart: "スマート"

From f3be077adcf6db8baa95c1f059a54e264f53eb31 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:19:27 +0900
Subject: [PATCH 122/134] Fix bug

---
 .../app/mobile/views/pages/settings/settings.profile.vue       | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/client/app/mobile/views/pages/settings/settings.profile.vue b/src/client/app/mobile/views/pages/settings/settings.profile.vue
index da97cbebd7..78023ba501 100644
--- a/src/client/app/mobile/views/pages/settings/settings.profile.vue
+++ b/src/client/app/mobile/views/pages/settings/settings.profile.vue
@@ -61,7 +61,6 @@ export default Vue.extend({
 			birthday: null,
 			avatarId: null,
 			bannerId: null,
-			isBot: false,
 			isCat: false,
 			saving: false,
 			avatarUploading: false,
@@ -77,7 +76,6 @@ export default Vue.extend({
 		this.birthday = this.$store.state.i.profile.birthday;
 		this.avatarId = this.$store.state.i.avatarId;
 		this.bannerId = this.$store.state.i.bannerId;
-		this.isBot = this.$store.state.i.isBot;
 		this.isCat = this.$store.state.i.isCat;
 	},
 
@@ -136,7 +134,6 @@ export default Vue.extend({
 				birthday: this.birthday || null,
 				avatarId: this.avatarId,
 				bannerId: this.bannerId,
-				isBot: this.isBot,
 				isCat: this.isCat
 			}).then(i => {
 				this.saving = false;

From ffdaa6bc56a0fed7fc8235996925f109ff9ea3ba Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:27:36 +0900
Subject: [PATCH 123/134] Use thumbnail

---
 src/client/app/desktop/views/components/post-form.vue | 2 +-
 src/client/app/mobile/views/components/post-form.vue  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/client/app/desktop/views/components/post-form.vue b/src/client/app/desktop/views/components/post-form.vue
index 334a457504..f504ce3deb 100644
--- a/src/client/app/desktop/views/components/post-form.vue
+++ b/src/client/app/desktop/views/components/post-form.vue
@@ -23,7 +23,7 @@
 		<div class="medias" :class="{ with: poll }" v-show="files.length != 0">
 			<x-draggable :list="files" :options="{ animation: 150 }">
 				<div v-for="file in files" :key="file.id">
-					<div class="img" :style="{ backgroundImage: `url(${file.url})` }" :title="file.name"></div>
+					<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
 					<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" title="%i18n:@attach-cancel%" alt=""/>
 				</div>
 			</x-draggable>
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index 1c71d0d46f..625fa45ac1 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -21,7 +21,7 @@
 			<div class="attaches" v-show="files.length != 0">
 				<x-draggable class="files" :list="files" :options="{ animation: 150 }">
 					<div class="file" v-for="file in files" :key="file.id">
-						<div class="img" :style="`background-image: url(${file.url})`" @click="detachMedia(file)"></div>
+						<div class="img" :style="`background-image: url(${file.thumbnailUrl})`" @click="detachMedia(file)"></div>
 					</div>
 				</x-draggable>
 			</div>

From b780ea336ccb85c5027d0f0e0afb9bf79c188d53 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 00:39:30 +0900
Subject: [PATCH 124/134] 6.2.0

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index ce9b0db985..3d8df6aaee 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
 	"name": "misskey",
 	"author": "syuilo <i@syuilo.com>",
-	"version": "6.1.0",
-	"clientVersion": "1.0.8377",
+	"version": "6.2.0",
+	"clientVersion": "1.0.8417",
 	"codename": "nighthike",
 	"main": "./built/index.js",
 	"private": true,

From d70e27a8654f3652972c8295e202e8d37c8131fd Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 17 Aug 2018 02:37:20 +0900
Subject: [PATCH 125/134] Provide id in announce activity

---
 src/remote/activitypub/renderer/announce.ts | 3 ++-
 src/services/note/create.ts                 | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/remote/activitypub/renderer/announce.ts b/src/remote/activitypub/renderer/announce.ts
index 8c3ff9f113..7a3f886447 100644
--- a/src/remote/activitypub/renderer/announce.ts
+++ b/src/remote/activitypub/renderer/announce.ts
@@ -1,4 +1,5 @@
-export default (object: any) => ({
+export default (id: string, object: any) => ({
 	type: 'Announce',
+	id,
 	object
 });
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 3745450e15..d11a02ada6 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -235,7 +235,7 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 
 async function renderActivity(data: Option, note: INote) {
 	const content = data.renote && data.text == null
-		? renderAnnounce(data.renote.uri ? data.renote.uri : await renderNote(data.renote))
+		? renderAnnounce(note._id.toHexString(), data.renote.uri ? data.renote.uri : await renderNote(data.renote))
 		: renderCreate(await renderNote(note));
 
 	return packAp(content);

From 7b94cf9f849e50044fdc5968b910c04976e4bd38 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Fri, 17 Aug 2018 03:08:17 +0900
Subject: [PATCH 126/134] Add a space between the words

---
 src/client/app/common/views/components/signin.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index 50c50cbe08..cdfc75971c 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -12,7 +12,7 @@
 	</ui-input>
 	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required/>
 	<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
-	<p style="margin: 8px 0;" v-if="twitterIntegration">%i18n:@or%<a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
+	<p style="margin: 8px 0;" v-if="twitterIntegration">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
 </form>
 </template>
 

From e93ea66d2d14f32518755c6df9a9419c71504e7f Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Fri, 17 Aug 2018 03:47:08 +0900
Subject: [PATCH 127/134] Update verified-user in ja.yml

---
 locales/ja.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/locales/ja.yml b/locales/ja.yml
index 07cc0e9a1c..0bb268d7a4 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -90,7 +90,7 @@ common:
   my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
   i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
   show-reversi-board-labels: "リバーシのボードの行と列のラベルを表示"
-  verified-user: "認証済みのユーザー"
+  verified-user: "公式アカウント"
   disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
 
   reversi:

From 39060374c212ddb5168d2aae6d6d8f74e11b86f5 Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Fri, 17 Aug 2018 03:51:42 +0900
Subject: [PATCH 128/134] Fix `npm run debug` not working

---
 src/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/index.ts b/src/index.ts
index c2994c606c..880bb5a452 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -28,7 +28,7 @@ import { Config } from './config/types';
 const clusterLog = debug('misskey:cluster');
 const ev = new Xev();
 
-if (process.env.NODE_ENV != 'production') {
+if (process.env.NODE_ENV != 'production' && process.env.DEBUG == null) {
 	debug.enable('misskey');
 }
 

From 431383ab546dd8a66bfd47dde8252000b6967b40 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Fri, 17 Aug 2018 05:33:20 +0900
Subject: [PATCH 129/134] Clean up

---
 src/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/index.ts b/src/index.ts
index 880bb5a452..086e665679 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -112,7 +112,7 @@ async function workerMain() {
 async function init(): Promise<Config> {
 	Logger.info('Welcome to Misskey!');
 
-	(new Logger('Deps')).info(`Node.js ${process.version}`);
+	new Logger('Deps').info(`Node.js ${process.version}`);
 	MachineInfo.show();
 	EnvironmentInfo.show();
 	new DependencyInfo().showAll();

From 48dc1678c383e09fcb6b542d6f8b59bce8d6dda4 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Fri, 17 Aug 2018 06:03:03 +0900
Subject: [PATCH 130/134] Resolve #2276

---
 locales/ja.yml                                        | 1 +
 src/client/app/desktop/views/components/post-form.vue | 2 +-
 src/client/app/desktop/views/components/settings.vue  | 7 +++++++
 src/client/app/mobile/views/components/post-form.vue  | 2 +-
 src/client/app/store.ts                               | 1 +
 5 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/locales/ja.yml b/locales/ja.yml
index 0bb268d7a4..888fb08c96 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -707,6 +707,7 @@ desktop/views/components/settings.vue:
   circle-icons: "円形のアイコンを使用"
   gradient-window-header: "ウィンドウのタイトルバーにグラデーションを使用"
   post-form-on-timeline: "タイムライン上部に投稿フォームを表示する"
+  suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
   show-reply-target: "リプライ先を表示する"
   show-my-renotes: "自分の行ったRenoteをタイムラインに表示する"
   show-renoted-my-notes: "自分の投稿のRenoteをタイムラインに表示する"
diff --git a/src/client/app/desktop/views/components/post-form.vue b/src/client/app/desktop/views/components/post-form.vue
index f504ce3deb..ea51144173 100644
--- a/src/client/app/desktop/views/components/post-form.vue
+++ b/src/client/app/desktop/views/components/post-form.vue
@@ -10,7 +10,7 @@
 			<span v-for="u in visibleUsers">{{ u | userName }}<a @click="removeVisibleUser(u)">[x]</a></span>
 			<a @click="addVisibleUser">%i18n:@add-visible-user%</a>
 		</div>
-		<div class="hashtags" v-if="recentHashtags.length > 0">
+		<div class="hashtags" v-if="recentHashtags.length > 0 && $store.state.settings.suggestRecentHashtags">
 			<b>%i18n:@recent-tags%:</b>
 			<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" title="%@click-to-tagging%">#{{ tag }}</a>
 		</div>
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index fa21410d39..e249afed85 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -48,6 +48,7 @@
 				<mk-switch v-model="$store.state.settings.iLikeSushi" @change="onChangeILikeSushi" text="%i18n:common.i-like-sushi%"/>
 			</div>
 			<mk-switch v-model="$store.state.settings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
+			<mk-switch v-model="$store.state.settings.suggestRecentHashtags" @change="onChangeSuggestRecentHashtags" text="%i18n:@suggest-recent-hashtags%"/>
 			<mk-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget" text="%i18n:@show-reply-target%"/>
 			<mk-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes" text="%i18n:@show-my-renotes%"/>
 			<mk-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
@@ -336,6 +337,12 @@ export default Vue.extend({
 				value: v
 			});
 		},
+		onChangeSuggestRecentHashtags(v) {
+			this.$store.dispatch('settings/set', {
+				key: 'suggestRecentHashtags',
+				value: v
+			});
+		},
 		onChangeShowReplyTarget(v) {
 			this.$store.dispatch('settings/set', {
 				key: 'showReplyTarget',
diff --git a/src/client/app/mobile/views/components/post-form.vue b/src/client/app/mobile/views/components/post-form.vue
index 625fa45ac1..702bc4c9e1 100644
--- a/src/client/app/mobile/views/components/post-form.vue
+++ b/src/client/app/mobile/views/components/post-form.vue
@@ -45,7 +45,7 @@
 			<input ref="file" class="file" type="file" accept="image/*" multiple="multiple" @change="onChangeFile"/>
 		</div>
 	</div>
-	<div class="hashtags" v-if="recentHashtags.length > 0">
+	<div class="hashtags" v-if="recentHashtags.length > 0 && $store.state.settings.suggestRecentHashtags">
 		<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)">#{{ tag }}</a>
 	</div>
 </div>
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index 487b12df8b..f85253a281 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -11,6 +11,7 @@ const defaultSettings = {
 	fetchOnScroll: true,
 	showMaps: true,
 	showPostFormOnTopOfTl: false,
+	suggestRecentHashtags: true,
 	circleIcons: true,
 	gradientWindowHeader: false,
 	showReplyTarget: true,

From a89c20657262441701bb50cd83d9d317940fc857 Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Fri, 17 Aug 2018 06:40:08 +0900
Subject: [PATCH 131/134] Fix Announce Activity

---
 .../activitypub/kernel/announce/note.ts       |  4 +++-
 src/remote/activitypub/renderer/announce.ts   | 20 ++++++++++++++-----
 src/services/note/create.ts                   |  4 ++--
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/src/remote/activitypub/kernel/announce/note.ts b/src/remote/activitypub/kernel/announce/note.ts
index 7aa6aa5707..60f2b9baa0 100644
--- a/src/remote/activitypub/kernel/announce/note.ts
+++ b/src/remote/activitypub/kernel/announce/note.ts
@@ -40,11 +40,13 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity:
 	if (!note.to.includes('https://www.w3.org/ns/activitystreams#Public')) {
 		if (note.cc.includes('https://www.w3.org/ns/activitystreams#Public')) {
 			visibility = 'home';
+		} else if (note.to.includes(`${actor.uri}/followers`)) {	// TODO: person.followerと照合するべき?
+			visibility = 'followers';
 		} else {
 			visibility = 'specified';
 			visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri)));
 		}
-	}	if (activity.cc.length == 0) visibility = 'followers';
+	}
 	//#endergion
 
 	await post(actor, {
diff --git a/src/remote/activitypub/renderer/announce.ts b/src/remote/activitypub/renderer/announce.ts
index 7a3f886447..6d5a67b5c3 100644
--- a/src/remote/activitypub/renderer/announce.ts
+++ b/src/remote/activitypub/renderer/announce.ts
@@ -1,5 +1,15 @@
-export default (id: string, object: any) => ({
-	type: 'Announce',
-	id,
-	object
-});
+import config from '../../../config';
+import { INote } from '../../../models/note';
+
+export default (object: any, note: INote) => {
+	const attributedTo = `${config.url}/users/${note.userId}`;
+
+	return {
+		id: `${config.url}/notes/${note._id}`,
+		type: 'Announce',
+		published: note.createdAt.toISOString(),
+		to: ['https://www.w3.org/ns/activitystreams#Public'],
+		cc: [attributedTo, `${attributedTo}/followers`],
+		object
+	};
+};
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index d11a02ada6..dd00867867 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -235,8 +235,8 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 
 async function renderActivity(data: Option, note: INote) {
 	const content = data.renote && data.text == null
-		? renderAnnounce(note._id.toHexString(), data.renote.uri ? data.renote.uri : await renderNote(data.renote))
-		: renderCreate(await renderNote(note));
+	? renderAnnounce(data.renote.uri ? data.renote.uri : await renderNote(data.renote), note)
+	: renderCreate(await renderNote(note));
 
 	return packAp(content);
 }

From 939c0dc5f9d6e1113ea236081ff9dbbf42d1d44e Mon Sep 17 00:00:00 2001
From: mei23 <m@m544.net>
Date: Fri, 17 Aug 2018 06:46:22 +0900
Subject: [PATCH 132/134] Fix indent

---
 src/services/note/create.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index dd00867867..521750dc84 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -235,8 +235,8 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 
 async function renderActivity(data: Option, note: INote) {
 	const content = data.renote && data.text == null
-	? renderAnnounce(data.renote.uri ? data.renote.uri : await renderNote(data.renote), note)
-	: renderCreate(await renderNote(note));
+		? renderAnnounce(data.renote.uri ? data.renote.uri : await renderNote(data.renote), note)
+		: renderCreate(await renderNote(note));
 
 	return packAp(content);
 }

From a507b7c0b01c65cc108cfbc56b355003ef6e3cc1 Mon Sep 17 00:00:00 2001
From: Aya Morisawa <AyaMorisawa4869@gmail.com>
Date: Fri, 17 Aug 2018 07:16:56 +0900
Subject: [PATCH 133/134] Enhance note footer

---
 .../desktop/views/components/note-detail.vue  | 21 ++++++++++-----
 .../desktop/views/components/notes.note.vue   | 26 +++++++++++--------
 2 files changed, 30 insertions(+), 17 deletions(-)

diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index b6980fae72..227bcc349d 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -55,15 +55,15 @@
 		</div>
 		<footer>
 			<mk-reactions-viewer :note="p"/>
-			<button @click="reply" title="">
+			<button class="replyButton" @click="reply" title="">
 				<template v-if="p.reply">%fa:reply-all%</template>
 				<template v-else>%fa:reply%</template>
 				<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
 			</button>
-			<button @click="renote" title="%i18n:@renote%">
+			<button class="renoteButton" @click="renote" title="%i18n:@renote%">
 				%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
 			</button>
-			<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:@add-reaction%">
+			<button class="reactionButton" :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:@add-reaction%">
 				%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
 			</button>
 			<button @click="menu" ref="menuButton">
@@ -372,15 +372,24 @@ root(isDark)
 				cursor pointer
 
 				&:hover
-					color isDark ? #9198af : #666
+					color isDark ? #a1a8bf : #444
+
+				&.replyButton:hover
+					color #0af
+
+				&.renoteButton:hover
+					color #8d0
+
+				&.reactionButton:hover
+					color #fa0
 
 				> .count
 					display inline
 					margin 0 0 0 8px
 					color #999
 
-				&.reacted
-					color $theme-color
+				&.reacted, &.reacted:hover
+					color #fa0
 
 	> .replies
 		> *
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index a98df104a3..87acf7974d 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -42,15 +42,15 @@
 			</div>
 			<footer>
 				<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
-				<button @click="reply" title="%i18n:@reply%">
+				<button class="replyButton" @click="reply" title="%i18n:@reply%">
 					<template v-if="p.reply">%fa:reply-all%</template>
 					<template v-else>%fa:reply%</template>
 					<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
 				</button>
-				<button @click="renote" title="%i18n:@renote%">
+				<button class="renoteButton" @click="renote" title="%i18n:@renote%">
 					%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
 				</button>
-				<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:@add-reaction%">
+				<button class="reactionButton" :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:@add-reaction%">
 					%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
 				</button>
 				<button @click="menu" ref="menuButton">
@@ -487,20 +487,24 @@ root(isDark)
 					cursor pointer
 
 					&:hover
-						color isDark ? #9198af : #666
+						color isDark ? #a1a8bf : #444
+
+					&.replyButton:hover
+						color #0af
+
+					&.renoteButton:hover
+						color #8d0
+
+					&.reactionButton:hover
+						color #fa0
 
 					> .count
 						display inline
 						margin 0 0 0 8px
 						color #999
 
-					&.reacted
-						color $theme-color
-
-					&:last-child
-						position absolute
-						right 0
-						margin 0
+					&.reacted, &.reacted:hover
+						color #fa0
 
 	> .detail
 		padding-top 4px

From 9476a240d9ca36836a8d8955b472aaad3d71199e Mon Sep 17 00:00:00 2001
From: sei0o <sei0o@live.jp>
Date: Fri, 17 Aug 2018 12:34:07 +0900
Subject: [PATCH 134/134] =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3?=
 =?UTF-8?q?=E5=A4=B1=E6=95=97=E6=99=82=E3=81=AE=E3=83=A1=E3=83=83=E3=82=BB?=
 =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=82=92=E6=94=B9=E5=96=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/en.yml                                    | 1 +
 locales/ja.yml                                    | 1 +
 src/client/app/common/views/components/signin.vue | 2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/locales/en.yml b/locales/en.yml
index cbf64b2be9..ba6adfbca1 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -287,6 +287,7 @@ common/views/components/signin.vue:
   signin: "Sign in"
   or: "Or"
   signin-with-twitter: "Log in with Twitter"
+  login-failed: "Log in failed. Make sure you have entered your correct username and password."
 common/views/components/signup.vue:
   username: "Username"
   checking: "Confirming..."
diff --git a/locales/ja.yml b/locales/ja.yml
index 888fb08c96..310a73a64e 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -314,6 +314,7 @@ common/views/components/signin.vue:
   signin: "サインイン"
   or: "または"
   signin-with-twitter: "Twitterでログイン"
+  login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
 
 common/views/components/signup.vue:
   username: "ユーザー名"
diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue
index cdfc75971c..0c3cceb1b3 100644
--- a/src/client/app/common/views/components/signin.vue
+++ b/src/client/app/common/views/components/signin.vue
@@ -60,7 +60,7 @@ export default Vue.extend({
 			}).then(() => {
 				location.reload();
 			}).catch(() => {
-				alert('something happened');
+				alert('%i18n:@login-failed%');
 				this.signing = false;
 			});
 		}