Merge branch 'develop' into swn
This commit is contained in:
		@@ -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>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,63 @@ 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;
 | 
			
		||||
 | 
			
		||||
				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%;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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">
 | 
			
		||||
 
 | 
			
		||||
@@ -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">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div>
 | 
			
		||||
<MkSpacer :content-max="800">
 | 
			
		||||
	<XNotes :pagination="pagination" @before="before()" @after="after()"/>
 | 
			
		||||
</div>
 | 
			
		||||
</MkSpacer>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
 
 | 
			
		||||
@@ -348,6 +348,7 @@ hr {
 | 
			
		||||
	contain: layout; // ふき出しがボックスから飛び出て表示されるようなデザインをする場合もあるので paint は contain することができない
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: 廃止
 | 
			
		||||
._monolithic_ {
 | 
			
		||||
	._section:not(:empty) {
 | 
			
		||||
		box-sizing: border-box;
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user