diff --git a/CHANGELOG.md b/CHANGELOG.md index eea57a30f5..34a2f88a9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,10 @@ - ローカリゼーションの更新 ### Client +- ノート詳細ページを改修 + - 読み込み時のパフォーマンスが向上しました + - リノート一覧、リアクション一覧がタブとして追加されました + - ノートのメニューからは当該項目は消えました - プロフィールにその人が作ったPlayの一覧出せるように - メニューのスイッチの動作を改善 - 絵文字ピッカーの検索の表示件数を100件に増加 @@ -48,7 +52,6 @@ - `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました - Playの操作を行うAPI TokenをAPIコンソールから発行できるように - リアクションの表示サイズをより大きくできるように -- ノート詳細ページ読み込み時のパフォーマンスを改善 - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 - 「Moderation note」、「Add moderation note」をローカライズできるように - 新しい実績を追加 diff --git a/locales/index.d.ts b/locales/index.d.ts index 94d9657ac8..ac714258e2 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1110,6 +1110,10 @@ export interface Locale { "pastAnnouncements": string; "youHaveUnreadAnnouncements": string; "useSecurityKey": string; + "replies": string; + "renotes": string; + "loadReplies": string; + "loadConversation": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 743814d339..d97b09f63c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -15,7 +15,7 @@ gotIt: "わかった" cancel: "キャンセル" noThankYou: "やめておく" enterUsername: "ユーザー名を入力" -renotedBy: "{user}がRenote" +renotedBy: "{user}がリノート" noNotes: "ノートはありません" noNotifications: "通知はありません" instance: "サーバー" @@ -45,10 +45,10 @@ pin: "ピン留め" unpin: "ピン留め解除" copyContent: "内容をコピー" copyLink: "リンクをコピー" -copyLinkRenote: "Renoteのリンクをコピー" +copyLinkRenote: "リノートのリンクをコピー" delete: "削除" deleteAndEdit: "削除して編集" -deleteAndEditConfirm: "このノートを削除してもう一度編集しますか?このノートへのリアクション、Renote、返信も全て削除されます。" +deleteAndEditConfirm: "このノートを削除してもう一度編集しますか?このノートへのリアクション、リノート、返信も全て削除されます。" addToList: "リストに追加" addToAntenna: "アンテナに追加" sendMessage: "メッセージを送信" @@ -105,13 +105,13 @@ followRequests: "フォロー申請" unfollow: "フォロー解除" followRequestPending: "フォロー許可待ち" enterEmoji: "絵文字を入力" -renote: "Renote" -unrenote: "Renote解除" -renoted: "Renoteしました。" -cantRenote: "この投稿はRenoteできません。" -cantReRenote: "RenoteをRenoteすることはできません。" +renote: "リノート" +unrenote: "リノート解除" +renoted: "リノートしました。" +cantRenote: "この投稿はリノートできません。" +cantReRenote: "リノートをリノートすることはできません。" quote: "引用" -inChannelRenote: "チャンネル内Renote" +inChannelRenote: "チャンネル内リノート" inChannelQuote: "チャンネル内引用" pinnedNote: "ピン留めされたノート" pinned: "ピン留め" @@ -657,7 +657,7 @@ behavior: "動作" sample: "サンプル" abuseReports: "通報" reportAbuse: "通報" -reportAbuseRenote: "Renoteを通報" +reportAbuseRenote: "リノートを通報" reportAbuseOf: "{name}を通報する" fillAbuseReportDescription: "通報理由の詳細を記入してください。対象のノートがある場合はそのURLも記入してください。" abuseReported: "内容が送信されました。ご報告ありがとうございました。" @@ -691,9 +691,9 @@ manageAccessTokens: "アクセストークンの管理" accountInfo: "アカウント情報" notesCount: "ノートの数" repliesCount: "返信した数" -renotesCount: "Renoteした数" +renotesCount: "リノートした数" repliedCount: "返信された数" -renotedCount: "Renoteされた数" +renotedCount: "リノートされた数" followingCount: "フォロー数" followersCount: "フォロワー数" sentReactionsCount: "リアクションした数" @@ -989,7 +989,7 @@ thisPostMayBeAnnoying: "この投稿は迷惑になる可能性があります thisPostMayBeAnnoyingHome: "ホームに投稿" thisPostMayBeAnnoyingCancel: "やめる" thisPostMayBeAnnoyingIgnore: "このまま投稿" -collapseRenotes: "見たことのあるRenoteを省略して表示" +collapseRenotes: "見たことのあるリノートを省略して表示" internalServerError: "サーバー内部エラー" internalServerErrorDescription: "サーバー内部で予期しないエラーが発生しました。" copyErrorInfo: "エラー情報をコピー" @@ -1037,7 +1037,7 @@ forceShowAds: "常に広告を表示する" addMemo: "メモを追加" editMemo: "メモを編集" reactionsList: "リアクション一覧" -renotesList: "Renote一覧" +renotesList: "リノート一覧" notificationDisplay: "通知の表示" leftTop: "左上" rightTop: "右上" @@ -1107,6 +1107,10 @@ currentAnnouncements: "現在のお知らせ" pastAnnouncements: "過去のお知らせ" youHaveUnreadAnnouncements: "未読のお知らせがあります。" useSecurityKey: "ブラウザまたはデバイスの指示に従って、セキュリティキーまたはパスキーを使用してください。" +replies: "返信" +renotes: "リノート" +loadReplies: "返信を見る" +loadConversation: "会話を見る" _announcement: forExistingUsers: "既存ユーザーのみ" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index bedacbce2a..fdf22c5995 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -86,9 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only - +
+ + + +
+
+
+
+ {{ i18n.ts.loadReplies }} +
+ +
+
+ + + +
+
+
+ +
+ + + +
+
@@ -169,6 +210,10 @@ import { claimAchievement } from '@/scripts/achievements'; import { MenuItem } from '@/types/menu'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog'; +import MkUserCardMini from '@/components/MkUserCardMini.vue'; +import MkPagination, { Paging } from '@/components/MkPagination.vue'; +import MkReactionIcon from '@/components/MkReactionIcon.vue'; +import MkButton from '@/components/MkButton.vue'; const props = defineProps<{ note: Misskey.entities.Note; @@ -224,6 +269,26 @@ const keymap = { 's': () => showContent.value !== showContent.value, }; +let tab = $ref('replies'); +let reactionTabType = $ref(null); + +const renotesPagination = $computed(() => ({ + endpoint: 'notes/renotes', + limit: 10, + params: { + noteId: appearNote.id, + }, +})); + +const reactionsPagination = $computed(() => ({ + endpoint: 'notes/reactions', + limit: 10, + params: { + noteId: appearNote.id, + type: reactionTabType, + }, +})); + useNoteCapture({ rootEl: el, note: $$(appearNote), @@ -426,14 +491,20 @@ function blur() { el.value.blur(); } -os.api('notes/children', { - noteId: appearNote.id, - limit: 30, -}).then(res => { - replies.value = res; -}); +const repliesLoaded = ref(false); +function loadReplies() { + repliesLoaded.value = true; + os.api('notes/children', { + noteId: appearNote.id, + limit: 30, + }).then(res => { + replies.value = res; + }); +} -if (appearNote.replyId) { +const conversationLoaded = ref(false); +function loadConversation() { + conversationLoaded.value = true; os.api('notes/conversation', { noteId: appearNote.replyId, }).then(res => { @@ -640,10 +711,52 @@ if (appearNote.replyId) { } } -.reply { +.reply:not(:first-child) { border-top: solid 0.5px var(--divider); } +.tabs { + border-top: solid 0.5px var(--divider); + border-bottom: solid 0.5px var(--divider); + display: flex; +} + +.tab { + flex: 1; + padding: 12px 8px; + border-top: solid 2px transparent; + border-bottom: solid 2px transparent; +} + +.tabActive { + border-bottom: solid 2px var(--accent); +} + +.tab_renotes { + padding: 16px; +} + +.tab_reactions { + padding: 16px; +} + +.reactionTabs { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-bottom: 8px; +} + +.reactionTab { + padding: 4px 6px; + border: solid 1px var(--divider); + border-radius: 6px; +} + +.reactionTabActive { + border-color: var(--accent); +} + @container (max-width: 500px) { .root { font-size: 0.9em; diff --git a/packages/frontend/src/components/MkReactedUsersDialog.vue b/packages/frontend/src/components/MkReactedUsersDialog.vue deleted file mode 100644 index b5f3a634a3..0000000000 --- a/packages/frontend/src/components/MkReactedUsersDialog.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/components/MkRenotedUsersDialog.vue b/packages/frontend/src/components/MkRenotedUsersDialog.vue deleted file mode 100644 index 5e6784bb9c..0000000000 --- a/packages/frontend/src/components/MkRenotedUsersDialog.vue +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 5bda993fff..d9fae946d3 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -238,18 +238,6 @@ export function getNoteMenu(props: { os.pageWindow(`/notes/${appearNote.id}`); } - function showReactions(): void { - os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), { - noteId: appearNote.id, - }, {}, 'closed'); - } - - function showRenotes(): void { - os.popup(defineAsyncComponent(() => import('@/components/MkRenotedUsersDialog.vue')), { - noteId: appearNote.id, - }, {}, 'closed'); - } - async function translate(): Promise { if (props.translation.value != null) return; props.translating.value = true; @@ -279,14 +267,6 @@ export function getNoteMenu(props: { icon: 'ti ti-info-circle', text: i18n.ts.details, action: openDetail, - }, { - icon: 'ti ti-repeat', - text: i18n.ts.renotesList, - action: showRenotes, - }, { - icon: 'ti ti-icons', - text: i18n.ts.reactionsList, - action: showReactions, }, { icon: 'ti ti-copy', text: i18n.ts.copyContent,