feat: 노트를 클릭하여 자세히 볼 수 있음

This commit is contained in:
NoriDev 2023-11-30 16:46:06 +09:00
parent 123b7a6668
commit d6fa889c93
13 changed files with 51 additions and 16 deletions

View file

@ -40,6 +40,7 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE
- 위치 조정
- 크기 조정
- 불투명도 조정
- Feat: 노트를 클릭하여 자세히 볼 수 있음
- Revert: 사용자 통계 표시 기능 제거 ([MisskeyIO/misskey@114c7fe6](https://github.com/MisskeyIO/misskey/commit/114c7fe6b37dd6bddbcd9d92406f8b13bf688e8b))
### Client

View file

@ -1317,6 +1317,8 @@ _cherrypick:
postFormVisibilityHotkeyDescription: "When writing a note, press Ctrl(control) + Shift to switch the visibility range. The hotkey to make it Local only is Ctrl(command or control) + Alt(option)."
showRenoteConfirmPopup: "Show confirmation popup when renote"
showRenoteConfirmPopupDescription: "This setting must have the \"General - Show renote and quote buttons separately\" setting turned on."
expandOnNoteClick: "Open note on click"
expandOnNoteClickDescription: "If disabled, you can still open 'Details' in the notes menu or by clicking the timestamp."
displayHeaderNavBarWhenScroll: "Show elements when scrolling (header, floating buttons, navigation bar)"
_displayHeaderNavBarWhenScroll:
all: "Display all"

2
locales/index.d.ts vendored
View file

@ -1330,6 +1330,8 @@ export interface Locale {
"postFormVisibilityHotkeyDescription": string;
"showRenoteConfirmPopup": string;
"showRenoteConfirmPopupDescription": string;
"expandOnNoteClick": string;
"expandOnNoteClickDescription": string;
"displayHeaderNavBarWhenScroll": string;
"_displayHeaderNavBarWhenScroll": {
"all": string;

View file

@ -1328,6 +1328,8 @@ _cherrypick:
postFormVisibilityHotkeyDescription: "ートを作成する際、Ctrl(control) + Shiftキーを押すと公開範囲を切り替えることができます。ローカルのみショートカットキーは、Ctrl(commandまたはcontrol) + Alt(option)キーです。"
showRenoteConfirmPopup: "Renoteするときに確認ポップアップを表示"
showRenoteConfirmPopupDescription: "この設定は「全般 - リノートと引用ボタンを分けて表示する」設定がオンになっている必要があります。"
expandOnNoteClick: "クリックでノートの詳細を開く"
expandOnNoteClickDescription: "オフの場合、ノートメニューの[詳細]をクリックするか、日付をクリックして開けます。"
displayHeaderNavBarWhenScroll: "スクロール時の要素表示(ヘッダー、フローティングボタン、ナビゲーションバー)"
_displayHeaderNavBarWhenScroll:
all: "全て表示"

View file

@ -1313,6 +1313,8 @@ _cherrypick:
postFormVisibilityHotkeyDescription: "노트를 작성할 때, Ctrl(control) + Shift 키를 누르면 공개 범위를 전환할 수 있어요. 로컬에만 보이게 하는 단축키는 Ctrl(command 또는 control) + Alt(option) 키에요."
showRenoteConfirmPopup: "리노트할 때 확인 팝업 표시"
showRenoteConfirmPopupDescription: "이 설정은 '일반 - 리노트와 인용 버튼을 분리해서 표시하기' 설정이 켜져 있어야 해요."
expandOnNoteClick: "노트를 클릭하여 자세히 표시"
expandOnNoteClickDescription: "비활성화한 경우에도 노트 메뉴에서 '자세히'를 클릭하거나 타임스탬프를 클릭하여 열 수 있어요."
displayHeaderNavBarWhenScroll: "스크롤 시 요소 표시 (헤더, 플로팅 버튼, 탐색 모음)"
_displayHeaderNavBarWhenScroll:
all: "모두 표시"

View file

@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkAvatar v-if="!defaultStore.state.hideAvatarsInNote" :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/>
<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">
<article v-else :class="$style.article" :style="{ cursor: expandOnNoteClick ? 'pointer' : '' }" @click="noteClick" @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 v-if="!defaultStore.state.hideAvatarsInNote" :class="[$style.avatar, { [$style.avatarReplyTo]: appearNote.reply, [$style.showEl]: !appearNote.reply && (showEl && ['hideHeaderOnly', 'hideHeaderFloatBtn', 'hide'].includes(<string>defaultStore.state.displayHeaderNavBarWhenScroll)) && mainRouter.currentRoute.value.name === 'index', [$style.showElTab]: !appearNote.reply && (showEl && ['hideHeaderOnly', 'hideHeaderFloatBtn', 'hide'].includes(<string>defaultStore.state.displayHeaderNavBarWhenScroll)) && mainRouter.currentRoute.value.name !== 'index' }]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
@ -112,13 +112,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll"/>
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/>
<button v-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || (appearNote.files.length > 0 && defaultStore.state.allMediaNoteCollapse)) && collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.collapsed" class="_button" @click="collapsed = false">
<button v-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || (appearNote.files.length > 0 && defaultStore.state.allMediaNoteCollapse)) && collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.collapsed" class="_button" @click.stop="collapsed = false">
<span :class="$style.collapsedLabel">
{{ i18n.ts.showMore }}
<span v-if="appearNote.files.length > 0" :class="$style.label">({{ collapseLabel }})</span>
</span>
</button>
<button v-else-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || (appearNote.files.length > 0 && defaultStore.state.allMediaNoteCollapse)) && !collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.showLess" class="_button" @click="collapsed = true">
<button v-else-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || (appearNote.files.length > 0 && defaultStore.state.allMediaNoteCollapse)) && !collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.showLess" class="_button" @click.stop="collapsed = true">
<span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span>
</button>
</div>
@ -132,7 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkReactionsViewer>
<footer :class="$style.footer">
<button v-if="!note.isHidden" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.reply" :class="$style.footerButton" class="_button" @click="reply()">
<button v-if="!note.isHidden" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.reply" :class="$style.footerButton" class="_button" @click.stop="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
</button>
@ -161,10 +161,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-if="appearNote.myReaction == null" v-tooltip="i18n.ts.reaction" class="ti ti-mood-plus"></i>
<i v-else v-tooltip="i18n.ts.editReaction" class="ti ti-mood-edit"></i>
</button>
<button v-if="appearNote.myReaction != null && appearNote.reactionAcceptance == 'likeOnly'" ref="reactButton" v-vibrate="defaultStore.state.vibrateSystem ? [30, 50, 50] : []" v-tooltip="i18n.ts.removeReaction" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)">
<button v-if="appearNote.myReaction != null && appearNote.reactionAcceptance == 'likeOnly'" ref="reactButton" v-vibrate="defaultStore.state.vibrateSystem ? [30, 50, 50] : []" v-tooltip="i18n.ts.removeReaction" :class="$style.footerButton" class="_button" @click.stop="undoReact(appearNote)">
<i class="ti ti-heart-minus"></i>
</button>
<button v-if="canRenote && defaultStore.state.renoteQuoteButtonSeparation" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.quote" class="_button" :class="$style.footerButton" @click="quote()">
<button v-if="canRenote && defaultStore.state.renoteQuoteButtonSeparation" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.quote" class="_button" :class="$style.footerButton" @click.stop="quote()">
<i class="ti ti-quote"></i>
</button>
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.clip" :class="$style.footerButton" class="_button" @mousedown="clip()">
@ -233,7 +233,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { shouldCollapsed, shouldMfmCollapsed } from '@/scripts/collapsed.js';
import { globalEvents } from '@/events.js';
import { mainRouter } from '@/router.js';
import { mainRouter, useRouter } from '@/router.js';
import { notePage } from '@/filters/note.js';
import { miLocalStorage } from '@/local-storage.js';
import { instance } from '@/instance.js';
@ -315,6 +315,8 @@ const translating = ref(false);
const viewTextSource = ref(false);
const noNyaize = ref(false);
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
const router = useRouter();
let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
const collapseLabel = computed(() => {
@ -389,6 +391,11 @@ if (!props.mock) {
});
}
function noteClick(ev: MouseEvent) {
if (document.getSelection().type === 'Range' || !expandOnNoteClick) ev.stopPropagation();
else router.push(notePage(appearNote));
}
function renote(viaKeyboard = false) {
pleaseLogin();
showMovedDialog();

View file

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.main">
<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
<MkAvatar v-if="!defaultStore.state.hideAvatarsInNote" :class="$style.avatar" :user="note.user" link preview/>
<div :class="$style.body">
<div :class="$style.body" :style="{ cursor: expandOnNoteClick ? 'pointer' : '' }" @click="noteClick">
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
<div>
<MkEvent v-if="note.event" :note="note"/>
@ -55,6 +55,7 @@ import { $i } from '@/account.js';
import { userPage } from '@/filters/user.js';
import { checkWordMute } from '@/scripts/check-word-mute.js';
import { defaultStore } from '@/store.js';
import { useRouter } from '@/router.js';
let hideLine = $ref(false);
@ -70,6 +71,9 @@ const props = withDefaults(defineProps<{
const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
const router = useRouter();
let showContent = $ref(false);
let replies: Misskey.entities.Note[] = $ref([]);
@ -82,6 +86,11 @@ if (props.detail) {
hideLine = true;
});
}
function noteClick(ev: MouseEvent) {
if (document.getSelection().type === 'Range' || !expandOnNoteClick) ev.stopPropagation();
else router.push(notePage(props.note));
}
</script>
<style lang="scss" module>

View file

@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-vibrate="defaultStore.state.vibrateSystem ? [10, 30, 40] : []"
class="_button"
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: (canToggle || alternative), [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
@click="toggleReaction()"
@click.stop="toggleReaction()"
>
<MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
<span :class="$style.count">{{ count }}</span>

View file

@ -50,13 +50,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
<button v-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || note.files.length > 0 || note.poll) && collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.fade" class="_button" @click="collapsed = false;">
<button v-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || note.files.length > 0 || note.poll) && collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.fade" class="_button" @click.stop="collapsed = false;">
<span :class="$style.fadeLabel">
{{ i18n.ts.showMore }}
<span v-if="note.files.length > 0" :class="$style.label">({{ collapseLabel }})</span>
</span>
</button>
<button v-else-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || note.files.length > 0 || note.poll) && !collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.showLess" class="_button" @click="collapsed = true;">
<button v-else-if="(isLong || (isMFM && defaultStore.state.collapseDefault) || note.files.length > 0 || note.poll) && !collapsed" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :class="$style.showLess" class="_button" @click.stop="collapsed = true;">
<span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span>
</button>
<div v-if="showSubNoteFooterButton">
@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</MkReactionsViewer>
<footer :class="$style.footer">
<button v-if="!note.isHidden" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.reply" :class="$style.footerButton" class="_button" @click="reply()">
<button v-if="!note.isHidden" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.reply" :class="$style.footerButton" class="_button" @click.stop="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="note.repliesCount > 0" :class="$style.footerButtonCount">{{ note.repliesCount }}</p>
</button>
@ -95,10 +95,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-if="note.myReaction == null" v-tooltip="i18n.ts.reaction" class="ti ti-mood-plus"></i>
<i v-else v-tooltip="i18n.ts.editReaction" class="ti ti-mood-edit"></i>
</button>
<button v-if="note.myReaction != null && note.reactionAcceptance == 'likeOnly'" ref="reactButton" v-vibrate="defaultStore.state.vibrateSystem ? [30, 50, 50] : []" v-tooltip="i18n.ts.removeReaction" :class="$style.footerButton" class="_button" @click="undoReact(note)">
<button v-if="note.myReaction != null && note.reactionAcceptance == 'likeOnly'" ref="reactButton" v-vibrate="defaultStore.state.vibrateSystem ? [30, 50, 50] : []" v-tooltip="i18n.ts.removeReaction" :class="$style.footerButton" class="_button" @click.stop="undoReact(note)">
<i class="ti ti-heart-minus"></i>
</button>
<button v-if="canRenote && defaultStore.state.renoteQuoteButtonSeparation" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.quote" class="_button" :class="$style.footerButton" @click="quote()">
<button v-if="canRenote && defaultStore.state.renoteQuoteButtonSeparation" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.quote" class="_button" :class="$style.footerButton" @click.stop="quote()">
<i class="ti ti-quote"></i>
</button>
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" v-tooltip="i18n.ts.clip" :class="$style.footerButton" class="_button" @mousedown="clip()">

View file

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<a v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :href="to" :class="active ? activeClass : null" @click.prevent="nav" @contextmenu.prevent.stop="onContextmenu">
<a v-vibrate="defaultStore.state.vibrateSystem ? 5 : []" :href="to" :class="active ? activeClass : null" @click.prevent.stop="nav" @contextmenu.prevent.stop="onContextmenu">
<slot></slot>
</a>
</template>

View file

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick">
<component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click.stop="onClick">
<MkImgWithBlurhash
:class="$style.inner"
:src="url"

View file

@ -28,6 +28,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts._cherrypick.showRenoteConfirmPopup }}</template>
<template #caption>{{ i18n.ts._cherrypick.showRenoteConfirmPopupDescription }}</template>
</MkSwitch>
<MkSwitch v-model="expandOnNoteClick">
<template #label>{{ i18n.ts._cherrypick.expandOnNoteClick }}</template>
<template #caption>{{ i18n.ts._cherrypick.expandOnNoteClickDescription }}</template>
</MkSwitch>
</div>
<div>
@ -96,6 +100,7 @@ const nicknameEnabled = computed(defaultStore.makeGetterSetter('nicknameEnabled'
const useEnterToSend = computed(defaultStore.makeGetterSetter('useEnterToSend'));
const postFormVisibilityHotkey = computed(defaultStore.makeGetterSetter('postFormVisibilityHotkey'));
const showRenoteConfirmPopup = computed(defaultStore.makeGetterSetter('showRenoteConfirmPopup'));
const expandOnNoteClick = computed(defaultStore.makeGetterSetter('expandOnNoteClick'));
const displayHeaderNavBarWhenScroll = computed(defaultStore.makeGetterSetter('displayHeaderNavBarWhenScroll'));
const reactableRemoteReactionEnabled = computed(defaultStore.makeGetterSetter('reactableRemoteReactionEnabled'));
const showFollowingMessageInsteadOfButtonEnabled = computed(defaultStore.makeGetterSetter('showFollowingMessageInsteadOfButtonEnabled'));
@ -113,6 +118,7 @@ watch([
});
watch([
expandOnNoteClick,
reactableRemoteReactionEnabled,
mobileHeaderChange,
renameTheButtonInPostFormToNya,

View file

@ -618,6 +618,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: true,
},
expandOnNoteClick: {
where: 'device',
default: true,
},
displayHeaderNavBarWhenScroll: {
where: 'device',
default: 'hideHeaderFloatBtn' as 'all' | 'hideHeaderOnly' | 'hideHeaderFloatBtn' | 'hideFloatBtnOnly' | 'hideFloatBtnNavBar' | 'hide',