Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance(frontend): リプライ・リノート・リアクションの総数を表示するか設定で選べるようにするなど #13538

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Enhance: 自分のノートの添付ファイルから直接ファイルの詳細ページに飛べるように
- Enhance: リアクション・いいねの総数を表示するように
- Enhance: リアクション受け入れが「いいねのみ」の場合はリアクション絵文字一覧を表示しないように
- Enhance: 「ノートの表示」の設定項目の一部がリアルタイムでプレビューできるように
- Fix: 一部のページ内リンクが正しく動作しない問題を修正

### Server
Expand Down
12 changes: 12 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,18 @@ export interface Locale extends ILocale {
* ノートのアクションをホバー時のみ表示する
*/
"showNoteActionsOnlyHover": string;
/**
* ノートの返信数を表示する
*/
"showRepliesCount": string;
/**
* ノートのリノート数を表示する
*/
"showRenotesCount": string;
/**
* ノートのリアクション数を表示する
*/
"showReactionsCount": string;
/**
* 履歴はありません
*/
Expand Down
3 changes: 3 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ emojiStyle: "絵文字のスタイル"
native: "ネイティブ"
disableDrawer: "メニューをドロワーで表示しない"
showNoteActionsOnlyHover: "ノートのアクションをホバー時のみ表示する"
showRepliesCount: "ノートの返信数を表示する"
showRenotesCount: "ノートのリノート数を表示する"
showReactionsCount: "ノートのリアクション数を表示する"
noHistory: "履歴はありません"
signinHistory: "ログイン履歴"
enableAdvancedMfm: "高度なMFMを有効にする"
Expand Down
Binary file not shown.
18 changes: 9 additions & 9 deletions packages/frontend/src/components/MkNote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-show="!isDeleted"
ref="rootEl"
v-hotkey="keymap"
:class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]"
:class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.reactiveState.showNoteActionsOnlyHover.value }]"
:tabindex="!isDeleted ? '-1' : undefined"
>
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
Expand Down Expand Up @@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<footer :class="$style.footer">
<button :class="$style.footerButton" class="_button" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
<p v-if="defaultStore.reactiveState.showRepliesCount.value && appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p>
</button>
<button
v-if="canRenote"
Expand All @@ -111,7 +111,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@mousedown="renote()"
>
<i class="ti ti-repeat"></i>
<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p>
<p v-if="defaultStore.reactiveState.showRenotesCount.value && appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p>
</button>
<button v-else :class="$style.footerButton" class="_button" disabled>
<i class="ti ti-ban"></i>
Expand All @@ -121,9 +121,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-plus"></i>
<p v-if="appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.reactiveState.showReactionsCount.value) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p>
</button>
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
<button v-if="defaultStore.reactiveState.showClipButtonInNoteFooter.value" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
<i class="ti ti-paperclip"></i>
</button>
<button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="showMenu()">
Expand Down Expand Up @@ -262,10 +262,10 @@ const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true));
const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
const translating = ref(false);
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
const showTicker = (defaultStore.reactiveState.instanceTicker.value === 'always') || (defaultStore.reactiveState.instanceTicker.value === 'remote' && appearNote.value.user.instance);
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id));
const renoteCollapsed = ref(
defaultStore.state.collapseRenotes && isRenote && (
defaultStore.reactiveState.collapseRenotes.value && isRenote && (
($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131
(appearNote.value.myReaction != null)
)
Expand All @@ -284,7 +284,7 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string

if (checkOnly) return false;

if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
if (inTimeline && !defaultStore.reactiveState.tl.value.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
return false;
}

Expand Down Expand Up @@ -447,7 +447,7 @@ function onContextmenu(ev: MouseEvent): void {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;

if (defaultStore.state.useReactionPickerForContextMenu) {
if (defaultStore.reactiveState.useReactionPickerForContextMenu) {
ev.preventDefault();
react();
} else {
Expand Down
12 changes: 6 additions & 6 deletions packages/frontend/src/components/MkNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" ref="reactionsViewer" :note="appearNote"/>
<button class="_button" :class="$style.noteFooterButton" @click="reply()">
<i class="ti ti-arrow-back-up"></i>
<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
<p v-if="defaultStore.reactiveState.showRepliesCount.value && appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.repliesCount) }}</p>
</button>
<button
v-if="canRenote"
Expand All @@ -119,7 +119,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@mousedown="renote()"
>
<i class="ti ti-repeat"></i>
<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
<p v-if="defaultStore.reactiveState.showRenotesCount.value && appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.renoteCount) }}</p>
</button>
<button v-else class="_button" :class="$style.noteFooterButton" disabled>
<i class="ti ti-ban"></i>
Expand All @@ -129,9 +129,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i>
<i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i>
<i v-else class="ti ti-plus"></i>
<p v-if="appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
<p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.reactiveState.showReactionsCount.value) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p>
</button>
<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
<button v-if="defaultStore.reactiveState.showClipButtonInNoteFooter.value" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
<i class="ti ti-paperclip"></i>
</button>
<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="showMenu()">
Expand Down Expand Up @@ -279,7 +279,7 @@ const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
const translating = ref(false);
const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
const urls = parsed ? extractUrlFromMfm(parsed).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null;
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
const showTicker = (defaultStore.reactiveState.instanceTicker.value === 'always') || (defaultStore.reactiveState.instanceTicker.value === 'remote' && appearNote.value.user.instance);
const conversation = ref<Misskey.entities.Note[]>([]);
const replies = ref<Misskey.entities.Note[]>([]);
const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i?.id);
Expand Down Expand Up @@ -430,7 +430,7 @@ function onContextmenu(ev: MouseEvent): void {
if (ev.target && isLink(ev.target as HTMLElement)) return;
if (window.getSelection()?.toString() !== '') return;

if (defaultStore.state.useReactionPickerForContextMenu) {
if (defaultStore.reactiveState.useReactionPickerForContextMenu.value) {
ev.preventDefault();
react();
} else {
Expand Down
12 changes: 10 additions & 2 deletions packages/frontend/src/components/MkReactionsViewer.reaction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ SPDX-License-Identifier: AGPL-3.0-only
ref="buttonEl"
v-ripple="canToggle"
class="_button"
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
:class="[
$style.root,
{
[$style.reacted]: note.myReaction == reaction,
[$style.canToggle]: canToggle,
[$style.small]: defaultStore.reactiveState.reactionsDisplaySize.value === 'small',
[$style.large]: defaultStore.reactiveState.reactionsDisplaySize.value === 'large',
}
]"
@click="toggleReaction()"
@contextmenu.prevent.stop="menu"
>
<MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
<MkReactionIcon :class="defaultStore.reactiveState.limitWidthOfReaction.value ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
<span :class="$style.count">{{ count }}</span>
</button>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
return c.match(/^[0-9a-f]{3,6}$/i) ? c : null;
};

const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm;
const useAnim = defaultStore.reactiveState.advancedMfm.value && defaultStore.reactiveState.animatedMfm.value;

/**
* Gen Vue Elements from MFM AST
Expand Down Expand Up @@ -177,17 +177,17 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
}
case 'x2': {
return h('span', {
class: defaultStore.state.advancedMfm ? 'mfm-x2' : '',
class: defaultStore.reactiveState.advancedMfm.value ? 'mfm-x2' : '',
}, genEl(token.children, scale * 2));
}
case 'x3': {
return h('span', {
class: defaultStore.state.advancedMfm ? 'mfm-x3' : '',
class: defaultStore.reactiveState.advancedMfm.value ? 'mfm-x3' : '',
}, genEl(token.children, scale * 3));
}
case 'x4': {
return h('span', {
class: defaultStore.state.advancedMfm ? 'mfm-x4' : '',
class: defaultStore.reactiveState.advancedMfm.value ? 'mfm-x4' : '',
}, genEl(token.children, scale * 4));
}
case 'font': {
Expand Down Expand Up @@ -230,14 +230,14 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven
break;
}
case 'position': {
if (!defaultStore.state.advancedMfm) break;
if (!defaultStore.reactiveState.advancedMfm.value) break;
const x = safeParseFloat(token.props.args.x) ?? 0;
const y = safeParseFloat(token.props.args.y) ?? 0;
style = `transform: translateX(${x}em) translateY(${y}em);`;
break;
}
case 'scale': {
if (!defaultStore.state.advancedMfm) {
if (!defaultStore.reactiveState.advancedMfm.value) {
style = '';
break;
}
Expand Down
72 changes: 64 additions & 8 deletions packages/frontend/src/pages/settings/general.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.displayOfNote }}</template>

<div class="_gaps_m">
<MkNote :class="$style.exampleNoteRoot" :note="exampleNote" :mock="true"/>
<div class="_gaps_s">
<MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
<MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
<MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
<MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch>
<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
<MkSwitch v-model="showRepliesCount">{{ i18n.ts.showRepliesCount }}</MkSwitch>
<MkSwitch v-model="showRenotesCount">{{ i18n.ts.showRenotesCount }}</MkSwitch>
<MkSwitch v-model="showReactionsCount">{{ i18n.ts.showReactionsCount }}</MkSwitch>
<MkRadios v-model="reactionsDisplaySize">
<template #label>{{ i18n.ts.reactionsDisplaySize }}</template>
<option value="small">{{ i18n.ts.small }}</option>
Expand All @@ -67,6 +68,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="limitWidthOfReaction">{{ i18n.ts.limitWidthOfReaction }}</MkSwitch>
</div>

<div class="_gaps_s">
<MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
</div>

<MkSelect v-model="instanceTicker">
<template #label>{{ i18n.ts.instanceTicker }}</template>
<option value="none">{{ i18n.ts._instanceTicker.none }}</option>
Expand Down Expand Up @@ -229,7 +236,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>

<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { computed, ref, reactive, watch } from 'vue';
import * as Misskey from 'misskey-js';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
Expand All @@ -241,7 +248,8 @@ import FormSection from '@/components/form/section.vue';
import FormLink from '@/components/form/link.vue';
import MkLink from '@/components/MkLink.vue';
import MkInfo from '@/components/MkInfo.vue';
import { langs } from '@/config.js';
import MkNote from '@/components/MkNote.vue';
import { url as local, langs } from '@/config.js';
import { defaultStore } from '@/store.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
Expand Down Expand Up @@ -281,6 +289,9 @@ const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
const animatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm'));
const advancedMfm = computed(defaultStore.makeGetterSetter('advancedMfm'));
const showRepliesCount = computed(defaultStore.makeGetterSetter('showRepliesCount'));
const showRenotesCount = computed(defaultStore.makeGetterSetter('showRenotesCount'));
const showReactionsCount = computed(defaultStore.makeGetterSetter('showReactionsCount'));
const enableQuickAddMfmFunction = computed(defaultStore.makeGetterSetter('enableQuickAddMfmFunction'));
const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle'));
const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
Expand Down Expand Up @@ -336,13 +347,10 @@ watch([
useSystemFont,
enableInfiniteScroll,
squareAvatars,
showNoteActionsOnlyHover,
showGapBetweenNotesInTimeline,
instanceTicker,
overridedDeviceKind,
mediaListWithOneImageAppearance,
reactionsDisplaySize,
limitWidthOfReaction,
highlightSensitiveMedia,
keepScreenOn,
disableStreamingTimeline,
Expand Down Expand Up @@ -461,6 +469,46 @@ watch(dataSaver, (to) => {
deep: true,
});

const exampleNote = reactive<Misskey.entities.Note>({
id: '0000000000',
createdAt: '2019-04-14T17:30:49.181Z',
userId: '0000000001',
user: {
id: '0000000001',
name: '藍',
username: 'ai',
host: null,
avatarDecorations: [],
avatarUrl: '/client-assets/tutorial/ai.webp',
avatarBlurhash: 'eiKmhHIByXxZ~qWXs:-pR*NbR*s:xuRjoL-oR*WCt6WWf6WVf6oeWB',
isBot: false,
isCat: true,
emojis: {},
onlineStatus: 'unknown',
badgeRoles: [],
},
text: 'You can change $[position.y=1 **position**], and you can even make it $[shake animated!]\n_',
cw: null,
visibility: 'public',
localOnly: false,
reactionAcceptance: null,
renoteCount: 1,
repliesCount: 1,
reactionCount: 30,
reactions: {
'👍': 25,
'👀': 4,
':[email protected]:': 1,
},
reactionEmojis: {
'[email protected]': `${local}/client-assets/tutorial/misskey2022.webp`,
},
fileIds: [],
files: [],
replyId: null,
renoteId: null,
});

const headerActions = computed(() => []);

const headerTabs = computed(() => []);
Expand All @@ -470,3 +518,11 @@ definePageMetadata(() => ({
icon: 'ti ti-adjustments',
}));
</script>

<style lang="scss" module>
.exampleNoteRoot {
border-radius: var(--radius);
border: var(--panelBorder);
background: var(--panel);
}
</style>
3 changes: 3 additions & 0 deletions packages/frontend/src/pages/settings/preferences-backups.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
'animation',
'animatedMfm',
'advancedMfm',
'showRepliesCount',
'showRenotesCount',
'showReactionsCount',
'loadRawImages',
'imageNewTab',
'dataSaver',
Expand Down
Loading
Loading