From 67bf6ff3ce7d4ecb5b68c0f3638c790c69ce9cff Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Oct 2021 00:13:54 +0900
Subject: [PATCH 1/5] :art:

---
 src/client/pages/announcements.vue | 10 +++++++---
 src/client/pages/featured.vue      |  6 +++---
 src/client/pages/mentions.vue      |  6 +++---
 src/client/pages/messages.vue      |  4 ++--
 src/client/style.scss              |  1 +
 5 files changed, 16 insertions(+), 11 deletions(-)

diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue
index 327fa9b1f6..429d183d1e 100644
--- a/src/client/pages/announcements.vue
+++ b/src/client/pages/announcements.vue
@@ -1,7 +1,7 @@
 <template>
-<div class="_section">
+<MkSpacer :content-max="800">
 	<MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content">
-		<section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id">
+		<section class="_card announcement" v-for="(announcement, i) in items" :key="announcement.id">
 			<div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div>
 			<div class="_content">
 				<Mfm :text="announcement.text"/>
@@ -12,7 +12,7 @@
 			</div>
 		</section>
 	</MkPagination>
-</div>
+</MkSpacer>
 </template>
 
 <script lang="ts">
@@ -58,6 +58,10 @@ export default defineComponent({
 <style lang="scss" scoped>
 .ruryvtyk {
 	> .announcement {
+		&:not(:last-child) {
+			margin-bottom: var(--margin);
+		}
+
 		> ._content {
 			> img {
 				display: block;
diff --git a/src/client/pages/featured.vue b/src/client/pages/featured.vue
index afe3d62638..50df26bfb1 100644
--- a/src/client/pages/featured.vue
+++ b/src/client/pages/featured.vue
@@ -1,7 +1,7 @@
 <template>
-<div class="_section">
-	<XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/>
-</div>
+<MkSpacer :content-max="800">
+	<XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/>
+</MkSpacer>
 </template>
 
 <script lang="ts">
diff --git a/src/client/pages/mentions.vue b/src/client/pages/mentions.vue
index 00c6a3d4d0..04682a856a 100644
--- a/src/client/pages/mentions.vue
+++ b/src/client/pages/mentions.vue
@@ -1,7 +1,7 @@
 <template>
-<div class="_section">
-	<XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/>
-</div>
+<MkSpacer :content-max="800">
+	<XNotes :pagination="pagination" @before="before()" @after="after()"/>
+</MkSpacer>
 </template>
 
 <script lang="ts">
diff --git a/src/client/pages/messages.vue b/src/client/pages/messages.vue
index 15cd9546be..e3d668cf45 100644
--- a/src/client/pages/messages.vue
+++ b/src/client/pages/messages.vue
@@ -1,7 +1,7 @@
 <template>
-<div>
+<MkSpacer :content-max="800">
 	<XNotes :pagination="pagination" @before="before()" @after="after()"/>
-</div>
+</MkSpacer>
 </template>
 
 <script lang="ts">
diff --git a/src/client/style.scss b/src/client/style.scss
index 0275efa8be..d77cdacf7b 100644
--- a/src/client/style.scss
+++ b/src/client/style.scss
@@ -348,6 +348,7 @@ hr {
 	contain: layout; // ふき出しがボックスから飛び出て表示されるようなデザインをする場合もあるので paint は contain することができない
 }
 
+// TODO: 廃止
 ._monolithic_ {
 	._section:not(:empty) {
 		box-sizing: border-box;

From e52a9e0a6529a9c739c5237c165b68861a9390dc Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Oct 2021 02:28:18 +0900
Subject: [PATCH 2/5] feat(client): Improve image viewer

Resolve #7545
Resolve #6811
Close #7808
---
 CHANGELOG.md                          |   5 ++
 package.json                          |   1 +
 src/client/components/media-image.vue |  12 ---
 src/client/components/media-list.vue  | 107 ++++++++++++++------------
 yarn.lock                             |   4 +
 5 files changed, 67 insertions(+), 62 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0bb427e8e9..44e928144d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,11 +10,16 @@
 ## 12.x.x (unreleased)
 
 ### Improvements
+- クライアント: 画像ビューアを強化
 - クライアント: メンションにユーザーのアバターを表示するように
 - クライアント: デザインの調整
 - クライアント: twemojiをセルフホスティングするように
 
 ### Bugfixes
+- クライアント: CWで画像が隠されたとき、画像の高さがおかしいことになる問題を修正
+
+### NOTE
+- このバージョンから、iOS 15未満のサポートがされなくなります。対象のバージョンをお使いの方は、iOSのバージョンアップを行ってください。
 
 ## 12.93.2 (2021/10/23)
 
diff --git a/package.json b/package.json
index 3d43caba68..0e231aa3b5 100644
--- a/package.json
+++ b/package.json
@@ -183,6 +183,7 @@
 		"os-utils": "0.0.14",
 		"parse5": "6.0.1",
 		"pg": "8.7.1",
+		"photoswipe": "git://github.com/dimsemenov/photoswipe#v5-beta",
 		"portscanner": "2.2.0",
 		"postcss": "8.3.11",
 		"postcss-loader": "6.2.0",
diff --git a/src/client/components/media-image.vue b/src/client/components/media-image.vue
index 863eb10272..fd5e0b5f9b 100644
--- a/src/client/components/media-image.vue
+++ b/src/client/components/media-image.vue
@@ -12,7 +12,6 @@
 	<a
 		:href="image.url"
 		:title="image.name"
-		@click.prevent="onClick"
 	>
 		<ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment" :title="image.comment" :cover="false"/>
 		<div class="gif" v-if="image.type === 'image/gif'">GIF</div>
@@ -73,17 +72,6 @@ export default defineComponent({
 			immediate: true,
 		});
 	},
-	methods: {
-		onClick() {
-			if (this.$store.state.imageNewTab) {
-				window.open(this.image.url, '_blank');
-			} else {
-				os.popup(ImageViewer, {
-					image: this.image
-				}, {}, 'closed');
-			}
-		}
-	}
 });
 </script>
 
diff --git a/src/client/components/media-list.vue b/src/client/components/media-list.vue
index 71767a0f9f..c1ec6147f1 100644
--- a/src/client/components/media-list.vue
+++ b/src/client/components/media-list.vue
@@ -1,11 +1,11 @@
 <template>
-<div class="mk-media-list">
+<div class="hoawjimk">
 	<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :media="media" :key="media.id"/>
-	<div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container" ref="gridOuter">
-		<div :data-count="mediaList.filter(media => previewable(media)).length" :style="gridInnerStyle">
+	<div v-if="mediaList.filter(media => previewable(media)).length > 0" class="gird-container">
+		<div :data-count="mediaList.filter(media => previewable(media)).length" ref="gallery">
 			<template v-for="media in mediaList">
 				<XVideo :video="media" :key="media.id" v-if="media.type.startsWith('video')"/>
-				<XImage :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
+				<XImage class="image" :data-id="media.id" :image="media" :key="media.id" v-else-if="media.type.startsWith('image')" :raw="raw"/>
 			</template>
 		</div>
 	</div>
@@ -13,11 +13,16 @@
 </template>
 
 <script lang="ts">
-import { defineComponent } from 'vue';
+import { defineComponent, onMounted, PropType, ref } from 'vue';
+import * as misskey from 'misskey-js';
+import PhotoSwipeLightbox from 'photoswipe/dist/photoswipe-lightbox.esm.js';
+import PhotoSwipe from 'photoswipe/dist/photoswipe.esm.js';
+import 'photoswipe/dist/photoswipe.css';
 import XBanner from './media-banner.vue';
 import XImage from './media-image.vue';
 import XVideo from './media-video.vue';
 import * as os from '@client/os';
+import { defaultStore } from '@client/store';
 
 export default defineComponent({
 	components: {
@@ -27,63 +32,65 @@ export default defineComponent({
 	},
 	props: {
 		mediaList: {
-			required: true
+			type: Array as PropType<misskey.entities.DriveFile[]>,
+			required: true,
 		},
 		raw: {
 			default: false
 		},
 	},
-	data() {
-		return {
-			gridInnerStyle: {},
-			sizeWaiting: false
-		}
-	},
-	mounted() {
-		this.size();
-		window.addEventListener('resize', this.size);
-	},
-	beforeUnmount() {
-		window.removeEventListener('resize', this.size);
-	},
-	activated() {
-		this.size();
-	},
-	methods: {
-		previewable(file) {
-			return file.type.startsWith('video') || file.type.startsWith('image');
-		},
-		size() {
-			// for Safari bug
-			if (this.sizeWaiting) return;
+	setup(props) {
+		const gallery = ref(null);
 
-			this.sizeWaiting = true;
-
-			window.requestAnimationFrame(() => {
-				this.sizeWaiting = false;
-
-				if (this.$refs.gridOuter) {
-					let height = 287;
-					const parent = this.$parent.$el;
-
-					if (this.$refs.gridOuter.clientHeight) {
-						height = this.$refs.gridOuter.clientHeight;
-					} else if (parent) {
-						height = parent.getBoundingClientRect().width * 9 / 16;
-					}
-
-					this.gridInnerStyle = { height: `${height}px` };
-				} else {
-					this.gridInnerStyle = {};
-				}
+		onMounted(() => {
+			const lightbox = new PhotoSwipeLightbox({
+				dataSource: props.mediaList.filter(media => media.type.startsWith('image')).map(media => ({
+					src: media.url,
+					w: media.properties.width,
+					h: media.properties.height,
+					alt: media.name,
+				})),
+				gallery: gallery.value,
+				children: '.image',
+				thumbSelector: '.image',
+				pswpModule: PhotoSwipe
 			});
-		}
+
+			lightbox.on('itemData', (e) => {
+				const { itemData } = e;
+
+				// element is children
+				const { element } = itemData;
+
+				console.log(element);
+
+				const id = element.dataset.id;
+				const file = props.mediaList.find(media => media.id === id);
+
+				itemData.src = file.url;
+				itemData.w = Number(file.properties.width);
+				itemData.h = Number(file.properties.height);
+				itemData.msrc = file.thumbnailUrl;
+				itemData.thumbCropped = true;
+			});
+
+			lightbox.init();
+		});
+
+		const previewable = (file: misskey.entities.DriveFile): boolean => {
+			return file.type.startsWith('video') || file.type.startsWith('image');
+		};
+
+		return {
+			previewable,
+			gallery,
+		};
 	},
 });
 </script>
 
 <style lang="scss" scoped>
-.mk-media-list {
+.hoawjimk {
 	> .gird-container {
 		position: relative;
 		width: 100%;
diff --git a/yarn.lock b/yarn.lock
index 4a0ac63f5c..e29de996bf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8271,6 +8271,10 @@ pgpass@1.x:
   dependencies:
     split "^1.0.0"
 
+"photoswipe@git://github.com/dimsemenov/photoswipe#v5-beta":
+  version "5.1.7"
+  resolved "git://github.com/dimsemenov/photoswipe#60040164333bd257409669e715e4327afdb3aec7"
+
 picocolors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"

From 5d0ee544f4fe78390ee9b99bf8c7706c7191cf51 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Oct 2021 02:30:31 +0900
Subject: [PATCH 3/5] chore: clean up

---
 src/client/components/media-list.vue | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/client/components/media-list.vue b/src/client/components/media-list.vue
index c1ec6147f1..c499525d84 100644
--- a/src/client/components/media-list.vue
+++ b/src/client/components/media-list.vue
@@ -62,8 +62,6 @@ export default defineComponent({
 				// element is children
 				const { element } = itemData;
 
-				console.log(element);
-
 				const id = element.dataset.id;
 				const file = props.mediaList.find(media => media.id === id);
 

From a21070c01bf13f494da6a84aa9845fd8e4884379 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Oct 2021 02:34:29 +0900
Subject: [PATCH 4/5] New Crowdin updates (#7912)

* New translations ja-JP.yml (Chinese Simplified)

* New translations stream.md (Chinese Simplified)

* New translations stream.md (Chinese Simplified)
---
 locales/zh-CN.yml                 |  2 ++
 src/docs/zh-CN/advanced/stream.md | 22 +++++++++++-----------
 2 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 9bcf917d6c..1764c366cf 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -795,7 +795,9 @@ itsOff: "已关闭"
 emailRequiredForSignup: "注册账户需要电子邮件地址"
 unread: "未读"
 filter: "筛选"
+controlPanel: "控制面板"
 manageAccounts: "管理账户"
+classic: "经典"
 _signup:
   almostThere: "即将完成"
   emailAddressInfo: "请输入您所使用的电子邮件地址"
diff --git a/src/docs/zh-CN/advanced/stream.md b/src/docs/zh-CN/advanced/stream.md
index 3351f2d839..16093e0b96 100644
--- a/src/docs/zh-CN/advanced/stream.md
+++ b/src/docs/zh-CN/advanced/stream.md
@@ -34,8 +34,8 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
 
 ひとつのストリーム上で、同時に複数のチャンネルに接続することができます。
 
-### チャンネルに接続する
-チャンネルに接続するには、次のようなデータをJSONでストリームに送信します:
+### 连接到频道
+要连接到频道,请将JSON数据发送到流:
 
 ```json
 {
@@ -51,16 +51,16 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
 ```
 
 其中:
-* `channel`には接続したいチャンネル名を設定します。チャンネルの種類については後述します。
+* `channel`には接続したいチャンネル名を設定します。频道类型将在后面说明。
 * `id`にはそのチャンネルとやり取りするための任意のIDを設定します。ストリームでは様々なメッセージが流れるので、そのメッセージがどのチャンネルからのものなのか識別する必要があるからです。このIDは、UUIDや、乱数のようなもので構いません。
 * `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。
 
 <div class="info">ℹ️ IDはチャンネルごとではなく「チャンネルの接続ごと」です。なぜなら、同じチャンネルに異なるパラメータで複数接続するケースもあるからです。</div>
 
-### チャンネルからのメッセージを受け取る
+### 从频道接收消息
 例えばタイムラインのチャンネルなら、新しい投稿があった時にメッセージを発します。そのメッセージを受け取ることで、タイムラインに新しい投稿がされたことをリアルタイムで知ることができます。
 
-チャンネルがメッセージを発すると、次のようなデータがJSONでストリームに流れてきます:
+当频道发送消息时,以下数据将以JSON格式传输到流中:
 ```json
 {
     type: 'channel',
@@ -79,10 +79,10 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
 * `type`にはメッセージの種類が設定されます。チャンネルによって、どのような種類のメッセージが流れてくるかは異なります。
 * `body`にはメッセージの内容が設定されます。チャンネルによって、どのような内容のメッセージが流れてくるかは異なります。
 
-### チャンネルに向けてメッセージを送信する
+### 向频道发送消息
 チャンネルによっては、メッセージを受け取るだけでなく、こちらから何かメッセージを送信し、何らかの操作を行える場合があります。
 
-チャンネルにメッセージを送信するには、次のようなデータをJSONでストリームに送信します:
+要将消息发送到频道,请将JSON格式数据发送到流:
 ```json
 {
     type: 'channel',
@@ -101,7 +101,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
 * `type`にはメッセージの種類を設定します。チャンネルによって、どのような種類のメッセージを受け付けるかは異なります。
 * `body`にはメッセージの内容を設定します。チャンネルによって、どのような内容のメッセージを受け付けるかは異なります。
 
-### チャンネルから切断する
+### 断开频道连接
 チャンネルから切断するには、次のようなデータをJSONでストリームに送信します:
 
 ```json
@@ -116,7 +116,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり
 其中:
 * `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。
 
-## ストリームを経由してAPIリクエストする
+## 通过流发送API请求
 
 ストリームを経由してAPIリクエストすると、HTTPリクエストを発生させずにAPIを利用できます。そのため、コードを簡潔にできたり、パフォーマンスの向上を見込めるかもしれません。
 
@@ -207,7 +207,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
 * `body`内の`type`に、イベントの種類が設定されます。
 * `body`内の`body`に、イベントの詳細が設定されます。
 
-#### イベントの種類
+#### 事件类型
 
 ##### `reacted`
 その投稿にリアクションがされた時に発生します。
@@ -233,7 +233,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま
 ##### `deleted`
 その投稿が削除された時に発生します。
 
-* `deletedAt`に、削除日時が設定されます。
+* `deletedAt`表示删除的日期和时间。
 
 例:
 ```json

From a905188e9554ca3dbd9d6106f54343798805967b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Oct 2021 02:34:46 +0900
Subject: [PATCH 5/5] 12.94.0

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44e928144d..b43fb528f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@
 
 -->
 
-## 12.x.x (unreleased)
+## 12.94.0 (2021/10/25)
 
 ### Improvements
 - クライアント: 画像ビューアを強化
diff --git a/package.json b/package.json
index 0e231aa3b5..e9ce6ef77e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,6 @@
 {
 	"name": "misskey",
-	"author": "syuilo <syuilotan@yahoo.co.jp>",
-	"version": "12.93.2",
+	"version": "12.94.0",
 	"codename": "indigo",
 	"repository": {
 		"type": "git",