mirror of
https://activitypub.software/TransFem-org/Sharkey
synced 2024-12-22 16:30:13 +00:00
upd: rework threading/post ui 1/2
Co-authored-by: Gaspard Wierzbinski <contact@cpluspatch.com> Co-authored-by: Marie <marie@kaifa.ch>
This commit is contained in:
parent
ad5a6c1e41
commit
1a4bff7698
8 changed files with 327 additions and 81 deletions
|
@ -35,51 +35,48 @@ const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(p
|
|||
const themeColor = instance.themeColor ?? '#777777';
|
||||
|
||||
const bg = {
|
||||
background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
|
||||
//background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
|
||||
background: `${themeColor}`,
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
$height: 2ex;
|
||||
|
||||
.root {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $height;
|
||||
border-radius: var(--radius-xs) 0 0 var(--radius-xs);
|
||||
height: 1.5ex;
|
||||
border-radius: var(--radius-xl);
|
||||
margin-top: 5px;
|
||||
padding: 4px;
|
||||
overflow: clip;
|
||||
color: #fff;
|
||||
text-shadow: /* .866 ≈ sin(60deg) */
|
||||
1px 0 1px #000,
|
||||
.866px .5px 1px #000,
|
||||
.5px .866px 1px #000,
|
||||
0 1px 1px #000,
|
||||
-.5px .866px 1px #000,
|
||||
-.866px .5px 1px #000,
|
||||
-1px 0 1px #000,
|
||||
-.866px -.5px 1px #000,
|
||||
-.5px -.866px 1px #000,
|
||||
0 -1px 1px #000,
|
||||
.5px -.866px 1px #000,
|
||||
.866px -.5px 1px #000;
|
||||
mask-image: linear-gradient(90deg,
|
||||
rgb(0,0,0),
|
||||
rgb(0,0,0) calc(100% - 16px),
|
||||
rgba(0,0,0,0) 100%
|
||||
);
|
||||
text-shadow: -1px -1px 0 var(--bg),1px -1px 0 var(--bg),-1px 1px 0 var(--bg),1px 1px 0 var(--bg)
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: $height;
|
||||
height: 2ex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-left: 4px;
|
||||
line-height: 1;
|
||||
font-size: 0.9em;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: visible;
|
||||
overflow: hidden;
|
||||
overflow-wrap: anywhere;
|
||||
max-width: 300px;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container (max-width: 400px) {
|
||||
.name {
|
||||
max-width: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -47,11 +47,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
|
||||
</div>
|
||||
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
|
||||
<div style="display: flex; padding-bottom: 10px;">
|
||||
<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
|
||||
<MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/>
|
||||
<div :class="[$style.main, { [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
|
||||
<MkNoteHeader :note="appearNote" :mini="true" v-on:click.stop/>
|
||||
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
||||
<MkAvatar :class="[$style.avatar, { [$style.avatarReplyTo]: appearNote.reply }]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
|
||||
<div :class="$style.main">
|
||||
<MkNoteHeader :note="appearNote" :mini="true"/>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
|
||||
<div style="container-type: inline-size;">
|
||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
|
||||
|
@ -60,7 +63,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]" >
|
||||
<div :class="$style.text">
|
||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
||||
<MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
|
||||
<Mfm
|
||||
v-if="appearNote.text"
|
||||
:parsedNodes="parsed"
|
||||
|
@ -176,7 +178,6 @@ import MkCwButton from '@/components/MkCwButton.vue';
|
|||
import MkPoll from '@/components/MkPoll.vue';
|
||||
import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
|
||||
import MkUrlPreview from '@/components/MkUrlPreview.vue';
|
||||
import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { pleaseLogin } from '@/scripts/please-login.js';
|
||||
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
||||
|
@ -830,7 +831,7 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 32px 8px 32px;
|
||||
padding: 24px 38px 16px;
|
||||
line-height: 28px;
|
||||
white-space: pre;
|
||||
color: var(--renote);
|
||||
|
@ -882,7 +883,7 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
align-items: center;
|
||||
line-height: 28px;
|
||||
white-space: pre;
|
||||
padding: 0 32px 18px;
|
||||
padding: 8px 38px 24px;
|
||||
}
|
||||
|
||||
.collapsedRenoteTargetAvatar {
|
||||
|
@ -909,7 +910,6 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
|
||||
.article {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 28px 32px;
|
||||
}
|
||||
|
||||
|
@ -926,12 +926,19 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
.avatar {
|
||||
flex-shrink: 0;
|
||||
display: block !important;
|
||||
position: sticky !important;
|
||||
margin: 0 14px 0 0;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
position: sticky !important;
|
||||
top: calc(22px + var(--stickyTop, 0px));
|
||||
left: 0;
|
||||
transition: top 0.5s;
|
||||
|
||||
&.avatarReplyTo {
|
||||
position: relative !important;
|
||||
top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
|
@ -994,7 +1001,6 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
|
||||
.text {
|
||||
overflow-wrap: break-word;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.replyIcon {
|
||||
|
@ -1027,7 +1033,8 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
|
||||
.quoteNote {
|
||||
padding: 16px;
|
||||
border: dashed 1px var(--renote);
|
||||
// Made border solid, stylistic choice
|
||||
border: solid 1px var(--renote);
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: clip;
|
||||
}
|
||||
|
@ -1067,7 +1074,11 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
}
|
||||
|
||||
.renote {
|
||||
padding: 12px 26px 0 26px;
|
||||
padding: 24px 28px 16px;
|
||||
}
|
||||
|
||||
.collapsedRenoteTarget {
|
||||
padding: 8px 28px 24px;
|
||||
}
|
||||
|
||||
.article {
|
||||
|
@ -1085,12 +1096,8 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.renote {
|
||||
padding: 10px 22px 0 22px;
|
||||
}
|
||||
|
||||
.article {
|
||||
padding: 20px 22px;
|
||||
padding: 23px 25px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
|
@ -1100,7 +1107,7 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
|
||||
@container (max-width: 480px) {
|
||||
.renote {
|
||||
padding: 8px 16px 0 16px;
|
||||
padding: 20px 24px 8px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
|
@ -1108,12 +1115,12 @@ function emitUpdReaction(emoji: string, delta: number) {
|
|||
}
|
||||
|
||||
.collapsedRenoteTarget {
|
||||
padding: 0 16px 9px;
|
||||
padding: 8px 24px 20px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.article {
|
||||
padding: 14px 16px;
|
||||
padding: 22px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
v-hotkey="keymap"
|
||||
:class="$style.root"
|
||||
>
|
||||
<div v-if="appearNote.reply && appearNote.reply.replyId">
|
||||
<div v-if="!conversationLoaded" style="padding: 16px">
|
||||
<div v-if="appearNote.reply && appearNote.reply.replyId && !conversationLoaded" style="padding: 16px">
|
||||
<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
|
||||
</div>
|
||||
<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
|
||||
</div>
|
||||
<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
|
||||
<div v-if="isRenote" :class="$style.renote">
|
||||
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
|
||||
<i class="ph-rocket-launch ph-bold ph-lg" style="margin-right: 4px;"></i>
|
||||
|
@ -43,15 +39,29 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="appearNote.reply && appearNote.reply.replyId">
|
||||
<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
|
||||
</template>
|
||||
<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
|
||||
<article :class="$style.note" @contextmenu.stop="onContextmenu">
|
||||
<header :class="$style.noteHeader">
|
||||
<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
|
||||
<div style="display: flex; align-items: center; white-space: nowrap; overflow: hidden;">
|
||||
<div :class="$style.noteHeaderBody">
|
||||
<div>
|
||||
<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
|
||||
<MkUserName :nowrap="false" :user="appearNote.user"/>
|
||||
</MkA>
|
||||
<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
|
||||
<span v-if="appearNote.user.badgeRoles" :class="$style.badgeRoles">
|
||||
<img v-for="role in appearNote.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
|
||||
</span>
|
||||
</div>
|
||||
<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: flex-end; margin-left: auto;">
|
||||
<div :class="$style.noteHeaderBody">
|
||||
<div :class="$style.noteHeaderInfo">
|
||||
<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
|
||||
<i v-if="appearNote.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
|
||||
|
@ -61,10 +71,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
|
||||
<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
|
||||
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div :class="$style.noteContent">
|
||||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||
|
@ -73,7 +82,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</p>
|
||||
<div v-show="appearNote.cw == null || showContent">
|
||||
<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
|
||||
<MkA v-if="appearNote.replyId" :class="$style.noteReplyTarget" :to="`/notes/${appearNote.replyId}`"><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
|
||||
<Mfm
|
||||
v-if="appearNote.text"
|
||||
:parsedNodes="parsed"
|
||||
|
@ -851,12 +859,19 @@ function animatedMFM() {
|
|||
|
||||
.noteHeaderInfo {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.noteHeaderUsername {
|
||||
margin-bottom: 2px;
|
||||
line-height: 1.3;
|
||||
word-wrap: anywhere;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.playMFMButton {
|
||||
|
@ -1037,9 +1052,31 @@ function animatedMFM() {
|
|||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex-shrink: 0 !important;
|
||||
display: block !important;
|
||||
margin: 0 10px 0 0 !important;
|
||||
width: 40px !important;
|
||||
height: 40px !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
}
|
||||
|
||||
.muted {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.badgeRoles {
|
||||
margin: 0 .5em 0 0;
|
||||
}
|
||||
|
||||
.badgeRole {
|
||||
height: 1.3em;
|
||||
vertical-align: -20%;
|
||||
|
||||
& + .badgeRole {
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,7 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<header :class="$style.root">
|
||||
<header v-if="!classic" :class="$style.root">
|
||||
<div :class="$style.section">
|
||||
<div style="display: flex;">
|
||||
<div v-if="mock" :class="$style.name">
|
||||
<MkUserName :user="note.user"/>
|
||||
</div>
|
||||
|
@ -12,11 +14,46 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkUserName :user="note.user"/>
|
||||
</MkA>
|
||||
<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
|
||||
<div :class="$style.username"><MkAcct :user="note.user"/></div>
|
||||
<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
|
||||
<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.username"><MkAcct :user="note.user"/></div>
|
||||
</div>
|
||||
<div :class="$style.section">
|
||||
<div :class="$style.info">
|
||||
<div v-if="mock">
|
||||
<MkTime :time="note.createdAt" colored/>
|
||||
</div>
|
||||
<MkA v-else :class="$style.time" :to="notePage(note)">
|
||||
<MkTime :time="note.createdAt" colored/>
|
||||
</MkA>
|
||||
<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
|
||||
<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
|
||||
<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
|
||||
<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
|
||||
</span>
|
||||
<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
|
||||
<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
|
||||
<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
|
||||
</div>
|
||||
<div :class="$style.info"><MkInstanceTicker v-if="showTicker" style="cursor: pointer;" :instance="note.user.instance" @click.stop="showOnRemote()"/></div>
|
||||
</div>
|
||||
</header>
|
||||
<header v-else :class="$style.classicRoot">
|
||||
<div v-if="mock" :class="$style.name">
|
||||
<MkUserName :user="note.user"/>
|
||||
</div>
|
||||
<MkA v-else v-user-preview="note.user.id" :class="$style.classicName" :to="userPage(note.user)">
|
||||
<MkUserName :user="note.user"/>
|
||||
</MkA>
|
||||
<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
|
||||
<div :class="$style.classicUsername"><MkAcct :user="note.user"/></div>
|
||||
<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
|
||||
<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
|
||||
</div>
|
||||
<MkInstanceTicker v-if="showTicker && !isMobile && defaultStore.state.showTickerOnReplies" style="cursor: pointer; max-height: 5px; top: 3px; position: relative; margin-top: 0px !important;" :instance="note.user.instance" @click.stop="showOnRemote()"/>
|
||||
<div :class="$style.classicInfo">
|
||||
<div v-if="mock">
|
||||
<MkTime :time="note.createdAt" colored/>
|
||||
</div>
|
||||
|
@ -36,19 +73,29 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, shallowRef } from 'vue';
|
||||
import { inject, shallowRef, ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { notePage } from '@/filters/note.js';
|
||||
import { userPage } from '@/filters/user.js';
|
||||
import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
|
||||
import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
|
||||
import { popupMenu } from '@/os.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { deviceKind } from '@/scripts/device-kind.js';
|
||||
|
||||
const props = defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
classic?: boolean;
|
||||
}>();
|
||||
|
||||
const menuVersionsButton = shallowRef<HTMLElement>();
|
||||
const router = useRouter();
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && props.note.user.instance);
|
||||
|
||||
const MOBILE_THRESHOLD = 500;
|
||||
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
|
||||
|
||||
async function menuVersions(viaKeyboard = false): Promise<void> {
|
||||
const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuVersionsButton });
|
||||
|
@ -57,18 +104,67 @@ async function menuVersions(viaKeyboard = false): Promise<void> {
|
|||
}).then(focus).finally(cleanup);
|
||||
}
|
||||
|
||||
function showOnRemote() {
|
||||
if (props.note.url ?? props.note.uri === undefined) router.push(notePage(props.note));
|
||||
else window.open(props.note.url ?? props.note.uri);
|
||||
}
|
||||
|
||||
const mock = inject<boolean>('mock', false);
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
display: flex;
|
||||
cursor: auto; /* not clickToOpen-able */
|
||||
}
|
||||
|
||||
.classicRoot {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
white-space: nowrap;
|
||||
cursor: auto; /* not clickToOpen-able */
|
||||
}
|
||||
|
||||
.section {
|
||||
align-items: flex-start;
|
||||
white-space: nowrap;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-child {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
margin-left: auto;
|
||||
padding-left: 10px;
|
||||
overflow: clip;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-shrink: 1;
|
||||
display: block;
|
||||
// note, these margin top values were done by hand may need futher checking if it actualy aligns pixel perfect
|
||||
margin: 3px .5em 0 0;
|
||||
padding: 0;
|
||||
overflow: scroll;
|
||||
overflow-wrap: anywhere;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 300px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--nameHover);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.classicName {
|
||||
flex-shrink: 1;
|
||||
display: block;
|
||||
margin: 0 .5em 0 0;
|
||||
|
@ -95,6 +191,20 @@ const mock = inject<boolean>('mock', false);
|
|||
}
|
||||
|
||||
.username {
|
||||
flex-shrink: 9999999;
|
||||
// note these top margins were made to align with the instance ticker
|
||||
margin: 4px .5em 0 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: .95em;
|
||||
max-width: 300px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.classicUsername {
|
||||
flex-shrink: 9999999;
|
||||
margin: 0 .5em 0 0;
|
||||
overflow: hidden;
|
||||
|
@ -102,11 +212,34 @@ const mock = inject<boolean>('mock', false);
|
|||
}
|
||||
|
||||
.info {
|
||||
&:first-child {
|
||||
margin-top: 4px;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
.classicInfo {
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.time {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.badgeRoles {
|
||||
margin: 0 .5em 0 0;
|
||||
}
|
||||
|
@ -119,4 +252,14 @@ const mock = inject<boolean>('mock', false);
|
|||
margin-left: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
@container (max-width: 500px) {
|
||||
.name, .username {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div :class="$style.root">
|
||||
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
||||
<div :class="$style.main">
|
||||
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
||||
<MkNoteHeader :class="$style.header" :classic="true" :note="note" :mini="true"/>
|
||||
<div>
|
||||
<p v-if="note.cw != null" :class="$style.cw">
|
||||
<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
|
||||
|
|
|
@ -5,11 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<div v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
|
||||
<div v-if="!hideLine" :class="$style.line"></div>
|
||||
<div :class="$style.main">
|
||||
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
|
||||
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
||||
<div :class="$style.body">
|
||||
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
||||
<MkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
|
||||
<div :class="$style.content">
|
||||
<p v-if="note.cw != null" :class="$style.cw">
|
||||
<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
|
||||
|
@ -106,6 +107,7 @@ import { getNoteMenu } from '@/scripts/get-note-menu.js';
|
|||
import { useNoteCapture } from '@/scripts/use-note-capture.js';
|
||||
|
||||
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
|
||||
const hideLine = computed(() => { return props.detail ? true : false; });
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
|
@ -361,7 +363,7 @@ if (props.detail) {
|
|||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
padding: 16px 32px;
|
||||
padding: 28px 32px;
|
||||
font-size: 0.9em;
|
||||
position: relative;
|
||||
|
||||
|
@ -371,6 +373,14 @@ if (props.detail) {
|
|||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 60px;
|
||||
// using solid instead of dotted, stylelistic choice
|
||||
border-left: 2.5px solid rgb(174, 174, 174);
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
@ -396,9 +406,9 @@ if (props.detail) {
|
|||
.avatar {
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
margin: 0 8px 0 0;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
margin: 0 14px 0 0;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
|
@ -411,6 +421,11 @@ if (props.detail) {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
@ -430,6 +445,36 @@ if (props.detail) {
|
|||
}
|
||||
}
|
||||
|
||||
.reply, .more {
|
||||
border-left: solid 0.5px var(--divider);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.more {
|
||||
padding: 10px 0 0 16px;
|
||||
}
|
||||
|
||||
@container (max-width: 580px) {
|
||||
.root {
|
||||
padding: 28px 26px 0;
|
||||
}
|
||||
|
||||
.line {
|
||||
left: 50.5px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@container (max-width: 500px) {
|
||||
.root {
|
||||
padding: 23px 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@container (max-width: 400px) {
|
||||
.noteFooterButton {
|
||||
&:not(:last-child) {
|
||||
|
@ -469,9 +514,9 @@ if (props.detail) {
|
|||
padding: 10px 0 0 16px;
|
||||
}
|
||||
|
||||
@container (max-width: 450px) {
|
||||
@container (max-width: 480px) {
|
||||
.root {
|
||||
padding: 14px 16px;
|
||||
padding: 22px 24px;
|
||||
|
||||
&.children {
|
||||
padding: 10px 0 0 8px;
|
||||
|
@ -479,6 +524,17 @@ if (props.detail) {
|
|||
}
|
||||
}
|
||||
|
||||
@container (max-width: 450px) {
|
||||
.line {
|
||||
left: 46px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
}
|
||||
}
|
||||
|
||||
.muted {
|
||||
text-align: center;
|
||||
padding: 8px !important;
|
||||
|
|
|
@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
|
||||
<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
|
||||
<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
|
||||
<MkSwitch v-model="showTickerOnReplies">Show instance ticker on replies</MkSwitch>
|
||||
<MkRadios v-model="reactionsDisplaySize">
|
||||
<template #label>{{ i18n.ts.reactionsDisplaySize }}</template>
|
||||
<option value="small">{{ i18n.ts.small }}</option>
|
||||
|
@ -271,6 +272,7 @@ const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificati
|
|||
const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
|
||||
const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline'));
|
||||
const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications'));
|
||||
const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies'));
|
||||
|
||||
watch(lang, () => {
|
||||
miLocalStorage.setItem('lang', lang.value as string);
|
||||
|
|
|
@ -250,6 +250,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||
where: 'device',
|
||||
default: false,
|
||||
},
|
||||
showTickerOnReplies: {
|
||||
where: 'device',
|
||||
default: false,
|
||||
},
|
||||
enableInfiniteScroll: {
|
||||
where: 'device',
|
||||
default: true,
|
||||
|
|
Loading…
Reference in a new issue