From 7528b0b122a7976eadea4c4f02958f3efdb48562 Mon Sep 17 00:00:00 2001 From: Alexander Tumin Date: Fri, 14 Aug 2026 10:53:47 +0300 Subject: [PATCH 01/21] fix follower remove api call --- changelog.d/follower-remove.fix | 1 + src/modules/users.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changelog.d/follower-remove.fix diff --git a/changelog.d/follower-remove.fix b/changelog.d/follower-remove.fix new file mode 100644 index 000000000..4b78b087d --- /dev/null +++ b/changelog.d/follower-remove.fix @@ -0,0 +1 @@ +Fix follower remove API call diff --git a/src/modules/users.js b/src/modules/users.js index c9aace6fa..1238a63df 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -39,6 +39,7 @@ import { editUserNote as apiEditUserNote, muteDomain as apiMuteDomain, muteUser as apiMuteUser, + removeUserFromFollowers as apiRemoveUserFromFollowers, unblockUser as apiUnblockUser, unmuteDomain as apiUnmuteDomain, unmuteUser as apiUnmuteUser, @@ -112,7 +113,7 @@ const unblockUser = (store, id) => { } const removeUserFromFollowers = (store, id) => { - return removeUserFromFollowers({ id }).then((relationship) => + return apiRemoveUserFromFollowers({ id }).then(({ data: relationship }) => store.commit('updateUserRelationship', [relationship]), ) } From 12df5d09ab5a0de1006d1539fe5a7770daabff74 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Aug 2026 14:52:26 +0300 Subject: [PATCH 02/21] logged-out fixes + rename mapPiniaState -> mapState --- src/components/conversation/conversation.js | 12 +++++------ src/components/login_form/login_form.js | 14 ++++++------- src/components/mention_link/mention_link.js | 4 ++-- src/components/nav_panel/nav_panel.js | 20 +++++++++---------- src/components/navigation/navigation_pins.js | 20 +++++++++---------- src/components/notification/notification.js | 3 --- .../helpers/vertical_tab_switcher.jsx | 4 ++-- 7 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index cc4aecfe3..675d2b4da 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,6 +1,5 @@ import { get, reduce } from 'lodash' -import { mapState as mapPiniaState } from 'pinia' -import { mapState } from 'vuex' +import { mapState } from 'pinia' import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue' import PostStatusForm from 'src/components/post_status_form/post_status_form.vue' @@ -13,6 +12,7 @@ import { useInterfaceStore } from 'src/stores/interface.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useOAuthStore } from 'src/stores/oauth.js' import { useStatusesStore } from 'src/stores/statuses.js' +import { useStreamingStore } from 'src/stores/streaming.js' import { fetchConversation, fetchStatus } from 'src/api/public.js' import { WSConnectionStatus } from 'src/api/websocket.js' @@ -388,11 +388,11 @@ const conversation = { maybeFocused() { return this.isExpanded ? this.focused : null }, - ...mapPiniaState(useMergedConfigStore, ['mergedConfig']), - ...mapState({ - mastoUserSocketStatus: (state) => state.api.mastoUserSocketStatus, + ...mapState(useMergedConfigStore, ['mergedConfig']), + ...mapState(useStreamingStore, { + mastoUserSocketStatus: (state) => state.state, }), - ...mapPiniaState(useInterfaceStore, { + ...mapState(useInterfaceStore, { mobileLayout: (store) => store.layoutType === 'mobile', }), }, diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index 6c2750677..e655e9da9 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -1,8 +1,8 @@ -import { mapActions, mapState as mapPiniaState } from 'pinia' -import { mapState } from 'vuex' +import { mapActions, mapState } from 'pinia' import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { useInstanceStore } from 'src/stores/instance.js' +import { useUsersStore } from 'src/stores/users.js' import { useOAuthStore } from 'src/stores/oauth.js' import { getLoginUrl, getTokenWithCredentials } from 'src/api/oauth.js' @@ -18,12 +18,10 @@ const LoginForm = { error: false, }), computed: { - ...mapState({ - loggingIn: (state) => state.users.loggingIn, - }), - ...mapPiniaState(useOAuthStore, ['clientId', 'clientSecret']), - ...mapPiniaState(useInstanceStore, ['server', 'registrationOpen']), - ...mapPiniaState(useAuthFlowStore, { + ...mapState(useUsersStore, ['loggingIn']), + ...mapState(useOAuthStore, ['clientId', 'clientSecret']), + ...mapState(useInstanceStore, ['server', 'registrationOpen']), + ...mapState(useAuthFlowStore, { isTokenAuth: (store) => store.requiredToken, isPasswordAuth: (store) => !store.requiredToken, }), diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index 374e62259..01ad0eeb1 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -78,8 +78,8 @@ const MentionLink = { return this.url && useUsersStore().findUserByUrl(this.url) }, isYou() { - // FIXME why user !== currentUser??? - return this.user?.id === this.currentUser.id + if (!this.currentUser) return false + return this.user === this.currentUser }, userName() { return this.user && this.userNameFullUi.split('@')[0] diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index 3e88ea564..c3e760efb 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,5 +1,5 @@ -import { mapState as mapPiniaState } from 'pinia' -import { mapState } from 'vuex' +import { mapState } from 'pinia' +import { mapState as mapVuexState } from 'vuex' import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' @@ -111,29 +111,29 @@ const NavPanel = { }, }, computed: { - ...mapPiniaState(useAnnouncementsStore, { + ...mapState(useAnnouncementsStore, { unreadAnnouncementCount: 'unreadAnnouncementCount', supportsAnnouncements: (store) => store.supportsAnnouncements, }), - ...mapPiniaState(useInstanceCapabilitiesStore, [ + ...mapState(useInstanceCapabilitiesStore, [ 'pleromaChatMessagesAvailable', 'pleromaBookmarkFoldersAvailable', 'localBubble', ]), - ...mapPiniaState(useInstanceStore, ['federating']), - ...mapPiniaState(useInstanceStore, { + ...mapState(useInstanceStore, ['federating']), + ...mapState(useInstanceStore, { privateMode: (store) => store.private, }), - ...mapPiniaState(useSyncConfigStore, { + ...mapState(useSyncConfigStore, { collapsed: (store) => store.prefsStorage.simple.collapseNav, pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems), }), - ...mapPiniaState(useUsersStore, ['currentUser']), - ...mapState({ + ...mapState(useUsersStore, ['currentUser']), + ...mapVuexState({ followRequestCount: (state) => state.api.followRequests.length, }), - ...mapPiniaState(useChatsStore, ['unreadChatsCount']), + ...mapState(useChatsStore, ['unreadChatsCount']), timelinesItems() { return filterNavigation( Object.entries({ ...TIMELINES }) diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index d1be73819..2efa94ee4 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -1,5 +1,5 @@ -import { mapState as mapPiniaState } from 'pinia' -import { mapState } from 'vuex' +import { mapState } from 'pinia' +import { mapState as mapVuexState } from 'vuex' import { filterNavigation, @@ -59,26 +59,26 @@ const NavPanel = { getters() { return this.$store.getters }, - ...mapPiniaState(useListsStore, { + ...mapState(useListsStore, { lists: getListEntries, }), - ...mapPiniaState(useAnnouncementsStore, { + ...mapState(useAnnouncementsStore, { supportsAnnouncements: (store) => store.supportsAnnouncements, }), - ...mapPiniaState(useBookmarkFoldersStore, { + ...mapState(useBookmarkFoldersStore, { bookmarks: getBookmarkFolderEntries, }), - ...mapPiniaState(useSyncConfigStore, { + ...mapState(useSyncConfigStore, { pinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedNavItems), }), - ...mapPiniaState(useInstanceStore, ['privateMode', 'federating']), - ...mapPiniaState(useInstanceCapabilitiesStore, [ + ...mapState(useInstanceStore, ['privateMode', 'federating']), + ...mapState(useInstanceCapabilitiesStore, [ 'pleromaChatMessagesAvailable', 'localBubble', ]), - ...mapPiniaState(useUsersStore, ['currentUser']), - ...mapState({ + ...mapState(useUsersStore, ['currentUser']), + ...mapVuexState({ followRequestCount: (state) => state.api.followRequests.length, }), pinnedList() { diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index a589c0cf6..3c82f455c 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -118,9 +118,6 @@ const Notification = { useInstanceStore().restrictedNicknames, ) }, - getUser(notification) { - return this.$store.state.users.usersObject[notification.from_profile.id] - }, interacted() { this.$emit('interacted') }, diff --git a/src/components/settings_modal/helpers/vertical_tab_switcher.jsx b/src/components/settings_modal/helpers/vertical_tab_switcher.jsx index 996939c00..2f3d5e28f 100644 --- a/src/components/settings_modal/helpers/vertical_tab_switcher.jsx +++ b/src/components/settings_modal/helpers/vertical_tab_switcher.jsx @@ -1,6 +1,6 @@ // eslint-disable-next-line no-unused -import { mapState as mapPiniaState } from 'pinia' +import { mapState } from 'pinia' import { Fragment } from 'vue' import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome' @@ -60,7 +60,7 @@ export default { return this.$slots.default().findIndex(isWanted) === this.activeIndex } }, - ...mapPiniaState(useInterfaceStore, { + ...mapState(useInterfaceStore, { mobileLayout: (store) => store.layoutType === 'mobile', }), }, From 1c1526c065228edfcc8785be96f081c7bd4a8fba Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Aug 2026 15:01:59 +0300 Subject: [PATCH 03/21] clear up queueFlush nonsense --- .../quick_filter_settings/quick_filter_settings.js | 2 +- src/components/settings_modal/tabs/clutter_tab.js | 2 +- src/components/settings_modal/tabs/filtering_tab.js | 2 +- src/components/timeline/timeline.js | 9 ++++----- src/stores/fetchers/timeline_fetcher.js | 2 +- src/stores/timelines.js | 12 ++++++------ 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/components/quick_filter_settings/quick_filter_settings.js b/src/components/quick_filter_settings/quick_filter_settings.js index 089cb1d26..482524eb6 100644 --- a/src/components/quick_filter_settings/quick_filter_settings.js +++ b/src/components/quick_filter_settings/quick_filter_settings.js @@ -28,7 +28,7 @@ const QuickFilterSettings = { path: 'replyVisibility', value: visibility, }) - useStatusesStore().queueFlushAll() + useStatusesStore().requireReloadAll() }, openTab(tab) { useInterfaceStore().openSettingsModalTab(tab) diff --git a/src/components/settings_modal/tabs/clutter_tab.js b/src/components/settings_modal/tabs/clutter_tab.js index a3f6e77a5..18300231c 100644 --- a/src/components/settings_modal/tabs/clutter_tab.js +++ b/src/components/settings_modal/tabs/clutter_tab.js @@ -36,7 +36,7 @@ const ClutterTab = { // Updating nested properties watch: { replyVisibility() { - useStatusesStore().queueFlushAll() + useStatusesStore().requireReloadAll() }, }, } diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 9d49eca81..fbc7b8902 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -266,7 +266,7 @@ const FilteringTab = { // Updating nested properties watch: { replyVisibility() { - useStatusesStore().queueFlushAll() + useStatusesStore().requireReloadAll() }, muteFiltersObject() { this.muteFiltersDraftObject = cloneDeep( diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index a76438ad0..19b34b21e 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -63,17 +63,17 @@ const Timeline = { return this.timeline.newStatusCount }, showLoadButton() { - return this.timeline.newStatusCount > 0 || this.timeline.flushMarker !== 0 + return this.timeline.newStatusCount > 0 || this.timeline.reloadNeeded }, loadButtonString() { - if (this.timeline.flushMarker !== 0) { + if (this.timeline.reloadNeeded) { return this.$t('timeline.reload') } else { return `${this.$t('timeline.show_new')} (${this.newStatusCount})` } }, mobileLoadButtonString() { - if (this.timeline.flushMarker !== 0) { + if (this.timeline.reloadNeeded) { return '+' } else { return this.newStatusCount > 99 ? '∞' : this.newStatusCount @@ -167,9 +167,8 @@ const Timeline = { if (e.key === '.') this.showNewStatuses() }, showNewStatuses() { - if (this.timeline.flushMarker !== 0) { + if (this.timeline.reloadNeeded) { useTimelinesStore().clearTimeline(this.timelineRef.name) - useTimelinesStore().queueFlush(this.timelineRef.name, '') this.fetchOlderStatuses() } else { this.blockClicksTemporarily() diff --git a/src/stores/fetchers/timeline_fetcher.js b/src/stores/fetchers/timeline_fetcher.js index 25e6e895f..bc8664dbd 100644 --- a/src/stores/fetchers/timeline_fetcher.js +++ b/src/stores/fetchers/timeline_fetcher.js @@ -50,7 +50,7 @@ const timelineFetcher = (timeline, argument, credentials) => { return fetchTimeline(args) .then(({ data: statuses, pagination, timestamp }) => { if (!older && statuses.length >= 20 && numStatusesBeforeFetch > 0) { - useTimelinesStore().queueFlush(timeline.name, timeline.maxId) + useTimelinesStore().requireReload(timeline.name) } if (older && statuses.length === 0) { diff --git a/src/stores/timelines.js b/src/stores/timelines.js index ded412d4b..6840a28f5 100644 --- a/src/stores/timelines.js +++ b/src/stores/timelines.js @@ -16,7 +16,7 @@ const emptyTl = (name, argument = null) => { maxId: '', minId: '', streaming: false, - flushMarker: 0, + reloadNeeded: false, fetcher: null, socket: null, } @@ -158,7 +158,7 @@ export const useTimelinesStore = defineStore('timelines', { timeline.newStatusCount = 0 timeline.maxId = '' timeline.minId = '' - timeline.flushMarker = 0 + timeline.reloadNeeded = false }, activatePersistents() { TIMELINES.forEach((name) => { @@ -295,12 +295,12 @@ export const useTimelinesStore = defineStore('timelines', { syncOrder(timeline) { timeline.order = timeline.order.filter((id) => timeline.statusIds.has(id)) }, - queueFlush(timeline, id) { - this[timeline].flushMarker = id + requireReload(timeline, id) { + this[timeline].reloadNeeded = true }, - queueFlushAll() { + requireReloadAll() { Object.keys(this).forEach((timeline) => { - this[timeline].flushMarker = this[timeline].maxId + this[timeline].reloadNeeded = true }) }, From 58bc4b33479fb593ec59a4824863df78fea1a130 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Aug 2026 15:36:28 +0300 Subject: [PATCH 04/21] virtual scrolling improvements --- src/components/conversation/conversation.js | 27 +++++++++++++++----- src/components/conversation/conversation.vue | 2 ++ src/stores/statuses.js | 5 ---- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 675d2b4da..7efaf1158 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -93,6 +93,7 @@ const conversation = { default: false, }, }, + emits: ['update:virtualHeight'], data() { return { focused: null, @@ -101,6 +102,7 @@ const conversation = { inlineDivePosition: null, loadStatusError: null, unsuspendibleIds: new Set(), + virtualHeight: 120, } }, created() { @@ -108,6 +110,9 @@ const conversation = { this.fetchConversation() } }, + mounted() { + this.updateVirtualHeight() + }, computed: { status() { return useStatusesStore().allStatuses.get(this.statusId) @@ -360,8 +365,8 @@ const conversation = { return !!(this.expanded || this.isPage) }, hiddenStyle() { - const height = this.status?.virtualHeight || '120px' - return this.virtualHidden ? { height } : {} + if (this.expanded) return {} + return { height: this.virtualHeight + 'px' } }, threadDisplayStatus() { return this.conversation.reduce((a, k) => { @@ -426,11 +431,14 @@ const conversation = { } }, virtualHidden() { - useStatusesStore().setVirtualHeight({ - statusId: this.statusId, - height: `${this.$el.clientHeight}px`, - }) + this.updateVirtualHeight() }, + status: { + handler() { + this.updateVirtualHeight() + }, + deep: true, + } }, methods: { fetchConversation() { @@ -607,6 +615,9 @@ const conversation = { this.threadDisplayStatusObject = {} }, onStatusSuspendStateChange({ id, suspend }) { + this.$nextTick(() => { + this.virtualHeight = this.$refs.body.clientHeight + }) if (!suspend) { this.unsuspendibleIds.add(id) } else { @@ -618,6 +629,10 @@ const conversation = { this.$router.push({ name: 'conversation', params: { id: data.id } }) } }, + updateVirtualHeight() { + this.$emit('update:virtualHeight', { id: this.status.id, height: this.virtualHeight }) + this.virtualHeight = this.$refs.body.clientHeight + }, }, } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index ae6e3a0ce..dd7ee65b3 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -40,6 +40,7 @@

@@ -56,6 +57,7 @@

Date: Mon, 24 Aug 2026 16:04:40 +0300 Subject: [PATCH 05/21] virtual scrolling improvements --- src/components/conversation/conversation.js | 16 +++++----------- src/components/conversation/conversation.vue | 3 +++ src/components/status/status.js | 18 +++++++++++++++++- src/components/status/status.vue | 7 +++++-- src/components/thread_tree/thread_tree.js | 2 +- src/components/thread_tree/thread_tree.vue | 2 ++ src/components/timeline/timeline.js | 1 + 7 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 7efaf1158..5570f6ac3 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -433,12 +433,6 @@ const conversation = { virtualHidden() { this.updateVirtualHeight() }, - status: { - handler() { - this.updateVirtualHeight() - }, - deep: true, - } }, methods: { fetchConversation() { @@ -615,9 +609,6 @@ const conversation = { this.threadDisplayStatusObject = {} }, onStatusSuspendStateChange({ id, suspend }) { - this.$nextTick(() => { - this.virtualHeight = this.$refs.body.clientHeight - }) if (!suspend) { this.unsuspendibleIds.add(id) } else { @@ -630,8 +621,11 @@ const conversation = { } }, updateVirtualHeight() { - this.$emit('update:virtualHeight', { id: this.status.id, height: this.virtualHeight }) - this.virtualHeight = this.$refs.body.clientHeight + this.$nextTick(() => { + console.log('LMAO', this.$refs.body.clientHeight) + this.virtualHeight = this.$refs.body.clientHeight + this.$emit('update:virtualHeight', { id: this.status.id, height: this.virtualHeight, top: this.$el.clientTop }) + }) }, }, } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index dd7ee65b3..cc1376978 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -118,6 +118,7 @@ @goto="setFocused" @dive="() => diveIntoStatus(status.id)" @suspendable-state-change="onStatusSuspendStateChange" + @height-change="updateVirtualHeight" />
diff --git a/src/components/status/status.js b/src/components/status/status.js index 749e0a12e..5b5edb845 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -118,7 +118,7 @@ const Status = { threadDisplayStatus: String, }, - emits: ['goto', 'dive', 'toggleExpanded', 'suspendableStateChange'], + emits: ['goto', 'dive', 'toggleExpanded', 'suspendableStateChange', 'heightChange'], data() { return { replying: false, @@ -539,6 +539,7 @@ const Status = { this.headTailLinks = headTailLinks }, toggleThreadDisplay() { + // FIXME this.controlledToggleThreadDisplay() }, scrollIfFocused(focused) { @@ -557,8 +558,22 @@ const Status = { } } }, + onTransitionEnd() { + this.$nextTick(() => { + this.$emit('heightChange') + }) + }, }, watch: { + status: { + deep: true, + handler() { + this.$emit('heightChange') + }, + }, + replying() { + this.$emit('heightChange') + }, focused: function (id) { this.scrollIfFocused(id) }, @@ -584,6 +599,7 @@ const Status = { }, isSuspendable: function (suspend) { this.$emit('suspendableStateChange', { id: this.status.id, suspend }) + this.$emit('heightChange') }, }, } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index f0b2f1277..8cc8c95f0 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -454,7 +454,10 @@
- +
- + id) diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index 2dae7beee..bbea4e004 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -22,6 +22,7 @@ @goto="$emit('goto', statusId)" @toggle-expanded="toggleExpanded" @suspendable-state-change="e => $emit('suspendableStateChange', e)" + @height-change="e => $emit('height-change', e)" />
Date: Mon, 24 Aug 2026 16:08:47 +0300 Subject: [PATCH 06/21] cleanup & lint --- src/components/conversation/conversation.js | 7 +++++-- src/components/login_form/login_form.js | 2 +- src/components/status/status.js | 8 +++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 5570f6ac3..9827fd321 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -622,9 +622,12 @@ const conversation = { }, updateVirtualHeight() { this.$nextTick(() => { - console.log('LMAO', this.$refs.body.clientHeight) this.virtualHeight = this.$refs.body.clientHeight - this.$emit('update:virtualHeight', { id: this.status.id, height: this.virtualHeight, top: this.$el.clientTop }) + this.$emit('update:virtualHeight', { + id: this.status.id, + height: this.virtualHeight, + top: this.$el.clientTop, + }) }) }, }, diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index e655e9da9..8801d2c2e 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -2,8 +2,8 @@ import { mapActions, mapState } from 'pinia' import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { useInstanceStore } from 'src/stores/instance.js' -import { useUsersStore } from 'src/stores/users.js' import { useOAuthStore } from 'src/stores/oauth.js' +import { useUsersStore } from 'src/stores/users.js' import { getLoginUrl, getTokenWithCredentials } from 'src/api/oauth.js' diff --git a/src/components/status/status.js b/src/components/status/status.js index 5b5edb845..ee69a7d05 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -118,7 +118,13 @@ const Status = { threadDisplayStatus: String, }, - emits: ['goto', 'dive', 'toggleExpanded', 'suspendableStateChange', 'heightChange'], + emits: [ + 'goto', + 'dive', + 'toggleExpanded', + 'suspendableStateChange', + 'heightChange', + ], data() { return { replying: false, From 034b2c3bf9c09e5ea1a7c52747099a22c911febc Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Aug 2026 16:34:08 +0300 Subject: [PATCH 07/21] render placeholder avatars while fetching mentions --- src/components/mention_link/mention_link.vue | 11 ++++++++--- src/components/user_avatar/user_avatar.js | 2 +- src/components/user_avatar/user_avatar.vue | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/mention_link/mention_link.vue b/src/components/mention_link/mention_link.vue index 33f0d9db7..3973522b0 100644 --- a/src/components/mention_link/mention_link.vue +++ b/src/components/mention_link/mention_link.vue @@ -8,15 +8,20 @@ :href="url" class="original" target="_blank" - v-html="content" - /> + > + + + Date: Mon, 24 Aug 2026 16:35:51 +0300 Subject: [PATCH 08/21] fix warnings --- src/components/thread_tree/thread_tree.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index bbea4e004..be95f8698 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -22,7 +22,7 @@ @goto="$emit('goto', statusId)" @toggle-expanded="toggleExpanded" @suspendable-state-change="e => $emit('suspendableStateChange', e)" - @height-change="e => $emit('height-change', e)" + @height-change="e => $emit('heightChange', e)" />
Date: Mon, 24 Aug 2026 16:49:19 +0300 Subject: [PATCH 09/21] more cleanup --- src/components/mention_link/mention_link.js | 4 ++-- src/components/status/status.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/mention_link/mention_link.js b/src/components/mention_link/mention_link.js index 01ad0eeb1..5db90ab41 100644 --- a/src/components/mention_link/mention_link.js +++ b/src/components/mention_link/mention_link.js @@ -29,7 +29,7 @@ const MentionLink = { }, props: { url: { - required: true, + required: false, type: String, }, content: { @@ -75,7 +75,7 @@ const MentionLink = { }, computed: { user() { - return this.url && useUsersStore().findUserByUrl(this.url) + return this.url ? useUsersStore().findUserByUrl(this.url) : null }, isYou() { if (!this.currentUser) return false diff --git a/src/components/status/status.js b/src/components/status/status.js index ee69a7d05..9f6be831c 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -195,7 +195,7 @@ const Status = { ) // User referenced in post might not be yet present in store - // since their data is not included in status data + // since their data is not included in status data, just the id return user?.statusnet_profile_url } }, @@ -376,7 +376,7 @@ const Status = { }, replyToName() { if (this.mainStatus.in_reply_to_screen_name) { - return this.status.in_reply_to_screen_name + return this.mainStatus.in_reply_to_screen_name } else { const user = useUsersStore().findUser( this.mainStatus.in_reply_to_user_id, From 5b79cff56cb800e56339fab7cd43190d865ebc3b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Aug 2026 17:28:05 +0300 Subject: [PATCH 10/21] restoration of pinned --- src/components/status/status.js | 1 - src/components/status/status.vue | 2 +- src/components/tab_switcher/tab_switcher.jsx | 4 ++++ src/components/timeline/timeline.js | 9 +++++---- src/components/timeline/timeline.vue | 2 +- src/components/user_profile/user_profile.js | 2 -- src/components/user_profile/user_profile.vue | 5 ----- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/status/status.js b/src/components/status/status.js index 9f6be831c..ac58c3b8d 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -105,7 +105,6 @@ const Status = { isPreview: Boolean, noHeading: Boolean, inlineExpanded: Boolean, - showPinned: Boolean, inProfile: Boolean, inConversation: Boolean, inQuote: Boolean, diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 8cc8c95f0..0db67f24b 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -169,7 +169,7 @@ diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index f72c7f93c..ee18b0c4d 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -27,11 +27,10 @@ library.add(faCircleNotch, faCog, faMinus, faArrowUp, faCirclePlus, faCheck) const Timeline = { props: { timelineRef: Object, - count: Number, footerSlipgate: Object, // reference to an element where we should put our footer embedded: Boolean, inProfile: Boolean, - skipPinned: Boolean, + skipPinned: Boolean }, data() { return { @@ -59,6 +58,9 @@ const Timeline = { .map((id) => useStatusesStore().allStatuses.get(id)) .filter(({ pinned }) => (this.skipPinned ? !pinned : true)) }, + count() { + return this.timeline.order.length + }, newStatusCount() { return this.timeline.newStatusCount }, @@ -190,13 +192,12 @@ const Timeline = { if (!this.virtualScrollingEnabled) return const statuses = this.$refs.timeline.children + if (statuses.length === 0) return const cappedScrollIndex = Math.max( 0, Math.min(this.virtualScrollIndex, statuses.length - 1), ) - if (statuses.length === 0) return - const height = Math.max(document.body.offsetHeight, window.pageYOffset) const centerOfScreen = window.pageYOffset + window.innerHeight * 0.5 diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index 745d8c399..faf184504 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -84,7 +84,7 @@ />
-
+
{ - console.log('LOAD', userId) this.userId = userId } @@ -139,7 +138,6 @@ const UserProfile = { } }, switchUser(userNameOrId) { - console.log('USER SWITCH') this.load(userNameOrId) }, onTabSwitch(tab) { diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue index fbad5c42d..f60521ffa 100644 --- a/src/components/user_profile/user_profile.vue +++ b/src/components/user_profile/user_profile.vue @@ -23,18 +23,14 @@ key="statuses" class="statuses" :label="$t('user_card.statuses')" - :count="user.statuses_count" :title="$t('user_profile.timeline_title')" > - Date: Mon, 24 Aug 2026 17:28:13 +0300 Subject: [PATCH 11/21] don't try to fetch older posts if bottomed out --- src/stores/fetchers/timeline_fetcher.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stores/fetchers/timeline_fetcher.js b/src/stores/fetchers/timeline_fetcher.js index bc8664dbd..83b1c3b9a 100644 --- a/src/stores/fetchers/timeline_fetcher.js +++ b/src/stores/fetchers/timeline_fetcher.js @@ -47,6 +47,7 @@ const timelineFetcher = (timeline, argument, credentials) => { const numStatusesBeforeFetch = timeline.statusIds.size + if (bottomedOut.value) return return fetchTimeline(args) .then(({ data: statuses, pagination, timestamp }) => { if (!older && statuses.length >= 20 && numStatusesBeforeFetch > 0) { From 121038dc9664b15ff9a267bf1ef434d5266ac961 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 24 Aug 2026 17:34:59 +0300 Subject: [PATCH 12/21] split loading between older and newer, add newer loading indicator --- src/components/timeline/timeline.js | 2 +- src/components/timeline/timeline.vue | 12 +++++++++++- src/stores/fetchers/timeline_fetcher.js | 18 ++++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js index ee18b0c4d..33ee9a21d 100644 --- a/src/components/timeline/timeline.js +++ b/src/components/timeline/timeline.js @@ -238,7 +238,7 @@ const Timeline = { const bodyBRect = document.body.getBoundingClientRect() const height = Math.max(bodyBRect.height, -bodyBRect.y) if ( - !this.timeline.fetcher.loading.value && + !this.timeline.fetcher.loadingOlder.value && this.$el.offsetHeight > 0 && window.innerHeight + window.pageYOffset >= height - 750 ) { diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index faf184504..7e3eefce2 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -8,6 +8,16 @@ v-if="!embedded" :timeline-name="timelineRef.name" /> +
+ +