diff --git a/locales/en-US.yml b/locales/en-US.yml index 5df1a3f8fe..67980552de 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1058,6 +1058,7 @@ thisPostIsMissingAltTextCancel: "Cancel" thisPostIsMissingAltTextIgnore: "Post anyway" thisPostIsMissingAltText: "One of the files attached to this post is missing alt text. Please ensure all the attachments have alt text." collapseRenotes: "Collapse boosts you've already seen" +collapseReplies: "Collapse replies" collapseFiles: "Collapse files" autoloadConversation: "Load conversation on replies" internalServerError: "Internal Server Error" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index a14854fba6..068a99a59e 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -12,7 +12,11 @@ SPDX-License-Identifier: AGPL-3.0-only :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" > - +
+ + +
+
{{ i18n.ts.pinnedNote }}
@@ -310,6 +314,7 @@ const renoteCollapsed = ref( (appearNote.value.myReaction != null) ) ); +const replyCollapsed = ref(defaultStore.state.collapseReplies && !renoteCollapsed.value); const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null); const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); @@ -919,7 +924,7 @@ function emitUpdReaction(emoji: string, delta: number) { margin-right: 4px; } -.collapsedRenoteTarget { +.collapsedRenoteTarget, .collapsedReply { display: flex; align-items: center; line-height: 28px; @@ -927,7 +932,12 @@ function emitUpdReaction(emoji: string, delta: number) { padding: 0 32px 18px; } -.collapsedRenoteTargetAvatar { +.collapsedReply { + padding: 28px 32px 0; + opacity: 0.7; +} + +.collapsedRenoteTargetAvatar, .collapsedReplyAvatar { flex-shrink: 0; display: inline-block; width: 28px; @@ -936,12 +946,15 @@ function emitUpdReaction(emoji: string, delta: number) { } .collapsedRenoteTargetText { + opacity: 0.7; +} + +.collapsedRenoteTargetText, .collapsedReplyText { overflow: hidden; flex-shrink: 1; text-overflow: ellipsis; white-space: nowrap; font-size: 90%; - opacity: 0.7; cursor: pointer; &:hover { @@ -1154,6 +1167,10 @@ function emitUpdReaction(emoji: string, delta: number) { margin-top: 4px; } + .collapsedReply { + padding: 28px 16px 0; + } + .article { padding: 14px 16px; } diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index 3c5a1baffc..553c38eed8 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -12,7 +12,12 @@ SPDX-License-Identifier: AGPL-3.0-only :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" > - + +
+
+ + +
{{ i18n.ts.pinnedNote }}
@@ -309,6 +314,7 @@ const renoteCollapsed = ref( (appearNote.value.myReaction != null) ) ); +const replyCollapsed = ref(defaultStore.state.collapseReplies && !renoteCollapsed.value); const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null); const animated = computed(() => parsed.value ? checkAnimationFromMfm(parsed.value) : null); const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false); @@ -934,7 +940,7 @@ function emitUpdReaction(emoji: string, delta: number) { margin-right: 4px; } -.collapsedRenoteTarget { +.collapsedRenoteTarget, .collapsedReply { display: flex; align-items: center; line-height: 28px; @@ -942,7 +948,11 @@ function emitUpdReaction(emoji: string, delta: number) { padding: 8px 38px 24px; } -.collapsedRenoteTargetAvatar { +.collapsedReply { + padding: 28px 44px 0; +} + +.collapsedRenoteTargetAvatar, .collapsedReplyAvatar { flex-shrink: 0; display: inline-block; width: 28px; @@ -950,7 +960,7 @@ function emitUpdReaction(emoji: string, delta: number) { margin: 0 8px 0 0; } -.collapsedRenoteTargetText { +.collapsedRenoteTargetText, .collapsedReplyText { overflow: hidden; flex-shrink: 1; text-overflow: ellipsis; @@ -964,6 +974,15 @@ function emitUpdReaction(emoji: string, delta: number) { } } +.collapsedReplyLine { + position: absolute; + left: 56px; + // using solid instead of dotted, stylelistic choice + border-left: var(--thread-width) solid var(--thread); + top: calc(28px + 28px); // 28px of .root padding, plus 28px of avatar height (see SkNote) + height: 28px; +} + .article { position: relative; padding: 28px 32px; @@ -1142,6 +1161,14 @@ function emitUpdReaction(emoji: string, delta: number) { padding: 8px 26px 24px; } + .collapsedReply { + padding: 28px 35px 0; + } + + .collapsedReplyLine { + left: 47px; + } + .article { padding: 24px 26px; } @@ -1189,6 +1216,14 @@ function emitUpdReaction(emoji: string, delta: number) { margin-top: 4px; } + .collapsedReply { + padding: 28px 33px 0; + } + + .collapsedReplyLine { + left: 45px; + } + .article { padding: 22px 24px; } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 76081666d8..0f428b6d30 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -53,6 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.showNoteActionsOnlyHover }} {{ i18n.ts.showClipButtonInNoteFooter }} {{ i18n.ts.collapseRenotes }} + {{ i18n.ts.collapseReplies }} {{ i18n.ts.collapseFiles }} Uncollapse CWs on notes {{ i18n.ts.autoloadConversation }} @@ -321,6 +322,7 @@ const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showC const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); +const collapseReplies = computed(defaultStore.makeGetterSetter('collapseReplies')); const clickToOpen = computed(defaultStore.makeGetterSetter('clickToOpen')); // copied from src/pages/timeline.vue const showBots = computed({ diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue index d4349b466d..514a37a3ff 100644 --- a/packages/frontend/src/pages/settings/preferences-backups.vue +++ b/packages/frontend/src/pages/settings/preferences-backups.vue @@ -56,6 +56,7 @@ const { t, ts } = i18n; const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [ 'collapseRenotes', + 'collapseReplies', 'menu', 'visibility', 'localOnly', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 7f6377613e..ba77f96dd5 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -89,6 +89,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: false, }, + collapseReplies: { + where: 'account', + default: false, + }, collapseFiles: { where: 'account', default: false,