Notes refactoring (?) + new CW design (#9888)
Moved a lot of the duplicated code in the different note components into the SubNoteContent component I've also replaced the detailed note stuff with just the MkNote component Co-authored-by: Freeplay <Freeplay@duck.com> Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9888 Co-authored-by: Free <freeplay@duck.com> Co-committed-by: Free <freeplay@duck.com>
This commit is contained in:
parent
4974088061
commit
29818a067b
|
@ -1,8 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<button class="nrvgflfu _button" @click.stop="toggle">
|
<button
|
||||||
<b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b>
|
class="_button"
|
||||||
<span v-if="!modelValue">{{ label }}</span>
|
:class="{showLess: modelValue, fade: !modelValue}"
|
||||||
|
@click.stop="toggle"
|
||||||
|
>
|
||||||
|
<span>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}
|
||||||
|
<span v-if="!modelValue">{{ label }}</span>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -30,7 +36,8 @@ const label = computed(() => {
|
||||||
? [i18n.t("_cw.files", { count: props.note.files.length })]
|
? [i18n.t("_cw.files", { count: props.note.files.length })]
|
||||||
: [],
|
: [],
|
||||||
props.note.poll != null ? [i18n.ts.poll] : [],
|
props.note.poll != null ? [i18n.ts.poll] : [],
|
||||||
] as string[][]).join(" / ");
|
props.note.renote != null ? [i18n.ts.quoteAttached] : [],
|
||||||
|
] as string[][]).join(", ");
|
||||||
});
|
});
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
|
@ -39,37 +46,25 @@ const toggle = () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.nrvgflfu {
|
._button {
|
||||||
position: relative;
|
font-weight: 700;
|
||||||
z-index: 2;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: var(--cwFg);
|
|
||||||
background: var(--cwBg);
|
|
||||||
padding: 6px 10px;
|
|
||||||
width: 90%;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 1px solid var(--divider);
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
transition: background-color 0.25s ease-in-out;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--cwFg);
|
|
||||||
color: var(--cwBg);
|
|
||||||
}
|
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
margin-left: 4px;
|
background: var(--cwBg) !important;
|
||||||
|
color: var(--cwFg);
|
||||||
&:before {
|
transition: background .2s, color .2s;
|
||||||
content: "(";
|
> span {
|
||||||
}
|
font-weight: 500;
|
||||||
|
&::before {
|
||||||
&:after {
|
content: "("
|
||||||
content: ")";
|
}
|
||||||
|
&::after {
|
||||||
|
content: ")"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:hover > span {
|
||||||
|
background: var(--cwFg) !important;
|
||||||
|
color: var(--cwBg) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
:class="{ renote: isRenote }"
|
:class="{ renote: isRenote }"
|
||||||
>
|
>
|
||||||
<MkNoteSub
|
<MkNoteSub
|
||||||
v-if="appearNote.reply"
|
v-if="appearNote.reply && !detailedView"
|
||||||
:note="appearNote.reply"
|
:note="appearNote.reply"
|
||||||
class="reply-to"
|
class="reply-to"
|
||||||
/>
|
/>
|
||||||
<div class="note-context" @click="noteClick">
|
<div v-if="!detailedView" class="note-context" @click="noteClick">
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
<div v-if="appearNote._prId_" class="info">
|
<div v-if="appearNote._prId_" class="info">
|
||||||
<i class="ph-megaphone-simple-bold ph-lg"></i>
|
<i class="ph-megaphone-simple-bold ph-lg"></i>
|
||||||
|
@ -77,93 +77,34 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<p v-if="appearNote.cw != null" class="cw">
|
<MkSubNoteContent
|
||||||
<Mfm
|
class="text"
|
||||||
v-if="appearNote.cw != ''"
|
:note="appearNote"
|
||||||
class="text"
|
:detailed="true"
|
||||||
:text="appearNote.cw"
|
:detailedView="detailedView"
|
||||||
:author="appearNote.user"
|
:parentId="appearNote.parentId"
|
||||||
:custom-emojis="appearNote.emojis"
|
@push="(e) => router.push(notePage(e))"
|
||||||
:i="$i"
|
></MkSubNoteContent>
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<XCwButton v-model="showContent" :note="appearNote" />
|
|
||||||
</p>
|
|
||||||
<div
|
<div
|
||||||
v-show="appearNote.cw == null || showContent"
|
v-if="translating || translation"
|
||||||
class="content"
|
class="translation"
|
||||||
:class="{ collapsed, isLong }"
|
|
||||||
>
|
>
|
||||||
<div class="text">
|
<MkLoading v-if="translating" mini />
|
||||||
|
<div v-else class="translated">
|
||||||
|
<b
|
||||||
|
>{{
|
||||||
|
i18n.t("translatedFrom", {
|
||||||
|
x: translation.sourceLang,
|
||||||
|
})
|
||||||
|
}}:
|
||||||
|
</b>
|
||||||
<Mfm
|
<Mfm
|
||||||
v-if="appearNote.text"
|
:text="translation.text"
|
||||||
:text="appearNote.text"
|
|
||||||
:author="appearNote.user"
|
:author="appearNote.user"
|
||||||
:i="$i"
|
:i="$i"
|
||||||
:custom-emojis="appearNote.emojis"
|
:custom-emojis="appearNote.emojis"
|
||||||
/>
|
/>
|
||||||
<!-- <a v-if="appearNote.renote != null" class="rp">RN:</a> -->
|
|
||||||
<div
|
|
||||||
v-if="translating || translation"
|
|
||||||
class="translation"
|
|
||||||
>
|
|
||||||
<MkLoading v-if="translating" mini />
|
|
||||||
<div v-else class="translated">
|
|
||||||
<b
|
|
||||||
>{{
|
|
||||||
i18n.t("translatedFrom", {
|
|
||||||
x: translation.sourceLang,
|
|
||||||
})
|
|
||||||
}}:
|
|
||||||
</b>
|
|
||||||
<Mfm
|
|
||||||
:text="translation.text"
|
|
||||||
:author="appearNote.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="appearNote.emojis"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="appearNote.files.length > 0" class="files">
|
|
||||||
<XMediaList :media-list="appearNote.files" />
|
|
||||||
</div>
|
|
||||||
<XPoll
|
|
||||||
v-if="appearNote.poll"
|
|
||||||
ref="pollViewer"
|
|
||||||
:note="appearNote"
|
|
||||||
class="poll"
|
|
||||||
/>
|
|
||||||
<MkUrlPreview
|
|
||||||
v-for="url in urls"
|
|
||||||
:key="url"
|
|
||||||
:url="url"
|
|
||||||
:compact="true"
|
|
||||||
:detail="false"
|
|
||||||
class="url-preview"
|
|
||||||
/>
|
|
||||||
<div v-if="appearNote.renote" class="renote">
|
|
||||||
<XNoteSimple
|
|
||||||
:note="appearNote.renote"
|
|
||||||
@click.stop="
|
|
||||||
router.push(notePage(appearNote.renote))
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
v-if="isLong && collapsed"
|
|
||||||
class="fade _button"
|
|
||||||
@click.stop="collapsed = false"
|
|
||||||
>
|
|
||||||
<span>{{ i18n.ts.showMore }}</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-else-if="isLong && !collapsed"
|
|
||||||
class="showLess _button"
|
|
||||||
@click.stop="collapsed = true"
|
|
||||||
>
|
|
||||||
<span>{{ i18n.ts.showLess }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<MkA
|
<MkA
|
||||||
v-if="appearNote.channel && !inChannel"
|
v-if="appearNote.channel && !inChannel"
|
||||||
|
@ -174,6 +115,14 @@
|
||||||
{{ appearNote.channel.name }}</MkA
|
{{ appearNote.channel.name }}</MkA
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="detailedView" class="info">
|
||||||
|
<MkA class="created-at" :to="notePage(appearNote)">
|
||||||
|
<MkTime
|
||||||
|
:time="appearNote.createdAt"
|
||||||
|
mode="absolute"
|
||||||
|
/>
|
||||||
|
</MkA>
|
||||||
|
</div>
|
||||||
<footer ref="el" class="footer" @click.stop>
|
<footer ref="el" class="footer" @click.stop>
|
||||||
<XReactionsViewer
|
<XReactionsViewer
|
||||||
v-if="enableEmojiReactions"
|
v-if="enableEmojiReactions"
|
||||||
|
@ -277,6 +226,7 @@ import * as mfm from "mfm-js";
|
||||||
import type { Ref } from "vue";
|
import type { Ref } from "vue";
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
import MkNoteSub from "@/components/MkNoteSub.vue";
|
import MkNoteSub from "@/components/MkNoteSub.vue";
|
||||||
|
import MkSubNoteContent from "./MkSubNoteContent.vue";
|
||||||
import XNoteHeader from "@/components/MkNoteHeader.vue";
|
import XNoteHeader from "@/components/MkNoteHeader.vue";
|
||||||
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
||||||
import XMediaList from "@/components/MkMediaList.vue";
|
import XMediaList from "@/components/MkMediaList.vue";
|
||||||
|
@ -297,7 +247,6 @@ import { userPage } from "@/filters/user";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import { defaultStore, noteViewInterruptors } from "@/store";
|
import { defaultStore, noteViewInterruptors } from "@/store";
|
||||||
import { reactionPicker } from "@/scripts/reaction-picker";
|
import { reactionPicker } from "@/scripts/reaction-picker";
|
||||||
import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
|
|
||||||
import { $i } from "@/account";
|
import { $i } from "@/account";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { getNoteMenu } from "@/scripts/get-note-menu";
|
import { getNoteMenu } from "@/scripts/get-note-menu";
|
||||||
|
@ -310,6 +259,7 @@ const router = useRouter();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: misskey.entities.Note;
|
note: misskey.entities.Note;
|
||||||
pinned?: boolean;
|
pinned?: boolean;
|
||||||
|
detailedView?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const inChannel = inject("inChannel", null);
|
const inChannel = inject("inChannel", null);
|
||||||
|
@ -344,18 +294,10 @@ let appearNote = $computed(() =>
|
||||||
);
|
);
|
||||||
const isMyRenote = $i && $i.id === note.userId;
|
const isMyRenote = $i && $i.id === note.userId;
|
||||||
const showContent = ref(false);
|
const showContent = ref(false);
|
||||||
const isLong =
|
|
||||||
appearNote.cw == null &&
|
|
||||||
appearNote.text != null &&
|
|
||||||
(appearNote.text.split("\n").length > 9 || appearNote.text.length > 500);
|
|
||||||
const collapsed = ref(appearNote.cw == null && isLong);
|
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const muted = ref(getWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
const muted = ref(getWordMute(appearNote, $i, defaultStore.state.mutedWords));
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
const urls = appearNote.text
|
|
||||||
? extractUrlFromMfm(mfm.parse(appearNote.text)).slice(0, 5)
|
|
||||||
: null;
|
|
||||||
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
|
||||||
|
|
||||||
const keymap = {
|
const keymap = {
|
||||||
|
@ -503,7 +445,7 @@ function focusAfter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function noteClick(e) {
|
function noteClick(e) {
|
||||||
if (document.getSelection().type === "Range") {
|
if (document.getSelection().type === "Range" || props.detailedView) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
} else {
|
} else {
|
||||||
router.push(notePage(appearNote));
|
router.push(notePage(appearNote));
|
||||||
|
@ -690,123 +632,24 @@ function readPromo() {
|
||||||
> .body {
|
> .body {
|
||||||
margin-top: 0.7em;
|
margin-top: 0.7em;
|
||||||
|
|
||||||
> .cw {
|
|
||||||
cursor: default;
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
> .content {
|
||||||
&.isLong {
|
> .translation {
|
||||||
> .showLess {
|
border: solid 0.5px var(--divider);
|
||||||
width: 100%;
|
border-radius: var(--radius);
|
||||||
margin-top: 1em;
|
padding: 12px;
|
||||||
position: sticky;
|
|
||||||
bottom: var(--stickyBottom);
|
|
||||||
|
|
||||||
> span {
|
|
||||||
display: inline-block;
|
|
||||||
background: var(--popup);
|
|
||||||
padding: 6px 10px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
border-radius: 999px;
|
|
||||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.collapsed {
|
|
||||||
position: relative;
|
|
||||||
max-height: 9em;
|
|
||||||
overflow: hidden;
|
|
||||||
> .text {
|
|
||||||
max-height: 9em;
|
|
||||||
mask: linear-gradient(
|
|
||||||
black calc(100% - 64px),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
-webkit-mask: linear-gradient(
|
|
||||||
black calc(100% - 64px),
|
|
||||||
transparent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
> .fade {
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
display: inline-block;
|
|
||||||
background: var(--panel);
|
|
||||||
padding: 6px 10px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
border-radius: 999px;
|
|
||||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> span {
|
|
||||||
background: var(--panelHighlight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
> .reply {
|
|
||||||
color: var(--accent);
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .rp {
|
|
||||||
margin-left: 4px;
|
|
||||||
font-style: oblique;
|
|
||||||
color: var(--renote);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .translation {
|
|
||||||
border: solid 0.5px var(--divider);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 12px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .files {
|
|
||||||
margin-top: 0.4em;
|
|
||||||
margin-bottom: 0.4em;
|
|
||||||
}
|
|
||||||
> .url-preview {
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
> .poll {
|
> .renote {
|
||||||
font-size: 80%;
|
padding-top: 8px;
|
||||||
}
|
> * {
|
||||||
|
padding: 16px;
|
||||||
> .renote {
|
border: solid 1px var(--renote);
|
||||||
padding: 8px 0;
|
border-radius: 8px;
|
||||||
|
transition: background 0.2s;
|
||||||
> * {
|
&:hover,
|
||||||
padding: 16px;
|
&:focus-within {
|
||||||
border: solid 1px var(--renote);
|
background-color: var(--panelHighlight);
|
||||||
border-radius: 8px;
|
|
||||||
transition: background 0.2s;
|
|
||||||
&:hover,
|
|
||||||
&:focus-within {
|
|
||||||
background-color: var(--panelHighlight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -816,13 +659,18 @@ function readPromo() {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
> .info {
|
||||||
|
margin-block: 16px;
|
||||||
|
opacity: 0.7;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
> .footer {
|
> .footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
pointer-events: none; // Allow clicking anything w/out pointer-events: all; to open post
|
pointer-events: none; // Allow clicking anything w/out pointer-events: all; to open post
|
||||||
|
margin-top: .4em;
|
||||||
> .button {
|
> .button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
|
@ -20,246 +20,16 @@
|
||||||
:note="appearNote.reply"
|
:note="appearNote.reply"
|
||||||
class="reply-to"
|
class="reply-to"
|
||||||
/>
|
/>
|
||||||
<div v-if="isRenote" class="renote">
|
|
||||||
<MkAvatar class="avatar" :user="note.user" />
|
<div ref="noteEl" class="article" tabindex="-1">
|
||||||
<i class="ph-repeat ph-bold ph-lg"></i>
|
<MkNote
|
||||||
<I18n :src="i18n.ts.renotedBy" tag="span">
|
@contextmenu.stop="onContextmenu"
|
||||||
<template #user>
|
tabindex="-1"
|
||||||
<MkA
|
:note="appearNote"
|
||||||
v-user-preview="note.userId"
|
:detailedView="true"
|
||||||
class="name"
|
></MkNote>
|
||||||
:to="userPage(note.user)"
|
|
||||||
>
|
|
||||||
<MkUserName :user="note.user" />
|
|
||||||
</MkA>
|
|
||||||
</template>
|
|
||||||
</I18n>
|
|
||||||
<div class="info">
|
|
||||||
<button
|
|
||||||
ref="renoteTime"
|
|
||||||
class="_button time"
|
|
||||||
@click="showRenoteMenu()"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
v-if="isMyRenote"
|
|
||||||
class="ph-dots-three-outline ph-bold ph-lg dropdownIcon"
|
|
||||||
></i>
|
|
||||||
<MkTime :time="note.createdAt" />
|
|
||||||
</button>
|
|
||||||
<MkVisibility :note="note" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<article
|
|
||||||
ref="noteEl"
|
|
||||||
class="article"
|
|
||||||
@contextmenu.stop="onContextmenu"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<header class="header">
|
|
||||||
<MkAvatar
|
|
||||||
class="avatar"
|
|
||||||
:user="appearNote.user"
|
|
||||||
:show-indicator="true"
|
|
||||||
/>
|
|
||||||
<div class="body">
|
|
||||||
<div class="top">
|
|
||||||
<MkA
|
|
||||||
v-user-preview="appearNote.user.id"
|
|
||||||
class="name"
|
|
||||||
:to="userPage(appearNote.user)"
|
|
||||||
>
|
|
||||||
<MkUserName :user="appearNote.user" />
|
|
||||||
</MkA>
|
|
||||||
<span v-if="appearNote.user.isBot" class="is-bot"
|
|
||||||
>bot</span
|
|
||||||
>
|
|
||||||
<div class="info">
|
|
||||||
<MkVisibility :note="appearNote" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="username">
|
|
||||||
<MkAcct :user="appearNote.user" />
|
|
||||||
</div>
|
|
||||||
<MkInstanceTicker
|
|
||||||
v-if="showTicker"
|
|
||||||
class="ticker"
|
|
||||||
:instance="appearNote.user.instance"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div class="main">
|
|
||||||
<div class="body">
|
|
||||||
<div v-if="appearNote.cw != null" class="cw">
|
|
||||||
<Mfm
|
|
||||||
v-if="appearNote.cw != ''"
|
|
||||||
class="text"
|
|
||||||
:text="appearNote.cw"
|
|
||||||
:author="appearNote.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="appearNote.emojis"
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<XCwButton v-model="showContent" :note="appearNote" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-show="appearNote.cw == null || showContent"
|
|
||||||
class="content"
|
|
||||||
>
|
|
||||||
<div class="text">
|
|
||||||
<Mfm
|
|
||||||
v-if="appearNote.text"
|
|
||||||
:text="appearNote.text"
|
|
||||||
:author="appearNote.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="appearNote.emojis"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="translating || translation"
|
|
||||||
class="translation"
|
|
||||||
>
|
|
||||||
<MkLoading v-if="translating" mini />
|
|
||||||
<div v-else class="translated">
|
|
||||||
<b
|
|
||||||
>{{
|
|
||||||
i18n.t("translatedFrom", {
|
|
||||||
x: translation.sourceLang,
|
|
||||||
})
|
|
||||||
}}:
|
|
||||||
</b>
|
|
||||||
<Mfm
|
|
||||||
:text="translation.text"
|
|
||||||
:author="appearNote.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="appearNote.emojis"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="appearNote.files.length > 0" class="files">
|
|
||||||
<XMediaList :media-list="appearNote.files" />
|
|
||||||
</div>
|
|
||||||
<XPoll
|
|
||||||
v-if="appearNote.poll"
|
|
||||||
ref="pollViewer"
|
|
||||||
:note="appearNote"
|
|
||||||
class="poll"
|
|
||||||
/>
|
|
||||||
<MkUrlPreview
|
|
||||||
v-for="url in urls"
|
|
||||||
:key="url"
|
|
||||||
:url="url"
|
|
||||||
:compact="true"
|
|
||||||
:detail="true"
|
|
||||||
class="url-preview"
|
|
||||||
/>
|
|
||||||
<div v-if="appearNote.renote" class="renote">
|
|
||||||
<XNoteSimple
|
|
||||||
:note="appearNote.renote"
|
|
||||||
@click.stop="
|
|
||||||
router.push(notePage(appearNote.renote))
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<MkA
|
|
||||||
v-if="appearNote.channel && !inChannel"
|
|
||||||
class="channel"
|
|
||||||
:to="`/channels/${appearNote.channel.id}`"
|
|
||||||
><i class="ph-television ph-bold ph-lg"></i>
|
|
||||||
{{ appearNote.channel.name }}</MkA
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="info">
|
|
||||||
<MkA class="created-at" :to="notePage(appearNote)">
|
|
||||||
<MkTime
|
|
||||||
:time="appearNote.createdAt"
|
|
||||||
mode="detail"
|
|
||||||
/>
|
|
||||||
</MkA>
|
|
||||||
</div>
|
|
||||||
<XReactionsViewer
|
|
||||||
v-if="enableEmojiReactions"
|
|
||||||
ref="reactionsViewer"
|
|
||||||
:note="appearNote"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
v-tooltip.noDelay.bottom="i18n.ts.reply"
|
|
||||||
class="button _button"
|
|
||||||
@click="reply()"
|
|
||||||
>
|
|
||||||
<template v-if="appearNote.reply"
|
|
||||||
><i class="ph-arrow-u-up-left ph-bold ph-lg"></i
|
|
||||||
></template>
|
|
||||||
<template v-else
|
|
||||||
><i class="ph-arrow-bend-up-left ph-bold ph-lg"></i
|
|
||||||
></template>
|
|
||||||
<p v-if="appearNote.repliesCount > 0" class="count">
|
|
||||||
{{ appearNote.repliesCount }}
|
|
||||||
</p>
|
|
||||||
</button>
|
|
||||||
<XRenoteButton
|
|
||||||
ref="renoteButton"
|
|
||||||
class="button"
|
|
||||||
:note="appearNote"
|
|
||||||
:count="appearNote.renoteCount"
|
|
||||||
/>
|
|
||||||
<XStarButtonNoEmoji
|
|
||||||
v-if="!enableEmojiReactions"
|
|
||||||
class="button"
|
|
||||||
:note="appearNote"
|
|
||||||
:count="
|
|
||||||
Object.values(appearNote.reactions).reduce(
|
|
||||||
(partialSum, val) => partialSum + val,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:reacted="appearNote.myReaction != null"
|
|
||||||
/>
|
|
||||||
<XStarButton
|
|
||||||
v-if="
|
|
||||||
enableEmojiReactions &&
|
|
||||||
appearNote.myReaction == null
|
|
||||||
"
|
|
||||||
ref="starButton"
|
|
||||||
class="button"
|
|
||||||
:note="appearNote"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
v-if="
|
|
||||||
enableEmojiReactions &&
|
|
||||||
appearNote.myReaction == null
|
|
||||||
"
|
|
||||||
ref="reactButton"
|
|
||||||
v-tooltip.noDelay.bottom="i18n.ts.reaction"
|
|
||||||
class="button _button"
|
|
||||||
@click="react()"
|
|
||||||
>
|
|
||||||
<i class="ph-smiley ph-bold ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="
|
|
||||||
enableEmojiReactions &&
|
|
||||||
appearNote.myReaction != null
|
|
||||||
"
|
|
||||||
ref="reactButton"
|
|
||||||
class="button _button reacted"
|
|
||||||
@click="undoReact(appearNote)"
|
|
||||||
>
|
|
||||||
<i class="ph-minus ph-bold ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
<XQuoteButton class="button" :note="appearNote" />
|
|
||||||
<button
|
|
||||||
ref="menuButton"
|
|
||||||
v-tooltip.noDelay.bottom="i18n.ts.more"
|
|
||||||
class="button _button"
|
|
||||||
@click="menu()"
|
|
||||||
>
|
|
||||||
<i class="ph-dots-three-outline ph-bold ph-lg"></i>
|
|
||||||
</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<MkNoteSub
|
<MkNoteSub
|
||||||
v-for="note in directReplies"
|
v-for="note in directReplies"
|
||||||
:key="note.id"
|
:key="note.id"
|
||||||
|
@ -298,6 +68,7 @@ import {
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import type * as misskey from "calckey-js";
|
import type * as misskey from "calckey-js";
|
||||||
|
import MkNote from "@/components/MkNote.vue";
|
||||||
import MkNoteSub from "@/components/MkNoteSub.vue";
|
import MkNoteSub from "@/components/MkNoteSub.vue";
|
||||||
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
import XNoteSimple from "@/components/MkNoteSimple.vue";
|
||||||
import XReactionsViewer from "@/components/MkReactionsViewer.vue";
|
import XReactionsViewer from "@/components/MkReactionsViewer.vue";
|
||||||
|
@ -672,8 +443,7 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
> .article {
|
> .article {
|
||||||
padding: 32px;
|
padding-block: 28px 6px;
|
||||||
padding-bottom: 6px;
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
@ -681,151 +451,8 @@ onUnmounted(() => {
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
outline: none;
|
outline: none;
|
||||||
scroll-margin-top: calc(var(--stickyTop) + 20vh);
|
scroll-margin-top: calc(var(--stickyTop) + 20vh);
|
||||||
> .header {
|
:deep(.article) {
|
||||||
display: flex;
|
cursor: unset;
|
||||||
position: relative;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
|
|
||||||
> .avatar {
|
|
||||||
display: block;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: var(--avatarSize);
|
|
||||||
height: var(--avatarSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .body {
|
|
||||||
width: 0;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
padding-left: 14px;
|
|
||||||
font-size: 0.95em;
|
|
||||||
|
|
||||||
> .top {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
> .name {
|
|
||||||
font-weight: bold;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .is-bot {
|
|
||||||
flex-shrink: 0;
|
|
||||||
align-self: center;
|
|
||||||
margin: 0 0.5em;
|
|
||||||
padding: 4px 6px;
|
|
||||||
font-size: 80%;
|
|
||||||
border: solid 0.5px var(--divider);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .info {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .main {
|
|
||||||
> .body {
|
|
||||||
> .cw {
|
|
||||||
cursor: default;
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
> .text {
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
> .reply {
|
|
||||||
color: var(--accent);
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .rp {
|
|
||||||
margin-left: 4px;
|
|
||||||
font-style: oblique;
|
|
||||||
color: var(--renote);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .translation {
|
|
||||||
border: solid 0.5px var(--divider);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 12px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .url-preview {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .poll {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .renote {
|
|
||||||
padding: 8px 0;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
padding: 16px;
|
|
||||||
border: solid 1px var(--renote);
|
|
||||||
border-radius: 8px;
|
|
||||||
transition: background 0.2s;
|
|
||||||
&:hover,
|
|
||||||
&:focus-within {
|
|
||||||
background-color: var(--panelHighlight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .channel {
|
|
||||||
opacity: 0.7;
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .footer {
|
|
||||||
> .info {
|
|
||||||
margin: 16px 0;
|
|
||||||
opacity: 0.7;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .button {
|
|
||||||
margin: 0;
|
|
||||||
padding: 8px;
|
|
||||||
opacity: 0.7;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--fgHighlighted);
|
|
||||||
}
|
|
||||||
|
|
||||||
> .count {
|
|
||||||
display: inline;
|
|
||||||
margin: 0 0 0 8px;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.reacted {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,7 +536,7 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
> .article {
|
> .article {
|
||||||
padding: 16px;
|
padding: 6px 0 0 0;
|
||||||
> .header > .body {
|
> .header > .body {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,7 @@
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<XNoteHeader class="header" :note="note" :mini="true" />
|
<XNoteHeader class="header" :note="note" :mini="true" />
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<p v-if="note.cw != null" class="cw">
|
<MkSubNoteContent class="text" :note="note" />
|
||||||
<Mfm
|
|
||||||
v-if="note.cw != ''"
|
|
||||||
class="text"
|
|
||||||
:text="note.cw"
|
|
||||||
:author="note.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="note.emojis"
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<XCwButton v-model="showContent" :note="note" />
|
|
||||||
</p>
|
|
||||||
<div v-show="note.cw == null || showContent" class="content">
|
|
||||||
<MkSubNoteContent class="text" :note="note" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,7 +15,6 @@ import {} from "vue";
|
||||||
import * as misskey from "calckey-js";
|
import * as misskey from "calckey-js";
|
||||||
import XNoteHeader from "@/components/MkNoteHeader.vue";
|
import XNoteHeader from "@/components/MkNoteHeader.vue";
|
||||||
import MkSubNoteContent from "@/components/MkSubNoteContent.vue";
|
import MkSubNoteContent from "@/components/MkSubNoteContent.vue";
|
||||||
import XCwButton from "@/components/MkCwButton.vue";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: misskey.entities.Note;
|
note: misskey.entities.Note;
|
||||||
|
@ -79,28 +64,6 @@ const showContent = $ref(false);
|
||||||
> .header {
|
> .header {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .body {
|
|
||||||
> .cw {
|
|
||||||
cursor: default;
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .content {
|
|
||||||
> .text {
|
|
||||||
cursor: default;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -21,51 +21,12 @@
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<XNoteHeader class="header" :note="note" :mini="true" />
|
<XNoteHeader class="header" :note="note" :mini="true" />
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<p v-if="appearNote.cw != null" class="cw">
|
<MkSubNoteContent
|
||||||
<MkA
|
class="text"
|
||||||
v-if="appearNote.replyId"
|
:note="note"
|
||||||
:to="`/notes/${appearNote.replyId}`"
|
:parentId="appearNote.parentId"
|
||||||
class="reply-icon"
|
:conversation="conversation"
|
||||||
@click.stop
|
/>
|
||||||
>
|
|
||||||
<i class="ph-arrow-bend-left-up ph-bold ph-lg"></i>
|
|
||||||
</MkA>
|
|
||||||
<MkA
|
|
||||||
v-if="
|
|
||||||
conversation &&
|
|
||||||
appearNote.renoteId &&
|
|
||||||
appearNote.renoteId != parentId &&
|
|
||||||
!appearNote.replyId
|
|
||||||
"
|
|
||||||
:to="`/notes/${appearNote.renoteId}`"
|
|
||||||
class="reply-icon"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<i class="ph-quotes ph-bold ph-lg"></i>
|
|
||||||
</MkA>
|
|
||||||
<Mfm
|
|
||||||
v-if="appearNote.cw != ''"
|
|
||||||
class="text"
|
|
||||||
:text="appearNote.cw"
|
|
||||||
:author="appearNote.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="appearNote.emojis"
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<XCwButton v-model="showContent" :note="note" />
|
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
v-show="appearNote.cw == null || showContent"
|
|
||||||
class="content"
|
|
||||||
>
|
|
||||||
<MkSubNoteContent
|
|
||||||
class="text"
|
|
||||||
:note="note"
|
|
||||||
:detailed="true"
|
|
||||||
:parentId="appearNote.parentId"
|
|
||||||
:conversation="conversation"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="translating || translation" class="translation">
|
<div v-if="translating || translation" class="translation">
|
||||||
<MkLoading v-if="translating" mini />
|
<MkLoading v-if="translating" mini />
|
||||||
<div v-else class="translated">
|
<div v-else class="translated">
|
||||||
|
@ -212,7 +173,6 @@ import XStarButton from "@/components/MkStarButton.vue";
|
||||||
import XStarButtonNoEmoji from "@/components/MkStarButtonNoEmoji.vue";
|
import XStarButtonNoEmoji from "@/components/MkStarButtonNoEmoji.vue";
|
||||||
import XRenoteButton from "@/components/MkRenoteButton.vue";
|
import XRenoteButton from "@/components/MkRenoteButton.vue";
|
||||||
import XQuoteButton from "@/components/MkQuoteButton.vue";
|
import XQuoteButton from "@/components/MkQuoteButton.vue";
|
||||||
import XCwButton from "@/components/MkCwButton.vue";
|
|
||||||
import { pleaseLogin } from "@/scripts/please-login";
|
import { pleaseLogin } from "@/scripts/please-login";
|
||||||
import { getNoteMenu } from "@/scripts/get-note-menu";
|
import { getNoteMenu } from "@/scripts/get-note-menu";
|
||||||
import { notePage } from "@/filters/note";
|
import { notePage } from "@/filters/note";
|
||||||
|
@ -262,7 +222,6 @@ let appearNote = $computed(() =>
|
||||||
const isDeleted = ref(false);
|
const isDeleted = ref(false);
|
||||||
const translation = ref(null);
|
const translation = ref(null);
|
||||||
const translating = ref(false);
|
const translating = ref(false);
|
||||||
let showContent = $ref(false);
|
|
||||||
const replies: misskey.entities.Note[] =
|
const replies: misskey.entities.Note[] =
|
||||||
props.conversation
|
props.conversation
|
||||||
?.filter(
|
?.filter(
|
||||||
|
@ -400,35 +359,6 @@ function noteClick(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
> .body {
|
> .body {
|
||||||
.reply-icon {
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 0.2em 0.2em;
|
|
||||||
margin-right: 0.2em;
|
|
||||||
color: var(--accent);
|
|
||||||
transition: background 0.2s;
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background: var(--buttonHoverBg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .cw {
|
|
||||||
cursor: default;
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
> .text {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .content {
|
|
||||||
> .text {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> .translation {
|
> .translation {
|
||||||
border: solid 0.5px var(--divider);
|
border: solid 0.5px var(--divider);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
|
|
@ -1,77 +1,110 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wrmlmaau" :class="{ collapsed, isLong }">
|
<p v-if="note.cw != null" class="cw">
|
||||||
<div class="body">
|
<MkA
|
||||||
<span v-if="note.deletedAt" style="opacity: 0.5"
|
v-if="!detailed && note.replyId"
|
||||||
>({{ i18n.ts.deleted }})</span
|
:to="`/notes/${note.replyId}`"
|
||||||
>
|
class="reply-icon"
|
||||||
<template v-if="!note.cw">
|
@click.stop
|
||||||
<MkA
|
|
||||||
v-if="note.replyId"
|
|
||||||
:to="`/notes/${note.replyId}`"
|
|
||||||
class="reply-icon"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<i class="ph-arrow-bend-left-up ph-bold ph-lg"></i>
|
|
||||||
</MkA>
|
|
||||||
<MkA
|
|
||||||
v-if="
|
|
||||||
conversation &&
|
|
||||||
note.renoteId &&
|
|
||||||
note.renoteId != parentId &&
|
|
||||||
!note.replyId
|
|
||||||
"
|
|
||||||
:to="`/notes/${note.renoteId}`"
|
|
||||||
class="reply-icon"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<i class="ph-quotes ph-bold ph-lg"></i>
|
|
||||||
</MkA>
|
|
||||||
</template>
|
|
||||||
<Mfm
|
|
||||||
v-if="note.text"
|
|
||||||
:text="note.text"
|
|
||||||
:author="note.user"
|
|
||||||
:i="$i"
|
|
||||||
:custom-emojis="note.emojis"
|
|
||||||
/>
|
|
||||||
<MkA v-if="note.renoteId" class="rp" :to="`/notes/${note.renoteId}`"
|
|
||||||
>{{ i18n.ts.quoteAttached }}: ...</MkA
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div v-if="note.files.length > 0">
|
|
||||||
<XMediaList :media-list="note.files" />
|
|
||||||
</div>
|
|
||||||
<div v-if="note.poll">
|
|
||||||
<summary>{{ i18n.ts.poll }}</summary>
|
|
||||||
<XPoll :note="note" />
|
|
||||||
</div>
|
|
||||||
<template v-if="detailed">
|
|
||||||
<!-- <div v-if="note.renoteId" class="renote">
|
|
||||||
<XNoteSimple :note="note.renote"/>
|
|
||||||
</div> -->
|
|
||||||
<MkUrlPreview
|
|
||||||
v-for="url in urls"
|
|
||||||
:key="url"
|
|
||||||
:url="url"
|
|
||||||
:compact="true"
|
|
||||||
:detail="false"
|
|
||||||
class="url-preview"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<button
|
|
||||||
v-if="isLong && collapsed"
|
|
||||||
class="fade _button"
|
|
||||||
@click.stop="collapsed = false"
|
|
||||||
>
|
>
|
||||||
<span>{{ i18n.ts.showMore }}</span>
|
<i class="ph-arrow-bend-left-up ph-bold ph-lg"></i>
|
||||||
</button>
|
</MkA>
|
||||||
<button
|
<MkA
|
||||||
v-if="isLong && !collapsed"
|
v-if="
|
||||||
class="showLess _button"
|
conversation &&
|
||||||
@click.stop="collapsed = true"
|
note.renoteId &&
|
||||||
|
note.renoteId != parentId &&
|
||||||
|
!note.replyId
|
||||||
|
"
|
||||||
|
:to="`/notes/${note.renoteId}`"
|
||||||
|
class="reply-icon"
|
||||||
|
@click.stop
|
||||||
>
|
>
|
||||||
<span>{{ i18n.ts.showLess }}</span>
|
<i class="ph-quotes ph-bold ph-lg"></i>
|
||||||
</button>
|
</MkA>
|
||||||
|
<Mfm
|
||||||
|
v-if="note.cw != ''"
|
||||||
|
class="text"
|
||||||
|
:text="note.cw"
|
||||||
|
:author="note.user"
|
||||||
|
:i="$i"
|
||||||
|
:custom-emojis="note.emojis"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="wrmlmaau"
|
||||||
|
>
|
||||||
|
<div class="content" :class="{ collapsed, isLong, showContent: note.cw && !showContent }">
|
||||||
|
<div class="body">
|
||||||
|
<span v-if="note.deletedAt" style="opacity: 0.5"
|
||||||
|
>({{ i18n.ts.deleted }})</span
|
||||||
|
>
|
||||||
|
<template v-if="!note.cw">
|
||||||
|
<MkA
|
||||||
|
v-if="!detailed && note.replyId"
|
||||||
|
:to="`/notes/${note.replyId}`"
|
||||||
|
class="reply-icon"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<i class="ph-arrow-bend-left-up ph-bold ph-lg"></i>
|
||||||
|
</MkA>
|
||||||
|
<MkA
|
||||||
|
v-if="
|
||||||
|
conversation &&
|
||||||
|
note.renoteId &&
|
||||||
|
note.renoteId != parentId &&
|
||||||
|
!note.replyId
|
||||||
|
"
|
||||||
|
:to="`/notes/${note.renoteId}`"
|
||||||
|
class="reply-icon"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<i class="ph-quotes ph-bold ph-lg"></i>
|
||||||
|
</MkA>
|
||||||
|
</template>
|
||||||
|
<Mfm
|
||||||
|
v-if="note.text"
|
||||||
|
:text="note.text"
|
||||||
|
:author="note.user"
|
||||||
|
:i="$i"
|
||||||
|
:custom-emojis="note.emojis"
|
||||||
|
/>
|
||||||
|
<MkA v-if="!detailed && note.renoteId" class="rp" :to="`/notes/${note.renoteId}`"
|
||||||
|
>{{ i18n.ts.quoteAttached }}: ...</MkA
|
||||||
|
>
|
||||||
|
<div v-if="note.files.length > 0" class="files">
|
||||||
|
<XMediaList :media-list="note.files" />
|
||||||
|
</div>
|
||||||
|
<XPoll v-if="note.poll" :note="note" class="poll"/>
|
||||||
|
<template v-if="detailed">
|
||||||
|
<MkUrlPreview
|
||||||
|
v-for="url in urls"
|
||||||
|
:key="url"
|
||||||
|
:url="url"
|
||||||
|
:compact="true"
|
||||||
|
:detail="false"
|
||||||
|
class="url-preview"
|
||||||
|
/>
|
||||||
|
<div v-if="note.renote" class="renote" @click.stop="emit('push', note.renote)">
|
||||||
|
<XNoteSimple :note="note.renote"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
v-if="isLong && collapsed"
|
||||||
|
class="fade _button"
|
||||||
|
@click.stop="collapsed = false"
|
||||||
|
>
|
||||||
|
<span>{{ i18n.ts.showMore }}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="isLong && !collapsed"
|
||||||
|
class="showLess _button"
|
||||||
|
@click.stop="collapsed = true"
|
||||||
|
>
|
||||||
|
<span>{{ i18n.ts.showLess }}</span>
|
||||||
|
</button>
|
||||||
|
<XCwButton v-if="note.cw" v-model="showContent" :note="note" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -83,6 +116,7 @@ import XNoteSimple from "@/components/MkNoteSimple.vue";
|
||||||
import XMediaList from "@/components/MkMediaList.vue";
|
import XMediaList from "@/components/MkMediaList.vue";
|
||||||
import XPoll from "@/components/MkPoll.vue";
|
import XPoll from "@/components/MkPoll.vue";
|
||||||
import MkUrlPreview from "@/components/MkUrlPreview.vue";
|
import MkUrlPreview from "@/components/MkUrlPreview.vue";
|
||||||
|
import XCwButton from "@/components/MkCwButton.vue";
|
||||||
import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
|
import { extractUrlFromMfm } from "@/scripts/extract-url-from-mfm";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
|
@ -91,82 +125,157 @@ const props = defineProps<{
|
||||||
parentId?;
|
parentId?;
|
||||||
conversation?;
|
conversation?;
|
||||||
detailed?: boolean;
|
detailed?: boolean;
|
||||||
|
detailedView?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isLong =
|
const emit = defineEmits<{
|
||||||
props.note.cw == null &&
|
(ev: "push", v): void;
|
||||||
props.note.text != null &&
|
}>();
|
||||||
(props.note.text.split("\n").length > 9 || props.note.text.length > 500);
|
|
||||||
|
|
||||||
|
const isLong = !props.detailedView && (
|
||||||
|
props.note.cw == null &&
|
||||||
|
props.note.text != null &&
|
||||||
|
(props.note.text.split("\n").length > 9 || props.note.text.length > 500)
|
||||||
|
);
|
||||||
const collapsed = $ref(props.note.cw == null && isLong);
|
const collapsed = $ref(props.note.cw == null && isLong);
|
||||||
const urls = props.note.text
|
const urls = props.note.text
|
||||||
? extractUrlFromMfm(mfm.parse(props.note.text))
|
? extractUrlFromMfm(mfm.parse(props.note.text)).slice(0, 5)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
let showContent = $ref(false);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.wrmlmaau {
|
.reply-icon {
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.2em 0.2em;
|
||||||
|
margin-right: 0.2em;
|
||||||
|
color: var(--accent);
|
||||||
|
transition: background 0.2s;
|
||||||
|
&:hover, &:focus {
|
||||||
|
background: var(--buttonHoverBg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.cw {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
> .text {
|
||||||
> .body {
|
margin-right: 8px;
|
||||||
> .rp {
|
|
||||||
margin-left: 4px;
|
|
||||||
font-style: oblique;
|
|
||||||
color: var(--renote);
|
|
||||||
}
|
|
||||||
.reply-icon {
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 0.2em 0.2em;
|
|
||||||
margin-right: 0.2em;
|
|
||||||
color: var(--accent);
|
|
||||||
transition: background 0.2s;
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background: var(--buttonHoverBg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
> .mk-url-preview {
|
.wrmlmaau {
|
||||||
margin-top: 8px;
|
.content {
|
||||||
}
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
&.collapsed {
|
|
||||||
position: relative;
|
|
||||||
max-height: 9em;
|
|
||||||
overflow: hidden;
|
|
||||||
> .body {
|
> .body {
|
||||||
max-height: 9em;
|
transition: filter .1s;
|
||||||
mask: linear-gradient(black calc(100% - 64px), transparent);
|
> .rp {
|
||||||
-webkit-mask: linear-gradient(black calc(100% - 64px), transparent);
|
margin-left: 4px;
|
||||||
}
|
font-style: oblique;
|
||||||
> .fade {
|
color: var(--renote);
|
||||||
display: block;
|
}
|
||||||
position: absolute;
|
.reply-icon {
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 64px;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: var(--panel);
|
border-radius: 6px;
|
||||||
padding: 6px 10px;
|
padding: 0.2em 0.2em;
|
||||||
font-size: 0.8em;
|
margin-right: 0.2em;
|
||||||
border-radius: 999px;
|
color: var(--accent);
|
||||||
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
transition: background 0.2s;
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background: var(--buttonHoverBg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .files {
|
||||||
|
margin-top: 0.4em;
|
||||||
|
margin-bottom: 0.4em;
|
||||||
|
}
|
||||||
|
> .url-preview {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .poll {
|
||||||
|
font-size: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
> .renote {
|
||||||
|
padding-top: 8px;
|
||||||
|
> * {
|
||||||
|
padding: 16px;
|
||||||
|
border: solid 1px var(--renote);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
&:hover,
|
||||||
|
&:focus-within {
|
||||||
|
background-color: var(--panelHighlight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
&.collapsed, &.showContent {
|
||||||
|
position: relative;
|
||||||
|
max-height: calc(9em + 50px);
|
||||||
|
> .body {
|
||||||
|
max-height: inherit;
|
||||||
|
mask: linear-gradient(black calc(100% - 64px), transparent);
|
||||||
|
-webkit-mask: linear-gradient(black calc(100% - 64px), transparent);
|
||||||
|
padding-inline: 50px;
|
||||||
|
margin-inline: -50px;
|
||||||
|
margin-top: -50px;
|
||||||
|
padding-top: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
&.collapsed > .body {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
&.showContent {
|
||||||
|
> .body {
|
||||||
|
min-height: 2em;
|
||||||
|
max-height: 5em;
|
||||||
|
filter: blur(4px);
|
||||||
|
}
|
||||||
|
:deep(.fade) {
|
||||||
|
inset: 0;
|
||||||
|
top: 40px;
|
||||||
|
}
|
||||||
|
:deep(span) {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.fade) {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
> span {
|
> span {
|
||||||
background: var(--panelHighlight);
|
display: inline-block;
|
||||||
|
background: var(--panel);
|
||||||
|
padding: .4em 1em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
> span {
|
||||||
|
background: var(--panelHighlight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&.isLong {
|
:deep(.showLess) {
|
||||||
> .showLess {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
Loading…
Reference in New Issue