mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-25 22:22:51 +09:00
mediaまわりの修正
This commit is contained in:
parent
87c75af182
commit
1c35bf32bb
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!);
|
||||||
|
Loading…
Reference in New Issue
Block a user