mediaまわりの修正
This commit is contained in:
		
							
								
								
									
										4
									
								
								locales/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								locales/index.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -4984,6 +4984,10 @@ export interface Locale extends ILocale { | |||||||
|      * お問い合わせ |      * お問い合わせ | ||||||
|      */ |      */ | ||||||
|     "inquiry": string; |     "inquiry": string; | ||||||
|  |     /** | ||||||
|  |      * {x}から | ||||||
|  |      */ | ||||||
|  |     "fromX": ParameterizedString<"x">; | ||||||
|     "_delivery": { |     "_delivery": { | ||||||
|         /** |         /** | ||||||
|          * 配信状態 |          * 配信状態 | ||||||
|   | |||||||
| @@ -1242,6 +1242,7 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ | |||||||
| noDescription: "説明文はありません" | noDescription: "説明文はありません" | ||||||
| alwaysConfirmFollow: "フォローの際常に確認する" | alwaysConfirmFollow: "フォローの際常に確認する" | ||||||
| inquiry: "お問い合わせ" | inquiry: "お問い合わせ" | ||||||
|  | fromX: "{x}から" | ||||||
|  |  | ||||||
| _delivery: | _delivery: | ||||||
|   status: "配信状態" |   status: "配信状態" | ||||||
|   | |||||||
| @@ -14,6 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 			title: image.name, | 			title: image.name, | ||||||
| 			class: $style.imageContainer, | 			class: $style.imageContainer, | ||||||
| 			href: image.url, | 			href: image.url, | ||||||
|  | 			target: '_blank', | ||||||
|  | 			rel: 'noopener', | ||||||
| 			style: 'cursor: zoom-in;' | 			style: 'cursor: zoom-in;' | ||||||
| 		}" | 		}" | ||||||
| 	> | 	> | ||||||
|   | |||||||
| @@ -5,7 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
|  |  | ||||||
| <template> | <template> | ||||||
| <div> | <div> | ||||||
| 	<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/> | 	<div v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :class="$style.banner"> | ||||||
|  | 		<XBanner :media="media"/> | ||||||
|  | 		<a v-if="inEmbedPage && originalEntityUrl" :href="originalEntityUrl" target="_blank" rel="noopener" :class="$style.mediaLinkForEmbed"></a> | ||||||
|  | 	</div> | ||||||
| 	<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container"> | 	<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container"> | ||||||
| 		<div | 		<div | ||||||
| 			ref="gallery" | 			ref="gallery" | ||||||
| @@ -18,17 +21,18 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 				}] : count === 2 ? $style.n2 : count === 3 ? $style.n3 : count === 4 ? $style.n4 : $style.nMany, | 				}] : count === 2 ? $style.n2 : count === 3 ? $style.n3 : count === 4 ? $style.n4 : $style.nMany, | ||||||
| 			]" | 			]" | ||||||
| 		> | 		> | ||||||
| 			<template v-for="media in mediaList.filter(media => previewable(media))"> | 			<div v-for="media in mediaList.filter(media => previewable(media))" :class="$style.media"> | ||||||
| 				<XVideo v-if="media.type.startsWith('video')" :key="`video:${media.id}`" :class="$style.media" :video="media"/> | 				<XVideo v-if="media.type.startsWith('video')" :key="`video:${media.id}`" :video="media" :class="$style.mediaInner"/> | ||||||
| 				<XImage v-else-if="media.type.startsWith('image')" :key="`image:${media.id}`" :class="$style.media" class="image" :data-id="media.id" :image="media" :raw="raw"/> | 				<XImage v-else-if="media.type.startsWith('image')" :key="`image:${media.id}`" :class="$style.mediaInner" class="image" :data-id="media.id" :image="media" :raw="raw"/> | ||||||
| 			</template> | 				<a v-if="inEmbedPage && originalEntityUrl" :href="originalEntityUrl" target="_blank" rel="noopener" :class="$style.mediaLinkForEmbed"></a> | ||||||
|  | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { computed, onMounted, onUnmounted, shallowRef } from 'vue'; | import { computed, onMounted, onUnmounted, shallowRef, inject } from 'vue'; | ||||||
| import * as Misskey from 'misskey-js'; | import * as Misskey from 'misskey-js'; | ||||||
| import PhotoSwipeLightbox from 'photoswipe/lightbox'; | import PhotoSwipeLightbox from 'photoswipe/lightbox'; | ||||||
| import PhotoSwipe from 'photoswipe'; | import PhotoSwipe from 'photoswipe'; | ||||||
| @@ -43,8 +47,13 @@ import { defaultStore } from '@/store.js'; | |||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	mediaList: Misskey.entities.DriveFile[]; | 	mediaList: Misskey.entities.DriveFile[]; | ||||||
| 	raw?: boolean; | 	raw?: boolean; | ||||||
|  |  | ||||||
|  | 	/** 埋め込みページ用 親要素の正規URL */ | ||||||
|  | 	originalEntityUrl?: string; | ||||||
| }>(); | }>(); | ||||||
|  |  | ||||||
|  | const inEmbedPage = inject<boolean>('EMBED_PAGE', false); | ||||||
|  |  | ||||||
| const gallery = shallowRef<HTMLDivElement>(); | const gallery = shallowRef<HTMLDivElement>(); | ||||||
| const pswpZIndex = os.claimZIndex('middle'); | const pswpZIndex = os.claimZIndex('middle'); | ||||||
| document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); | document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); | ||||||
| @@ -90,6 +99,7 @@ async function calcAspectRatio() { | |||||||
|  |  | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
| 	calcAspectRatio(); | 	calcAspectRatio(); | ||||||
|  | 	if (defaultStore.state.imageNewTab || inEmbedPage) return; | ||||||
|  |  | ||||||
| 	lightbox = new PhotoSwipeLightbox({ | 	lightbox = new PhotoSwipeLightbox({ | ||||||
| 		dataSource: props.mediaList | 		dataSource: props.mediaList | ||||||
| @@ -284,6 +294,26 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => { | |||||||
| .media { | .media { | ||||||
| 	overflow: hidden; // clipにするとバグる | 	overflow: hidden; // clipにするとバグる | ||||||
| 	border-radius: 8px; | 	border-radius: 8px; | ||||||
|  | 	position: relative; | ||||||
|  |  | ||||||
|  | 	>.mediaInner { | ||||||
|  | 		width: 100%; | ||||||
|  | 		height: 100%; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .banner { | ||||||
|  | 	position: relative; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .mediaLinkForEmbed::after { | ||||||
|  | 	position: absolute; | ||||||
|  | 	top: 0; | ||||||
|  | 	left: 0; | ||||||
|  | 	right: 0; | ||||||
|  | 	bottom: 0; | ||||||
|  | 	z-index: 1; | ||||||
|  | 	content: ''; | ||||||
| } | } | ||||||
|  |  | ||||||
| :global(.pswp) { | :global(.pswp) { | ||||||
|   | |||||||
| @@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div v-if="appearNote.files && appearNote.files.length > 0"> | 					<div v-if="appearNote.files && appearNote.files.length > 0"> | ||||||
| 						<MkMediaList :mediaList="appearNote.files"/> | 						<MkMediaList :mediaList="appearNote.files" :originalEntityUrl="`${url}/notes/${appearNote.id}`"/> | ||||||
| 					</div> | 					</div> | ||||||
| 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> | 					<MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> | ||||||
| 					<div v-if="isEnabledUrlPreview"> | 					<div v-if="isEnabledUrlPreview"> | ||||||
| @@ -216,6 +216,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; | |||||||
| import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; | import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; | ||||||
| import { shouldCollapsed } from '@/scripts/collapsed.js'; | import { shouldCollapsed } from '@/scripts/collapsed.js'; | ||||||
| import { isEnabledUrlPreview } from '@/instance.js'; | import { isEnabledUrlPreview } from '@/instance.js'; | ||||||
|  | import { url } from '@/config.js'; | ||||||
|  |  | ||||||
| const props = withDefaults(defineProps<{ | const props = withDefaults(defineProps<{ | ||||||
| 	note: Misskey.entities.Note; | 	note: Misskey.entities.Note; | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<div v-if="appearNote.files && appearNote.files.length > 0"> | 				<div v-if="appearNote.files && appearNote.files.length > 0"> | ||||||
| 					<MkMediaList :mediaList="appearNote.files"/> | 					<MkMediaList :mediaList="appearNote.files" :originalEntityUrl="`${url}/notes/${appearNote.id}`"/> | ||||||
| 				</div> | 				</div> | ||||||
| 				<MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> | 				<MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> | ||||||
| 				<div v-if="isEnabledUrlPreview"> | 				<div v-if="isEnabledUrlPreview"> | ||||||
| @@ -645,7 +645,7 @@ function loadConversation() { | |||||||
| 	font-size: 1.2em; | 	font-size: 1.2em; | ||||||
|  |  | ||||||
| 	&.embeddedNote { | 	&.embeddedNote { | ||||||
| 		padding: 16px 32px; | 		padding: 24px 32px 16px; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	&:hover > .main > .footer > .button { | 	&:hover > .main > .footer > .button { | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ const props = defineProps<{ | |||||||
| }>(); | }>(); | ||||||
|  |  | ||||||
| const mock = inject<boolean>('mock', false); | const mock = inject<boolean>('mock', false); | ||||||
|  | const inEmbedPage = inject<boolean>('EMBED_PAGE', false); | ||||||
|  |  | ||||||
| const emit = defineEmits<{ | const emit = defineEmits<{ | ||||||
| 	(ev: 'reactionToggled', emoji: string, newCount: number): void; | 	(ev: 'reactionToggled', emoji: string, newCount: number): void; | ||||||
| @@ -140,7 +141,7 @@ onMounted(() => { | |||||||
| 	if (!props.isInitial) anime(); | 	if (!props.isInitial) anime(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| if (!mock) { | if (!mock && !inEmbedPage) { | ||||||
| 	useTooltip(buttonEl, async (showing) => { | 	useTooltip(buttonEl, async (showing) => { | ||||||
| 		const reactions = await misskeyApiGet('notes/reactions', { | 		const reactions = await misskeyApiGet('notes/reactions', { | ||||||
| 			noteId: props.note.id, | 			noteId: props.note.id, | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 	</div> | 	</div> | ||||||
| 	<details v-if="note.files && note.files.length > 0"> | 	<details v-if="note.files && note.files.length > 0"> | ||||||
| 		<summary>({{ i18n.tsx.withNFiles({ n: note.files.length }) }})</summary> | 		<summary>({{ i18n.tsx.withNFiles({ n: note.files.length }) }})</summary> | ||||||
| 		<MkMediaList :mediaList="note.files"/> | 		<MkMediaList :mediaList="note.files" :originalEntityUrl="`${url}/notes/${note.id}`"/> | ||||||
| 	</details> | 	</details> | ||||||
| 	<details v-if="note.poll"> | 	<details v-if="note.poll"> | ||||||
| 		<summary>{{ i18n.ts.poll }}</summary> | 		<summary>{{ i18n.ts.poll }}</summary> | ||||||
| @@ -36,6 +36,7 @@ import MkMediaList from '@/components/MkMediaList.vue'; | |||||||
| import MkPoll from '@/components/MkPoll.vue'; | import MkPoll from '@/components/MkPoll.vue'; | ||||||
| import { i18n } from '@/i18n.js'; | import { i18n } from '@/i18n.js'; | ||||||
| import { shouldCollapsed } from '@/scripts/collapsed.js'; | import { shouldCollapsed } from '@/scripts/collapsed.js'; | ||||||
|  | import { url } from '@/config.js'; | ||||||
|  |  | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	note: Misskey.entities.Note; | 	note: Misskey.entities.Note; | ||||||
|   | |||||||
| @@ -12,16 +12,19 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref } from 'vue'; | import { ref, provide } from 'vue'; | ||||||
| import * as Misskey from 'misskey-js'; | import * as Misskey from 'misskey-js'; | ||||||
| import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; | import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; | ||||||
| import XNotFound from '@/pages/not-found.vue'; | import XNotFound from '@/pages/not-found.vue'; | ||||||
| import { misskeyApi } from '@/scripts/misskey-api.js'; | import { misskeyApi } from '@/scripts/misskey-api.js'; | ||||||
|  | import { url } from '@/config.js'; | ||||||
|  |  | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	noteId: string; | 	noteId: string; | ||||||
| }>(); | }>(); | ||||||
|  |  | ||||||
|  | provide('EMBED_ORIGINAL_ENTITY_URL', `${url}/notes/${props.noteId}`); | ||||||
|  |  | ||||||
| const note = ref<Misskey.entities.Note | null>(null); | const note = ref<Misskey.entities.Note | null>(null); | ||||||
| const loading = ref(true); | const loading = ref(true); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||||
| 							</a> | 							</a> | ||||||
| 						</template> | 						</template> | ||||||
| 					</I18n> | 					</I18n> | ||||||
| 					<div :class="$style.sub"></div> | 					<div :class="$style.sub">{{ i18n.tsx.fromX({ x: instanceName }) }}</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<a :href="url" :class="$style.instanceIconLink" target="_blank" rel="noopener noreferrer"> | 				<a :href="url" :class="$style.instanceIconLink" target="_blank" rel="noopener noreferrer"> | ||||||
| 					<img | 					<img | ||||||
| @@ -48,7 +48,7 @@ import type { Paging } from '@/components/MkPagination.vue'; | |||||||
| import { misskeyApi } from '@/scripts/misskey-api.js'; | import { misskeyApi } from '@/scripts/misskey-api.js'; | ||||||
| import { i18n } from '@/i18n.js'; | import { i18n } from '@/i18n.js'; | ||||||
| import { instance } from '@/instance.js'; | import { instance } from '@/instance.js'; | ||||||
| import { url } from '@/config.js'; | import { url, instanceName } from '@/config.js'; | ||||||
|  |  | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	username: string; | 	username: string; | ||||||
| @@ -101,6 +101,7 @@ misskeyApi('users/show', { | |||||||
|  |  | ||||||
| 	.headerTitle { | 	.headerTitle { | ||||||
| 		font-weight: 700; | 		font-weight: 700; | ||||||
|  | 		line-height: 1.1; | ||||||
|  |  | ||||||
| 		.sub { | 		.sub { | ||||||
| 			font-size: 0.8em; | 			font-size: 0.8em; | ||||||
| @@ -112,6 +113,7 @@ misskeyApi('users/show', { | |||||||
| 	.instanceIconLink { | 	.instanceIconLink { | ||||||
| 		display: block; | 		display: block; | ||||||
| 		margin-left: auto; | 		margin-left: auto; | ||||||
|  | 		height: 24px; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	.instanceIcon { | 	.instanceIcon { | ||||||
|   | |||||||
| @@ -71,24 +71,14 @@ const maxHeight = ref(params.get('maxHeight') ? parseInt(params.get('maxHeight') | |||||||
| //#region Embed Resizer | //#region Embed Resizer | ||||||
| const rootEl = shallowRef<HTMLElement | null>(null); | const rootEl = shallowRef<HTMLElement | null>(null); | ||||||
|  |  | ||||||
| let resizeMessageThrottleTimer: number | null = null; |  | ||||||
| let resizeMessageThrottleFlag = false; |  | ||||||
| let previousHeight = 0; | let previousHeight = 0; | ||||||
| const resizeObserver = new ResizeObserver(async () => { | const resizeObserver = new ResizeObserver(async () => { | ||||||
| 	const height = rootEl.value!.scrollHeight + 2; // border 上下1px | 	const height = rootEl.value!.scrollHeight + 2; // border 上下1px | ||||||
| 	if (resizeMessageThrottleFlag && Math.abs(previousHeight - height) < 30) return; // プラマイ30px未満の変化は無視 | 	if (Math.abs(previousHeight - height) < 1) return; // 1px未満の変化は無視 | ||||||
| 	if (resizeMessageThrottleTimer) window.clearTimeout(resizeMessageThrottleTimer); |  | ||||||
|  |  | ||||||
| 	postMessageToParentWindow('misskey:embed:changeHeight', { | 	postMessageToParentWindow('misskey:embed:changeHeight', { | ||||||
| 		height: (maxHeight.value > 0 && height > maxHeight.value) ? maxHeight.value : height, | 		height: (maxHeight.value > 0 && height > maxHeight.value) ? maxHeight.value : height, | ||||||
| 	}); | 	}); | ||||||
| 	previousHeight = height; | 	previousHeight = height; | ||||||
|  |  | ||||||
| 	resizeMessageThrottleFlag = true; |  | ||||||
|  |  | ||||||
| 	resizeMessageThrottleTimer = window.setTimeout(() => { |  | ||||||
| 		resizeMessageThrottleFlag = false; // 収縮をやりすぎるとチカチカする |  | ||||||
| 	}, 500); |  | ||||||
| }); | }); | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
| 	resizeObserver.observe(rootEl.value!); | 	resizeObserver.observe(rootEl.value!); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 kakkokari-gtyih
					kakkokari-gtyih