From 3dd2570c2d3d1aa8b1eef991fd93259992018f32 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:17:05 +0300 Subject: [PATCH 01/29] cherry-pick this into 2.11.3 fixes --- src/components/moderation_tools/moderation_tools.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js index ca4852ab3..ba13afc8b 100644 --- a/src/components/moderation_tools/moderation_tools.js +++ b/src/components/moderation_tools/moderation_tools.js @@ -405,7 +405,7 @@ const ModerationTools = { ) }, isAdmin() { - this.$store.state.users.currentUser.role === 'admin' + return this.$store.state.users.currentUser.role === 'admin' }, }, methods: { From ab2a011183f6c4808acad62f7ed65ba37ebd076c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:22:33 +0300 Subject: [PATCH 02/29] also cherry-pick? --- .../notifications_fetcher/notifications_fetcher.service.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index 8530c468c..0e0bc0277 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -66,7 +66,8 @@ const fetchAndUpdate = ({ store, credentials, older = false, sinceId }) => { const unreadNotifsIds = notifications .filter((n) => !n.seen) .map((n) => n.id) - if (readNotifsIds.length > 0 && readNotifsIds.length > 0) { + + if (readNotifsIds.length > 0 && unreadNotifsIds.length > 0) { const minId = Math.min(...unreadNotifsIds) // Oldest known unread notification if (minId !== Infinity) { args.sinceId = null // Don't use since_id since it sorta conflicts with min_id From 4c8b5fe68b911621b4dde49de2041147c8a9da33 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 4 Aug 2026 17:32:02 +0300 Subject: [PATCH 03/29] fix domain unmuting --- changelog.d/fix-domain-mute.fix | 1 + changelog.d/moderation-tools.fix | 1 + src/modules/users.js | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 changelog.d/fix-domain-mute.fix create mode 100644 changelog.d/moderation-tools.fix diff --git a/changelog.d/fix-domain-mute.fix b/changelog.d/fix-domain-mute.fix new file mode 100644 index 000000000..1cb910d22 --- /dev/null +++ b/changelog.d/fix-domain-mute.fix @@ -0,0 +1 @@ +domain mute fixedw diff --git a/changelog.d/moderation-tools.fix b/changelog.d/moderation-tools.fix new file mode 100644 index 000000000..1417048a5 --- /dev/null +++ b/changelog.d/moderation-tools.fix @@ -0,0 +1 @@ +fix moderation tools button possibly not appearing for admins diff --git a/src/modules/users.js b/src/modules/users.js index b58566859..0ecee269f 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -40,6 +40,8 @@ import { muteUser as apiMuteUser, unblockUser as apiUnblockUser, unmuteUser as apiUnmuteUser, + muteDomain as apiMuteDomain, + unmuteDomain as apiUnmuteDomain, fetchBlocks, fetchDomainMutes, fetchMutes, @@ -170,14 +172,14 @@ const showReblogs = (store, userId) => { } const muteDomain = (store, domain) => { - return muteDomain({ + return apiMuteDomain({ domain, credentials: useOAuthStore().token, }).then(() => store.commit('addDomainMute', domain)) } const unmuteDomain = (store, domain) => { - return unmuteDomain({ + return apiUnmuteDomain({ domain, credentials: useOAuthStore().token, }).then(() => store.commit('removeDomainMute', domain)) From 69da111951a1df3191077f48c89e96d74bf20947 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:39:49 +0300 Subject: [PATCH 04/29] fix #3525 --- src/components/image_cropper/image_cropper.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js index c8529c8e6..c7a213a4f 100644 --- a/src/components/image_cropper/image_cropper.js +++ b/src/components/image_cropper/image_cropper.js @@ -70,6 +70,9 @@ const ImageCropper = { ) }, onCropperSelectionChange(event) { + if (!this.$refs.cropperCanvas) { + return // Cropper sends even before component is fully initialized + } const cropperCanvas = this.$refs.cropperCanvas const cropperCanvasRect = cropperCanvas.getBoundingClientRect() const selection = event.detail From 15a59100656e79ae3acc60cabca431a030d76ba4 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 18:40:53 +0300 Subject: [PATCH 05/29] changelog --- changelog.d/avatar_upload.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/avatar_upload.fix diff --git a/changelog.d/avatar_upload.fix b/changelog.d/avatar_upload.fix new file mode 100644 index 000000000..b5dd9ee56 --- /dev/null +++ b/changelog.d/avatar_upload.fix @@ -0,0 +1 @@ +fix image cropper error preventing avatar upload From 05d3fdd62ca5f90b15ec6bfb86c9c87f15b1ff84 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:13:07 +0300 Subject: [PATCH 06/29] fix third column setting change not affecting UI immideately --- src/App.js | 24 ++++++++++++------- src/components/notifications/notifications.js | 6 +++-- .../settings_modal/helpers/setting.js | 5 +++- .../settings_modal/tabs/layout_tab.js | 6 +++++ .../settings_modal/tabs/layout_tab.vue | 1 + 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/App.js b/src/App.js index 4ce6f1abc..c5b38d958 100644 --- a/src/App.js +++ b/src/App.js @@ -74,6 +74,8 @@ export default { }, data: () => ({ mobileActivePanel: 'timeline', + updateMobileState: null, + updateScrollState: null, }), provide() { return { @@ -211,22 +213,26 @@ export default { hideShoutbox() { return this.isChats || useMergedConfigStore().mergedConfig.hideShoutbox }, + thirdColumnMode() { + return this.mergedConfig.thirdColumnMode + }, + reverseSetting() { + return this.mergedConfig.sidebarRight + }, reverseLayout() { - const { thirdColumnMode, sidebarRight: reverseSetting } = - useMergedConfigStore().mergedConfig if (this.layoutType !== 'wide') { - return reverseSetting + return this.reverseSetting } else { - return thirdColumnMode === 'notifications' - ? reverseSetting - : !reverseSetting + return this.thirdColumnMode === 'notifications' + ? this.reverseSetting + : !this.reverseSetting } }, noSticky() { - return useMergedConfigStore().mergedConfig.disableStickyHeaders + return this.mergedConfig.disableStickyHeaders }, showScrollbars() { - return useMergedConfigStore().mergedConfig.showScrollbars + return this.mergedConfig.showScrollbars }, scrollParent() { return window /* this.$refs.appContentRef */ @@ -234,7 +240,7 @@ export default { showInstanceSpecificPanel() { return ( this.instanceSpecificPanelPresent && - !useMergedConfigStore().mergedConfig.hideISP + !this.mergedConfig.hideISP ) }, ...mapState(useMergedConfigStore, ['mergedConfig']), diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 393aef639..5ae6014bc 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -134,12 +134,11 @@ const Notifications = { return this.minimalMode || layoutType === 'mobile' }, teleportTarget() { - const { layoutType } = useInterfaceStore() const map = { wide: '#notifs-column', mobile: '#mobile-notifications', } - return map[layoutType] || '#notifs-sidebar' + return map[this.layoutType] || '#notifs-sidebar' }, popoversZLayer() { const { layoutType } = useInterfaceStore() @@ -162,6 +161,9 @@ const Notifications = { }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), ...mapState(useChatsStore, ['unreadChatsCount']), + ...mapState(useInterfaceStore, [ + 'layoutType', + ]), }, mounted() { this.scrollerRef = this.$refs.root.closest('.column.-scrollable') diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index c2f5279f6..6b7f33925 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -113,6 +113,7 @@ export default { localDraft: null, } }, + emits: ['update:modelValue'], created() { if ( this.realDraftMode && @@ -244,7 +245,7 @@ export default { }, configSink() { if (this.path == null) { - return (k, v) => this.$emit('update:modelValue', v) + return () => {} } switch (this.realSource) { @@ -385,11 +386,13 @@ export default { if (this.realDraftMode) { this.draft = this.getValue(e) } else { + this.$emit('update:modelValue', this.getValue(e)) this.configSink(this.path, this.getValue(e)) } }, commitDraft() { if (this.realDraftMode) { + this.$emit('update:modelValue', v) this.configSink(this.path, this.draft) } }, diff --git a/src/components/settings_modal/tabs/layout_tab.js b/src/components/settings_modal/tabs/layout_tab.js index fc56e1392..c1a3cd3b4 100644 --- a/src/components/settings_modal/tabs/layout_tab.js +++ b/src/components/settings_modal/tabs/layout_tab.js @@ -7,6 +7,7 @@ import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' +import { useInterfaceStore } from 'src/stores/interface.js' const GeneralTab = { data() { @@ -46,6 +47,11 @@ const GeneralTab = { }, ...SharedComputedObject(), }, + methods: { + updateLayout() { + useInterfaceStore().setLayoutWidth() + }, + }, } export default GeneralTab diff --git a/src/components/settings_modal/tabs/layout_tab.vue b/src/components/settings_modal/tabs/layout_tab.vue index 0035f3a11..b649572fb 100644 --- a/src/components/settings_modal/tabs/layout_tab.vue +++ b/src/components/settings_modal/tabs/layout_tab.vue @@ -105,6 +105,7 @@ id="thirdColumnMode" path="thirdColumnMode" :options="thirdColumnModeOptions" + @change="updateLayout" > {{ $t('settings.third_column_mode') }} From 5b6f55e15ed578d643f63eb370500993a00b597b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:13:58 +0300 Subject: [PATCH 07/29] changelog --- changelog.d/third_column.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/third_column.fix diff --git a/changelog.d/third_column.fix b/changelog.d/third_column.fix new file mode 100644 index 000000000..191e914d5 --- /dev/null +++ b/changelog.d/third_column.fix @@ -0,0 +1 @@ +Fixed layout selector (third column/reverse) not working immideately upon change From b4c00f4263d56f4dfe035b6e912fe486020968cf Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:15:37 +0300 Subject: [PATCH 08/29] lint --- src/App.js | 5 +---- src/components/notifications/notifications.js | 4 +--- src/components/settings_modal/helpers/setting.js | 4 ++-- src/components/settings_modal/tabs/layout_tab.js | 2 +- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/App.js b/src/App.js index c5b38d958..d7a63bd8a 100644 --- a/src/App.js +++ b/src/App.js @@ -238,10 +238,7 @@ export default { return window /* this.$refs.appContentRef */ }, showInstanceSpecificPanel() { - return ( - this.instanceSpecificPanelPresent && - !this.mergedConfig.hideISP - ) + return this.instanceSpecificPanelPresent && !this.mergedConfig.hideISP }, ...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useInterfaceStore, [ diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 5ae6014bc..feb59857e 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -161,9 +161,7 @@ const Notifications = { }, ...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']), ...mapState(useChatsStore, ['unreadChatsCount']), - ...mapState(useInterfaceStore, [ - 'layoutType', - ]), + ...mapState(useInterfaceStore, ['layoutType']), }, mounted() { this.scrollerRef = this.$refs.root.closest('.column.-scrollable') diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index 6b7f33925..92d36b207 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -245,7 +245,7 @@ export default { }, configSink() { if (this.path == null) { - return () => {} + return () => {/* no-op */} } switch (this.realSource) { @@ -392,7 +392,7 @@ export default { }, commitDraft() { if (this.realDraftMode) { - this.$emit('update:modelValue', v) + this.$emit('update:modelValue', this.draft) this.configSink(this.path, this.draft) } }, diff --git a/src/components/settings_modal/tabs/layout_tab.js b/src/components/settings_modal/tabs/layout_tab.js index c1a3cd3b4..50761fa43 100644 --- a/src/components/settings_modal/tabs/layout_tab.js +++ b/src/components/settings_modal/tabs/layout_tab.js @@ -6,8 +6,8 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import UnitSetting from '../helpers/unit_setting.vue' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useInterfaceStore } from 'src/stores/interface.js' +import { useMergedConfigStore } from 'src/stores/merged_config.js' const GeneralTab = { data() { From fbcfbb757c35ec17c954e5c2c9205976541d14e1 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:18:33 +0300 Subject: [PATCH 09/29] lint --- src/components/settings_modal/helpers/setting.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index 92d36b207..f7cd1ca91 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -245,7 +245,9 @@ export default { }, configSink() { if (this.path == null) { - return () => {/* no-op */} + return () => { + /* no-op */ + } } switch (this.realSource) { From 0fbf6704f27b62ce9210a8f03cdfe5d66ee13571 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:56:57 +0300 Subject: [PATCH 10/29] fix cjk pinned display --- src/components/status/status.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/status/status.scss b/src/components/status/status.scss index 1214d8b12..1b0e3cf3e 100644 --- a/src/components/status/status.scss +++ b/src/components/status/status.scss @@ -119,6 +119,7 @@ display: flex; flex-shrink: 0; align-self: baseline; + word-break: keep-all; .button-unstyled { padding: 0.2em; From d64daed77de1fad69ec4cdae32a85fd6bbae912e Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 5 Aug 2026 19:57:34 +0300 Subject: [PATCH 11/29] changelog --- changelog.d/cjk.fix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/cjk.fix diff --git a/changelog.d/cjk.fix b/changelog.d/cjk.fix new file mode 100644 index 000000000..861c6cb91 --- /dev/null +++ b/changelog.d/cjk.fix @@ -0,0 +1 @@ +fix pinned indicator looking wrong on CJK locales From 58fdd50838f00f1f9594079680e1e60d3572ef30 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Sat, 1 Aug 2026 23:36:31 +0300 Subject: [PATCH 12/29] fix status index approxmiation in timeline rendering for dynamically changing viewport geometries --- changelog.d/timeline_rendering_status_index_approx.fix | 1 + src/components/timeline/timeline.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelog.d/timeline_rendering_status_index_approx.fix diff --git a/changelog.d/timeline_rendering_status_index_approx.fix b/changelog.d/timeline_rendering_status_index_approx.fix new file mode 100644 index 000000000..717464c58 --- /dev/null +++ b/changelog.d/timeline_rendering_status_index_approx.fix @@ -0,0 +1 @@ +Fixed status index approxmiation in timeline rendering for dynamically changing viewport geometries diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index 17065a9ef..c3f4d381d 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -263,7 +263,10 @@ const Timeline = { // Start from approximating the index of some visible status by using the // the center of the screen on the timeline. - let approxIndex = Math.floor(statuses.length * (centerOfScreen / height)) + let approxIndex = Math.min( + Math.floor(statuses.length * (centerOfScreen / height)), + statuses.length - 1, + ) let err = statuses[approxIndex].getBoundingClientRect().y // if we have a previous scroll index that can be used, test if it's From 54b36e8f8e8fa8afd2517e92dee6dfb7b262ebf4 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Thu, 6 Aug 2026 07:14:22 +0300 Subject: [PATCH 13/29] fix server-side domain mutes rendering/api calls --- changelog.d/server-mute-render-modify.fix | 1 + src/components/settings_modal/tabs/mutes_and_blocks_tab.vue | 1 - src/modules/users.js | 6 ++++-- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 changelog.d/server-mute-render-modify.fix diff --git a/changelog.d/server-mute-render-modify.fix b/changelog.d/server-mute-render-modify.fix new file mode 100644 index 000000000..cb94bb826 --- /dev/null +++ b/changelog.d/server-mute-render-modify.fix @@ -0,0 +1 @@ +Fixed server-side domain mutes rendering/api calls diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue index f89253133..a5395ae36 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue @@ -153,7 +153,6 @@