note-improvements (#9734)

Not sure if this is the best way to jump to the post

This also lets you select text w/out clicking to the post (and clicking normally on top of the text opens the post)

Co-authored-by: Freeplay <Freeplay@duck.com>
Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9734
Co-authored-by: Free <freeplay@duck.com>
Co-committed-by: Free <freeplay@duck.com>
This commit is contained in:
Free 2023-03-20 02:31:02 +00:00 committed by Kainoa Kanter
parent d75a4e56e3
commit c2df136dda
4 changed files with 48 additions and 15 deletions

View File

@ -33,7 +33,7 @@
</div> </div>
</div> </div>
</div> </div>
<article class="article" @contextmenu.stop="onContextmenu" @click="router.push(notePage(appearNote))"> <article class="article" @contextmenu.stop="onContextmenu" @click="noteClick">
<div class="main"> <div class="main">
<div class="header-container"> <div class="header-container">
<MkAvatar class="avatar" :user="appearNote.user"/> <MkAvatar class="avatar" :user="appearNote.user"/>
@ -41,16 +41,16 @@
</div> </div>
<div class="body"> <div class="body">
<p v-if="appearNote.cw != null" class="cw"> <p 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" @click.stop/> <Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :custom-emojis="appearNote.emojis" :i="$i"/>
<XCwButton v-model="showContent" :note="appearNote"/> <XCwButton v-model="showContent" :note="appearNote"/>
</p> </p>
<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed, isLong }"> <div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed, isLong }">
<div class="text"> <div class="text">
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis" @click.stop/> <Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
<!-- <a v-if="appearNote.renote != null" class="rp">RN:</a> --> <!-- <a v-if="appearNote.renote != null" class="rp">RN:</a> -->
<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" @click.stop> <div v-else class="translated">
<b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b>
<Mfm :text="translation.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> <Mfm :text="translation.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
</div> </div>
@ -264,6 +264,14 @@ function focusAfter() {
focusNext(el.value); focusNext(el.value);
} }
function noteClick(e) {
if (document.getSelection().type === 'Range') {
e.stopPropagation();
} else {
router.push(notePage(appearNote))
}
}
function readPromo() { function readPromo() {
os.api('promo/read', { os.api('promo/read', {
noteId: appearNote.id, noteId: appearNote.id,

View File

@ -9,8 +9,8 @@
:tabindex="!isDeleted ? '-1' : null" :tabindex="!isDeleted ? '-1' : null"
:class="{ renote: isRenote }" :class="{ renote: isRenote }"
> >
<MkNoteSub v-for="note in conversation" :key="note.id" class="reply-to-more" :note="note" @click.self="router.push(notePage(note))"/> <MkNoteSub v-for="note in conversation" :key="note.id" class="reply-to-more" :note="note"/>
<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" class="reply-to" @click.self="router.push(notePage(appearNote))"/> <MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" class="reply-to"/>
<div v-if="isRenote" class="renote"> <div v-if="isRenote" class="renote">
<MkAvatar class="avatar" :user="note.user"/> <MkAvatar class="avatar" :user="note.user"/>
<i class="ph-repeat ph-bold ph-lg"></i> <i class="ph-repeat ph-bold ph-lg"></i>
@ -29,7 +29,7 @@
<MkVisibility :note="note"/> <MkVisibility :note="note"/>
</div> </div>
</div> </div>
<article class="article" @contextmenu.stop="onContextmenu"> <article ref="noteEl" class="article" @contextmenu.stop="onContextmenu" tabindex="-1">
<header class="header"> <header class="header">
<MkAvatar class="avatar" :user="appearNote.user" :show-indicator="true"/> <MkAvatar class="avatar" :user="appearNote.user" :show-indicator="true"/>
<div class="body"> <div class="body">
@ -99,7 +99,7 @@
</footer> </footer>
</div> </div>
</article> </article>
<MkNoteSub v-for="note in directReplies" :key="note.id" :note="note" class="reply" :conversation="replies" @click.self="router.push(notePage(note))"/> <MkNoteSub v-for="note in directReplies" :key="note.id" :note="note" class="reply" :conversation="replies"/>
</div> </div>
<div v-else class="_panel muted" @click="muted = false"> <div v-else class="_panel muted" @click="muted = false">
<I18n :src="i18n.ts.userSaysSomething" tag="small"> <I18n :src="i18n.ts.userSaysSomething" tag="small">
@ -113,7 +113,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onMounted, onUnmounted, reactive, ref } from 'vue'; import { computed, inject, onMounted, onUnmounted, onUpdated, reactive, ref } 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 MkNoteSub from '@/components/MkNoteSub.vue'; import MkNoteSub from '@/components/MkNoteSub.vue';
@ -175,6 +175,7 @@ const isRenote = (
); );
const el = ref<HTMLElement>(); const el = ref<HTMLElement>();
const noteEl = $ref();
const menuButton = ref<HTMLElement>(); const menuButton = ref<HTMLElement>();
const starButton = ref<InstanceType<typeof XStarButton>>(); const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
@ -192,6 +193,8 @@ const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultS
const conversation = ref<misskey.entities.Note[]>([]); const conversation = ref<misskey.entities.Note[]>([]);
const replies = ref<misskey.entities.Note[]>([]); const replies = ref<misskey.entities.Note[]>([]);
const directReplies = ref<misskey.entities.Note[]>([]); const directReplies = ref<misskey.entities.Note[]>([]);
let isScrolling;
const keymap = { const keymap = {
'r': () => reply(true), 'r': () => reply(true),
@ -281,11 +284,11 @@ function showRenoteMenu(viaKeyboard = false): void {
} }
function focus() { function focus() {
el.value.focus(); noteEl.focus();
} }
function blur() { function blur() {
el.value.blur(); noteEl.blur();
} }
os.api('notes/children', { os.api('notes/children', {
@ -302,6 +305,7 @@ if (appearNote.replyId) {
noteId: appearNote.replyId, noteId: appearNote.replyId,
}).then(res => { }).then(res => {
conversation.value = res.reverse(); conversation.value = res.reverse();
focus();
}); });
} }
@ -322,13 +326,26 @@ function onNoteReplied(noteData: NoteUpdatedEvent): void {
} }
document.addEventListener("wheel", () => {
isScrolling = true;
})
onMounted(() => { onMounted(() => {
stream.on("noteUpdated", onNoteReplied); stream.on("noteUpdated", onNoteReplied);
isScrolling = false;
noteEl.scrollIntoView();
}); });
onUpdated(() => {
if (!isScrolling) {
noteEl.scrollIntoView()
}
})
onUnmounted(() => { onUnmounted(() => {
stream.off("noteUpdated", onNoteReplied); stream.off("noteUpdated", onNoteReplied);
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -434,7 +451,8 @@ onUnmounted(() => {
} }
font-size: 1.2em; font-size: 1.2em;
overflow: clip; overflow: clip;
outline: none;
scroll-margin-top: calc(var(--stickyTop) + 20vh);
> .header { > .header {
display: flex; display: flex;
position: relative; position: relative;

View File

@ -1,7 +1,7 @@
<template> <template>
<div v-size="{ max: [450, 500] }" class="wrpstxzv" :class="{ children: depth > 1, singleStart: replies.length == 1, firstColumn: depth == 1 && conversation }"> <div v-size="{ max: [450, 500] }" class="wrpstxzv" :class="{ children: depth > 1, singleStart: replies.length == 1, firstColumn: depth == 1 && conversation }">
<div v-if="conversation && depth > 1" class="line"></div> <div v-if="conversation && depth > 1" class="line"></div>
<div class="main" @click="router.push(notePage(note))"> <div class="main" @click="noteClick">
<div class="avatar-container"> <div class="avatar-container">
<MkAvatar class="avatar" :user="note.user"/> <MkAvatar class="avatar" :user="note.user"/>
<div v-if="(!conversation) || replies.length > 0" class="line"></div> <div v-if="(!conversation) || replies.length > 0" class="line"></div>
@ -19,7 +19,7 @@
<Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/> <Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis"/>
<XCwButton v-model="showContent" :note="note"/> <XCwButton v-model="showContent" :note="note"/>
</p> </p>
<div v-show="note.cw == null || showContent" class="content" @click="router.push(notePage(note))"> <div v-show="note.cw == null || showContent" class="content">
<MkSubNoteContent class="text" :note="note" :detailed="true" :parentId="note.parentId" :conversation="conversation"/> <MkSubNoteContent class="text" :note="note" :detailed="true" :parentId="note.parentId" :conversation="conversation"/>
</div> </div>
</div> </div>
@ -68,6 +68,14 @@ const props = withDefaults(defineProps<{
let showContent = $ref(false); let showContent = $ref(false);
const replies: misskey.entities.Note[] = props.conversation?.filter(item => item.replyId === props.note.id || item.renoteId === props.note.id).reverse() ?? []; const replies: misskey.entities.Note[] = props.conversation?.filter(item => item.replyId === props.note.id || item.renoteId === props.note.id).reverse() ?? [];
function noteClick(e) {
if (document.getSelection().type === 'Range') {
e.stopPropagation();
} else {
router.push(notePage(props.note))
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -88,7 +88,6 @@ html._themeChanging_ {
html, body { html, body {
margin: 0; margin: 0;
padding: 0; padding: 0;
scroll-behavior: smooth;
} }
a { a {