埋め込みノートのスタイル調整

This commit is contained in:
kakkokari-gtyih 2024-06-03 11:55:45 +09:00
parent 8513114a9c
commit dc58cff307
3 changed files with 148 additions and 53 deletions

View File

@ -100,7 +100,25 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA> <MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA>
</template> </template>
</MkReactionsViewer> </MkReactionsViewer>
<footer :class="$style.footer"> <footer v-if="inEmbedPage" :class="$style.footer">
<a :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.footerButton, $style.footerButtonLink]" class="_button">
<i class="ti ti-arrow-back-up"></i>
</a>
<a v-if="canRenote" :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.footerButton, $style.footerButtonLink]" class="_button">
<i class="ti ti-repeat"></i>
</a>
<a v-else :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.footerButton, $style.footerButtonLink]" class="_button" disabled>
<i class="ti ti-ban"></i>
</a>
<a :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.footerButton, $style.footerButtonLink]" class="_button">
<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-plus"></i>
</a>
<a :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.footerButton, $style.footerButtonLink]" class="_button">
<i class="ti ti-dots"></i>
</a>
</footer>
<footer v-else :class="$style.footer">
<button :class="$style.footerButton" class="_button" @click="reply()"> <button :class="$style.footerButton" class="_button" @click="reply()">
<i class="ti ti-arrow-back-up"></i> <i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p> <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
@ -218,6 +236,7 @@ const emit = defineEmits<{
const inTimeline = inject<boolean>('inTimeline', false); const inTimeline = inject<boolean>('inTimeline', false);
const inChannel = inject('inChannel', null); const inChannel = inject('inChannel', null);
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
const inEmbedPage = inject<boolean>('EMBED_PAGE', false)
const note = ref(deepClone(props.note)); const note = ref(deepClone(props.note));
@ -311,7 +330,7 @@ provide('react', (reaction: string) => {
}); });
}); });
if (props.mock) { if (props.mock || inEmbedPage) {
watch(() => props.note, (to) => { watch(() => props.note, (to) => {
note.value = deepClone(to); note.value = deepClone(to);
}, { deep: true }); }, { deep: true });
@ -324,7 +343,7 @@ if (props.mock) {
}); });
} }
if (!props.mock) { if (!props.mock && !inEmbedPage) {
useTooltip(renoteButton, async (showing) => { useTooltip(renoteButton, async (showing) => {
const renotes = await misskeyApi('notes/renotes', { const renotes = await misskeyApi('notes/renotes', {
noteId: appearNote.value.id, noteId: appearNote.value.id,
@ -894,6 +913,13 @@ function emitUpdReaction(emoji: string, delta: number) {
} }
} }
.footerButtonLink:hover,
.footerButtonLink:focus,
.footerButtonLink:active {
text-decoration: none;
}
.footerButtonCount { .footerButtonCount {
display: inline; display: inline;
margin: 0 0 0 8px; margin: 0 0 0 8px;

View File

@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-hotkey="keymap" v-hotkey="keymap"
:class="$style.root" :class="$style.root"
> >
<div v-if="appearNote.reply && appearNote.reply.replyId"> <div v-if="!inEmbedPage && appearNote.reply && appearNote.reply.replyId">
<div v-if="!conversationLoaded" style="padding: 16px"> <div v-if="!conversationLoaded" style="padding: 16px">
<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton> <MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
</div> </div>
@ -43,25 +43,32 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
</div> </div>
</div> </div>
<article :class="$style.note" @contextmenu.stop="onContextmenu"> <article :class="[$style.note, { [$style.embeddedNote]: inEmbedPage }]" @contextmenu.stop="onContextmenu">
<header :class="$style.noteHeader"> <header :class="$style.noteHeader">
<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/> <MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
<div :class="$style.noteHeaderBody"> <div :class="$style.noteHeaderBody">
<div :class="$style.noteHeaderBodyUpper">
<div>
<div> <div>
<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)"> <MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
<MkUserName :nowrap="false" :user="appearNote.user"/> <MkUserName :nowrap="false" :user="appearNote.user"/>
</MkA> </MkA>
<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span> <span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
</div>
<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
</div>
<div :class="$style.noteHeaderInfo"> <div :class="$style.noteHeaderInfo">
<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]"> <img v-if="inEmbedPage" :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.noteHeaderInstanceIcon"/>
<template v-else>
<div v-if="appearNote.visibility !== 'public'" :title="i18n.ts._visibility[appearNote.visibility]">
<i v-if="appearNote.visibility === 'home'" class="ti ti-home"></i> <i v-if="appearNote.visibility === 'home'" class="ti ti-home"></i>
<i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i>
<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> <i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i>
</span> </div>
<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> <div v-if="appearNote.localOnly" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></div>
</template>
</div> </div>
</div> </div>
<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
</div> </div>
</header> </header>
@ -99,16 +106,49 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/>
</div> </div>
<div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
<button v-if="inEmbedPage && isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false">
<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
</button>
<button v-else-if="inEmbedPage && isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true">
<span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span>
</button>
</div> </div>
<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA>
</div> </div>
<footer> <footer>
<div :class="$style.noteFooterInfo"> <div :class="$style.noteFooterInfo">
<template v-if="inEmbedPage">
<span v-if="appearNote.visibility !== 'public'" style="display: inline-block; margin-right: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
<i v-if="appearNote.visibility === 'home'" class="ti ti-home"></i>
<i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i>
<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i>
</span>
<span v-if="appearNote.localOnly" style="display: inline-block; margin-right: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
</template>
<MkA :to="notePage(appearNote)"> <MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail" colored/> <MkTime :time="appearNote.createdAt" mode="detail" colored/>
</MkA> </MkA>
</div> </div>
<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/> <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/>
<template v-if="inEmbedPage">
<a :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.noteFooterButton, $style.footerButtonLink]" class="_button">
<i class="ti ti-arrow-back-up"></i>
</a>
<a v-if="canRenote" :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.noteFooterButton, $style.footerButtonLink]" class="_button">
<i class="ti ti-repeat"></i>
</a>
<a v-else :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.noteFooterButton, $style.footerButtonLink]" class="_button" disabled>
<i class="ti ti-ban"></i>
</a>
<a :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.noteFooterButton, $style.footerButtonLink]" class="_button">
<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-plus"></i>
</a>
<a :href="`/notes/${appearNote.id}`" target="_blank" rel="noopener" :class="[$style.noteFooterButton, $style.footerButtonLink]" class="_button">
<i class="ti ti-dots"></i>
</a>
</template>
<template v-else>
<button class="_button" :class="$style.noteFooterButton" @click="reply()"> <button class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i> <i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p> <p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
@ -139,14 +179,15 @@ SPDX-License-Identifier: AGPL-3.0-only
<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="showMenu()"> <button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="showMenu()">
<i class="ti ti-dots"></i> <i class="ti ti-dots"></i>
</button> </button>
</template>
</footer> </footer>
</article> </article>
<div :class="$style.tabs"> <div v-if="!inEmbedPage" :class="$style.tabs">
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' }]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' }]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button>
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes' }]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes' }]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button>
<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button>
</div> </div>
<div> <div v-if="!inEmbedPage">
<div v-if="tab === 'replies'"> <div v-if="tab === 'replies'">
<div v-if="!repliesLoaded" style="padding: 16px"> <div v-if="!repliesLoaded" style="padding: 16px">
<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton> <MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
@ -228,11 +269,12 @@ import { useTooltip } from '@/scripts/use-tooltip.js';
import { claimAchievement } from '@/scripts/achievements.js'; import { claimAchievement } from '@/scripts/achievements.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue'; 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 MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkPagination, { type Paging } from '@/components/MkPagination.vue';
import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { isEnabledUrlPreview } from '@/instance.js'; import { isEnabledUrlPreview, instance } from '@/instance.js';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
note: Misskey.entities.Note; note: Misskey.entities.Note;
@ -242,6 +284,7 @@ const props = withDefaults(defineProps<{
}); });
const inChannel = inject('inChannel', null); const inChannel = inject('inChannel', null);
const inEmbedPage = inject<boolean>('EMBED_PAGE', false);
const note = ref(deepClone(props.note)); const note = ref(deepClone(props.note));
@ -288,6 +331,8 @@ const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
const translating = ref(false); const translating = ref(false);
const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null; const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null; const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null;
const isLong = shouldCollapsed(appearNote.value, urls ?? []);
const collapsed = ref(appearNote.value.cw == null && isLong);
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
const conversation = ref<Misskey.entities.Note[]>([]); const conversation = ref<Misskey.entities.Note[]>([]);
const replies = ref<Misskey.entities.Note[]>([]); const replies = ref<Misskey.entities.Note[]>([]);
@ -596,6 +641,10 @@ function loadConversation() {
padding: 32px; padding: 32px;
font-size: 1.2em; font-size: 1.2em;
&.embeddedNote {
padding: 16px 32px;
}
&:hover > .main > .footer > .button { &:hover > .main > .footer > .button {
opacity: 1; opacity: 1;
} }
@ -624,6 +673,10 @@ function loadConversation() {
font-size: 0.95em; font-size: 0.95em;
} }
.noteHeaderBodyUpper {
display: flex;
}
.noteHeaderName { .noteHeaderName {
font-weight: bold; font-weight: bold;
line-height: 1.3; line-height: 1.3;
@ -640,7 +693,17 @@ function loadConversation() {
} }
.noteHeaderInfo { .noteHeaderInfo {
float: right; margin-left: auto;
display: flex;
gap: 0.5em;
align-items: center;
}
.noteHeaderInstanceIcon {
display: inline-block;
width: 32px;
height: 32px;
margin-left: 4px;
} }
.noteHeaderUsername { .noteHeaderUsername {
@ -720,6 +783,12 @@ function loadConversation() {
} }
} }
.footerButtonLink:hover,
.footerButtonLink:focus,
.footerButtonLink:active {
text-decoration: none;
}
.noteFooterButtonCount { .noteFooterButtonCount {
display: inline; display: inline;
margin: 0 0 0 8px; margin: 0 0 0 8px;

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div :class="$style.noteEmbedRoot"> <div :class="$style.noteEmbedRoot">
<MkLoading v-if="loading"/> <MkLoading v-if="loading"/>
<MkNote v-else-if="note" :note="note"/> <MkNoteDetailed v-else-if="note" :note="note"/>
<XNotFound v-else/> <XNotFound v-else/>
</div> </div>
</template> </template>
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import MkNote from '@/components/MkNote.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';