diff --git a/locales/en-US.yml b/locales/en-US.yml index 97abb02fe2..124ebcec60 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1062,6 +1062,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" +collapseNotesRepliedTo: "Collapse notes replied to" 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 4d27770138..7e6e6707b8 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -12,7 +12,14 @@ SPDX-License-Identifier: AGPL-3.0-only :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" > - +
+ + + + : + +
+
{{ i18n.ts.pinnedNote }}
@@ -44,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
@@ -310,6 +317,7 @@ const renoteCollapsed = ref( (appearNote.value.myReaction != null) ) ); +const inReplyToCollapsed = ref(defaultStore.state.collapseNotesRepliedTo); 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); @@ -923,7 +931,7 @@ function emitUpdReaction(emoji: string, delta: number) { margin-right: 4px; } -.collapsedRenoteTarget { +.collapsedRenoteTarget, .collapsedInReplyTo { display: flex; align-items: center; line-height: 28px; @@ -931,7 +939,12 @@ function emitUpdReaction(emoji: string, delta: number) { padding: 0 32px 18px; } -.collapsedRenoteTargetAvatar { +.collapsedInReplyTo { + padding: 28px 32px 0; + opacity: 0.7; +} + +.collapsedRenoteTargetAvatar, .collapsedInReplyToAvatar { flex-shrink: 0; display: inline-block; width: 28px; @@ -940,12 +953,15 @@ function emitUpdReaction(emoji: string, delta: number) { } .collapsedRenoteTargetText { + opacity: 0.7; +} + +.collapsedRenoteTargetText, .collapsedInReplyToText { overflow: hidden; flex-shrink: 1; text-overflow: ellipsis; white-space: nowrap; font-size: 90%; - opacity: 0.7; cursor: pointer; &:hover { @@ -1158,6 +1174,10 @@ function emitUpdReaction(emoji: string, delta: number) { margin-top: 4px; } + .collapsedInReplyTo { + padding: 14px 16px 0; + } + .article { padding: 14px 16px; } diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index cbdaa757e2..8f0a5a8f11 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -12,7 +12,15 @@ SPDX-License-Identifier: AGPL-3.0-only :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" :tabindex="!isDeleted ? '-1' : undefined" > - + +
+
+ + + + : + +
{{ i18n.ts.pinnedNote }}
@@ -44,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
@@ -309,6 +317,7 @@ const renoteCollapsed = ref( (appearNote.value.myReaction != null) ) ); +const inReplyToCollapsed = ref(defaultStore.state.collapseNotesRepliedTo); 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); @@ -938,7 +947,7 @@ function emitUpdReaction(emoji: string, delta: number) { margin-right: 4px; } -.collapsedRenoteTarget { +.collapsedRenoteTarget, .collapsedInReplyTo { display: flex; align-items: center; line-height: 28px; @@ -946,7 +955,11 @@ function emitUpdReaction(emoji: string, delta: number) { padding: 8px 38px 24px; } -.collapsedRenoteTargetAvatar { +.collapsedInReplyTo { + padding: 28px 44px 0; +} + +.collapsedRenoteTargetAvatar, .collapsedInReplyToAvatar { flex-shrink: 0; display: inline-block; width: 28px; @@ -954,7 +967,7 @@ function emitUpdReaction(emoji: string, delta: number) { margin: 0 8px 0 0; } -.collapsedRenoteTargetText { +.collapsedRenoteTargetText, .collapsedInReplyToText { overflow: hidden; flex-shrink: 1; text-overflow: ellipsis; @@ -968,6 +981,15 @@ function emitUpdReaction(emoji: string, delta: number) { } } +.collapsedInReplyToLine { + position: absolute; + left: calc(32px + .5 * var(--avatar)); + // 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; @@ -1146,6 +1168,14 @@ function emitUpdReaction(emoji: string, delta: number) { padding: 8px 26px 24px; } + .collapsedInReplyTo { + padding: 28px 35px 0; + } + + .collapsedInReplyToLine { + left: calc(26px + .5 * var(--avatar)); + } + .article { padding: 24px 26px; } @@ -1163,6 +1193,10 @@ function emitUpdReaction(emoji: string, delta: number) { .footer { margin-bottom: -8px; } + + .collapsedInReplyToLine { + left: calc(25px + .5 * var(--avatar)); + } } @container (max-width: 500px) { @@ -1193,6 +1227,15 @@ function emitUpdReaction(emoji: string, delta: number) { margin-top: 4px; } + .collapsedInReplyTo { + padding: 22px 33px 0; + } + + .collapsedInReplyToLine { + left: calc(24px + .5 * var(--avatar)); + top: calc(22px + 28px); // 22px of .root padding, plus 28px of avatar height + } + .article { padding: 22px 24px; } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 06f1f75ef0..f0185c8dcd 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.collapseNotesRepliedTo }} {{ i18n.ts.collapseFiles }} Uncollapse CWs on notes {{ i18n.ts.autoloadConversation }} @@ -345,6 +346,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 collapseNotesRepliedTo = computed(defaultStore.makeGetterSetter('collapseNotesRepliedTo')); 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..eeedcca3c8 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', + 'collapseNotesRepliedTo', 'menu', 'visibility', 'localOnly', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 0a4d853ca5..f78160e7ba 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -90,6 +90,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: false, }, + collapseNotesRepliedTo: { + where: 'account', + default: false, + }, collapseFiles: { where: 'account', default: false,