mediaまわりの修正

This commit is contained in:
kakkokari-gtyih 2024-06-03 19:05:17 +09:00
parent 87c75af182
commit 1c35bf32bb
11 changed files with 60 additions and 25 deletions

4
locales/index.d.ts vendored
View File

@ -4984,6 +4984,10 @@ export interface Locale extends ILocale {
* *
*/ */
"inquiry": string; "inquiry": string;
/**
* {x}
*/
"fromX": ParameterizedString<"x">;
"_delivery": { "_delivery": {
/** /**
* *

View File

@ -1242,6 +1242,7 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
noDescription: "説明文はありません" noDescription: "説明文はありません"
alwaysConfirmFollow: "フォローの際常に確認する" alwaysConfirmFollow: "フォローの際常に確認する"
inquiry: "お問い合わせ" inquiry: "お問い合わせ"
fromX: "{x}から"
_delivery: _delivery:
status: "配信状態" status: "配信状態"

View File

@ -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;'
}" }"
> >

View File

@ -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) {

View File

@ -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;

View File

@ -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 {

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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 {

View File

@ -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!);