From b721cd99512d7b335945dee72eb4d06f82422408 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 31 Aug 2026 18:05:45 +0300 Subject: [PATCH 01/10] simplify lodash tree-shaking (no real benefit though, but build should be simpler --- .babelrc | 2 +- package.json | 3 +- yarn.lock | 79 ++++------------------------------------------------ 3 files changed, 7 insertions(+), 77 deletions(-) diff --git a/.babelrc b/.babelrc index 4ec104161..48f99d6ca 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,5 @@ { "presets": ["@babel/preset-env"], - "plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"], + "plugins": ["@babel/plugin-transform-runtime", "@vue/babel-plugin-jsx"], "comments": true } diff --git a/package.json b/package.json index 50138e6a2..c10f86b84 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,6 @@ "@vue/compiler-sfc": "3.5.22", "@vue/test-utils": "2.4.6", "autoprefixer": "10.4.21", - "babel-plugin-lodash": "3.3.4", "chai": "5.3.3", "chalk": "5.6.2", "chromedriver": "135.0.4", @@ -97,7 +96,7 @@ "function-bind": "1.1.2", "http-proxy-middleware": "3.0.5", "iso-639-1": "3.1.5", - "lodash": "4.17.21", + "lodash-es": "4.17.21", "msw": "2.14.6", "nightwatch": "3.12.2", "oxc": "^1.0.1", diff --git a/yarn.lock b/yarn.lock index 8644d2d53..4ca508961 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,7 +20,7 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.26.2": +"@babel/code-frame@^7.0.0": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -113,17 +113,6 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.1" -"@babel/generator@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" - integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== - dependencies: - "@babel/parser" "^7.27.0" - "@babel/types" "^7.27.0" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^3.0.2" - "@babel/generator@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e" @@ -281,14 +270,6 @@ "@babel/traverse" "^7.29.7" "@babel/types" "^7.29.7" -"@babel/helper-module-imports@^7.0.0-beta.49": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" - integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - "@babel/helper-module-imports@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" @@ -479,13 +460,6 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" - integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== - dependencies: - "@babel/types" "^7.27.0" - "@babel/parser@^7.27.2", "@babel/parser@^7.28.0", "@babel/parser@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.3.tgz#d2d25b814621bca5fe9d172bc93792547e7a2a71" @@ -1131,15 +1105,6 @@ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== -"@babel/template@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" - integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/parser" "^7.27.0" - "@babel/types" "^7.27.0" - "@babel/template@^7.27.1", "@babel/template@^7.27.2": version "7.27.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" @@ -1158,19 +1123,6 @@ "@babel/parser" "^7.29.7" "@babel/types" "^7.29.7" -"@babel/traverse@^7.25.9": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" - integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.27.0" - "@babel/parser" "^7.27.0" - "@babel/template" "^7.27.0" - "@babel/types" "^7.27.0" - debug "^4.3.1" - globals "^11.1.0" - "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.3.tgz#6911a10795d2cce43ec6a28cffc440cca2593434" @@ -1223,7 +1175,7 @@ "@babel/types" "^7.29.7" debug "^4.3.1" -"@babel/types@^7.0.0-beta.49", "@babel/types@^7.25.9", "@babel/types@^7.27.0", "@babel/types@^7.4.4": +"@babel/types@^7.25.9", "@babel/types@^7.4.4": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -1804,7 +1756,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": version "0.3.30" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz#4a76c4daeee5df09f5d3940e087442fb36ce2b99" integrity sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q== @@ -3393,17 +3345,6 @@ axios@^1.7.4: form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-plugin-lodash@3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz#4f6844358a1340baed182adbeffa8df9967bc196" - integrity sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg== - dependencies: - "@babel/helper-module-imports" "^7.0.0-beta.49" - "@babel/types" "^7.0.0-beta.49" - glob "^7.1.1" - lodash "^4.17.10" - require-package-name "^2.0.1" - babel-plugin-polyfill-corejs2@^0.4.14: version "0.4.14" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz#8101b82b769c568835611542488d463395c2ef8f" @@ -5404,7 +5345,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@7.2.3, glob@^7.1.1, glob@^7.1.4, glob@^7.2.3: +glob@7.2.3, glob@^7.1.4, glob@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5455,11 +5396,6 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" @@ -6488,7 +6424,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.21: +lodash@4.17.21, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7763,11 +7699,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -require-package-name@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" - integrity sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q== - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" From d8825bae3355bebf43c6a954f8e12aa9faf51d06 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 31 Aug 2026 18:44:30 +0300 Subject: [PATCH 02/10] yarn lock --- yarn.lock | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 4ca508961..4913bcfb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6379,6 +6379,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash-es@4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -6424,7 +6429,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== -lodash@4.17.21, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== From 8a743c648c77378a92993b230d4c4f587c243fee Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 31 Aug 2026 19:05:26 +0300 Subject: [PATCH 03/10] migrate follow requests to separate pinia store --- .../extra_notifications.js | 6 +-- .../extra_notifications.vue | 2 +- .../follow_request_card.js | 18 ++++---- .../follow_requests/follow_requests.js | 4 +- src/components/mobile_nav/mobile_nav.js | 3 +- src/components/nav_panel/nav_panel.js | 6 +-- src/components/navigation/navigation.js | 2 +- src/components/navigation/navigation_pins.js | 6 +-- src/components/notifications/notifications.js | 3 +- src/components/side_drawer/side_drawer.js | 5 +-- src/components/side_drawer/side_drawer.vue | 4 +- src/modules/api.js | 26 ----------- .../follow_request_fetcher.service.js | 33 -------------- .../notification_utils/notification_utils.js | 10 ++--- src/stores/fetchers/follow_requests.js | 44 +++++++++++++++++++ src/stores/follow_requests.js | 37 ++++++++++++++++ src/stores/users.js | 11 +++-- 17 files changed, 121 insertions(+), 99 deletions(-) delete mode 100644 src/services/follow_request_fetcher/follow_request_fetcher.service.js create mode 100644 src/stores/fetchers/follow_requests.js create mode 100644 src/stores/follow_requests.js diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js index 851656ae5..d20cbb67e 100644 --- a/src/components/extra_notifications/extra_notifications.js +++ b/src/components/extra_notifications/extra_notifications.js @@ -1,8 +1,8 @@ import { mapState } from 'pinia' -import { mapGetters } from 'vuex' import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useChatsStore } from 'src/stores/chats.js' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' @@ -37,7 +37,7 @@ const ExtraNotifications = { return ( this.mergedConfig.showExtraNotifications && this.mergedConfig.showFollowRequestsInExtraNotifications && - this.followRequestCount + this.followRequestsCount ) }, hasAnythingToShow() { @@ -55,12 +55,12 @@ const ExtraNotifications = { currentUser() { return useUsersStore().currentUser }, - ...mapGetters(['followRequestCount']), ...mapState(useAnnouncementsStore, { unreadAnnouncementCount: 'unreadAnnouncementCount', }), ...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useChatsStore, ['unreadChatsCount']), + ...mapState(useFollowRequestsStore, ['followRequestsCount']), }, methods: { openNotificationSettings() { diff --git a/src/components/extra_notifications/extra_notifications.vue b/src/components/extra_notifications/extra_notifications.vue index 388435950..329c5c1c3 100644 --- a/src/components/extra_notifications/extra_notifications.vue +++ b/src/components/extra_notifications/extra_notifications.vue @@ -48,7 +48,7 @@ class="fa-scale-110 icon" icon="user-plus" /> - {{ $t('notifications.unread_follow_requests', { num: followRequestCount }, followRequestCount) }} + {{ $t('notifications.unread_follow_requests', { num: followRequestsCount }, followRequestsCount) }} { + const notifId = this.findFollowRequestNotificationId() + useFollowRequestsStore().remove(this.user.id) + notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId) }) - // TODO fix - this.$store.dispatch('removeFollowRequest', this.user) - - const notifId = this.findFollowRequestNotificationId() - useNotificationsStore().markSingleNotificationAsSeen(notifId) this.hideApproveConfirmDialog() }, denyUser() { @@ -70,15 +70,13 @@ const FollowRequestCard = { } }, doDeny() { - const notifId = this.findFollowRequestNotificationId() - denyUser({ id: this.user.id, credentials: useOAuthStore().token, }).then(() => { - useNotificationsStore().dismissNotificationLocal(notifId) - // TODO fix - this.$store.dispatch('removeFollowRequest', this.user) + const notifId = this.findFollowRequestNotificationId() + useFollowRequestsStore().remove(this.user.id) + notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId) }) this.hideDenyConfirmDialog() }, diff --git a/src/components/follow_requests/follow_requests.js b/src/components/follow_requests/follow_requests.js index 513298afc..5936e2303 100644 --- a/src/components/follow_requests/follow_requests.js +++ b/src/components/follow_requests/follow_requests.js @@ -1,3 +1,5 @@ +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' + import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue' const FollowRequests = { @@ -6,7 +8,7 @@ const FollowRequests = { }, computed: { requests() { - return this.$store.state.api.followRequests + return useFollowRequestsStore().requests.values() }, }, } diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index 97645ca73..e7fe404e0 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -10,6 +10,7 @@ import { import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useChatsStore } from 'src/stores/chats.js' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInstanceStore } from 'src/stores/instance.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useNotificationsStore } from 'src/stores/notifications.js' @@ -67,10 +68,10 @@ const MobileNav = { return ( this.unseenNotifications.length + countExtraNotifications( - this.$store, useMergedConfigStore().mergedConfig, useChatsStore().unreadChatsCount, useAnnouncementsStore().unreadAnnouncementCount, + useFollowRequestsStore().followRequestsCount, ) ) }, diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js index c3e760efb..f5de8bb2c 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,5 +1,4 @@ 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' @@ -11,6 +10,7 @@ import NavigationPins from 'src/components/navigation/navigation_pins.vue' import { useAnnouncementsStore } from 'src/stores/announcements' import { useChatsStore } from 'src/stores/chats.js' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' @@ -130,9 +130,7 @@ const NavPanel = { new Set(store.prefsStorage.collections.pinnedNavItems), }), ...mapState(useUsersStore, ['currentUser']), - ...mapVuexState({ - followRequestCount: (state) => state.api.followRequests.length, - }), + ...mapState(useFollowRequestsStore, ['followRequestsCount']), ...mapState(useChatsStore, ['unreadChatsCount']), timelinesItems() { return filterNavigation( diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index 39fa2c993..7a2e29646 100644 --- a/src/components/navigation/navigation.js +++ b/src/components/navigation/navigation.js @@ -85,7 +85,7 @@ export const ROOT_ITEMS = { label: 'nav.friend_requests', badgeStyle: 'notification', criteria: ['lockedUser'], - badgeGetter: 'followRequestCount', + badgeGetter: 'followRequestsCount', }, about: { route: 'about', diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index 2efa94ee4..f16336ef3 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -1,5 +1,4 @@ import { mapState } from 'pinia' -import { mapState as mapVuexState } from 'vuex' import { filterNavigation, @@ -14,6 +13,7 @@ import { import { useAnnouncementsStore } from 'src/stores/announcements' import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useListsStore } from 'src/stores/lists' @@ -78,9 +78,7 @@ const NavPanel = { 'localBubble', ]), ...mapState(useUsersStore, ['currentUser']), - ...mapVuexState({ - followRequestCount: (state) => state.api.followRequests.length, - }), + ...mapState(useFollowRequestsStore, ['followRequestsCount']), pinnedList() { if (!this.currentUser) { return filterNavigation( diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 2272c78af..734671521 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -14,6 +14,7 @@ import NotificationFilters from './notification_filters.vue' import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useChatsStore } from 'src/stores/chats.js' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useNotificationsStore } from 'src/stores/notifications.js' @@ -107,10 +108,10 @@ const Notifications = { }, extraNotificationsCount() { return countExtraNotifications( - this.$store, useMergedConfigStore().mergedConfig, useChatsStore().unreadChatsCount, useAnnouncementsStore().unreadAnnouncementCount, + useFollowRequestsStore().followRequestsCount, ) }, unseenCountTitle() { diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index 2d2886d58..40d922228 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -8,6 +8,7 @@ import { unseenNotifications } from '../../services/notification_utils/notificat import { useAnnouncementsStore } from 'src/stores/announcements' import { useChatsStore } from 'src/stores/chats.js' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface' @@ -85,9 +86,6 @@ const SideDrawer = { unseenNotificationsCount() { return this.unseenNotifications.length }, - followRequestCount() { - return this.$store.state.api.followRequests.length - }, timelinesRoute() { let name if (useInterfaceStore().lastTimeline) { @@ -100,6 +98,7 @@ const SideDrawer = { return { name } } }, + ...mapState(useFollowRequestsStore, ['followRequestsCount']), ...mapState(useAnnouncementsStore, [ 'supportsAnnouncements', 'unreadAnnouncementCount', diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index c810d93a0..a42287b3c 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -141,10 +141,10 @@ icon="user-plus" /> {{ $t("nav.friend_requests") }} - {{ followRequestCount }} + {{ followRequestsCount }} diff --git a/src/modules/api.js b/src/modules/api.js index 290fbfc27..1395752e0 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -1,19 +1,12 @@ import { Socket } from 'phoenix' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useOAuthStore } from 'src/stores/oauth.js' import { useShoutStore } from 'src/stores/shout.js' -import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service' - const api = { state: { fetchers: {}, socket: null, - followRequests: [], - }, - getters: { - followRequestCount: (state) => state.followRequests.length, }, mutations: { addFetcher(state, { fetcherName, fetcher }) { @@ -29,27 +22,8 @@ const api = { setSocket(state, socket) { state.socket = socket }, - setFollowRequests(state, value) { - state.followRequests = value - }, }, actions: { - // Follow requests - startFetchingFollowRequests(store) { - if (store.state.fetchers.followRequests) return - const fetcher = followRequestFetcher.startFetching({ - store, - credentials: useOAuthStore().token, - }) - - store.commit('addFetcher', { fetcherName: 'followRequests', fetcher }) - }, - stopFetchingFollowRequests(store) { - const fetcher = store.state.fetchers.followRequests - if (!fetcher) return - store.commit('removeFetcher', { fetcherName: 'followRequests', fetcher }) - }, - // Pleroma websocket setWsToken(store, token) { store.commit('setWsToken', token) diff --git a/src/services/follow_request_fetcher/follow_request_fetcher.service.js b/src/services/follow_request_fetcher/follow_request_fetcher.service.js deleted file mode 100644 index 492c4e648..000000000 --- a/src/services/follow_request_fetcher/follow_request_fetcher.service.js +++ /dev/null @@ -1,33 +0,0 @@ -import { useUsersStore } from 'src/stores/users.js' - -import { fetchFollowRequests } from 'src/api/user.js' -import { promiseInterval } from 'src/services/promise_interval/promise_interval.js' - -const fetchAndUpdate = ({ store, credentials }) => { - return fetchFollowRequests({ credentials }) - .then( - (result) => { - const { data: requests } = result - store.commit('setFollowRequests', requests) - useUsersStore().addNewUsers(result) - }, - (rej) => { - console.error(rej) - }, - ) - .catch((e) => { - console.error(e) - }) -} - -const startFetching = ({ credentials, store }) => { - const boundFetchAndUpdate = () => fetchAndUpdate({ credentials, store }) - boundFetchAndUpdate() - return promiseInterval(boundFetchAndUpdate, 10000) -} - -const followRequestFetcher = { - startFetching, -} - -export default followRequestFetcher diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js index 1fdb7bec1..803a56322 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -98,13 +98,11 @@ export const unseenNotifications = ( } export const countExtraNotifications = ( - store, mergedConfig, unreadChatsCount, - unreadAnnouncementCount, + unreadAnnouncementsCount, + followRequestsCount, ) => { - const rootGetters = store.rootGetters || store.getters - if (!mergedConfig.showExtraNotifications) { return 0 } @@ -112,10 +110,10 @@ export const countExtraNotifications = ( return [ mergedConfig.showChatsInExtraNotifications ? unreadChatsCount : 0, mergedConfig.showAnnouncementsInExtraNotifications - ? unreadAnnouncementCount + ? unreadAnnouncementsCount : 0, mergedConfig.showFollowRequestsInExtraNotifications - ? rootGetters.followRequestCount + ? followRequestsCount : 0, ].reduce((a, c) => a + c, 0) } diff --git a/src/stores/fetchers/follow_requests.js b/src/stores/fetchers/follow_requests.js new file mode 100644 index 000000000..8794ab07e --- /dev/null +++ b/src/stores/fetchers/follow_requests.js @@ -0,0 +1,44 @@ +import { ref } from 'vue' + +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' +import { useUsersStore } from 'src/stores/users.js' + +import { fetchFollowRequests } from 'src/api/user.js' +import { promiseInterval } from 'src/services/promise_interval/promise_interval.js' + +const followRequestFetcher = ({ credentials }) => { + const interval = ref(null) + + const fetchAndUpdate = () => { + return fetchFollowRequests({ credentials }) + .then((result) => { + const { data: requests } = result + useFollowRequestsStore().setFollowRequests(requests) + useUsersStore().addNewUsers(result) + }) + .catch((e) => { + console.error(e) + }) + } + + const startFetching = () => { + if (interval.value) throw new Error('Interval already exists!') + + fetchAndUpdate() + + interval.value = promiseInterval(fetchAndUpdate, 10000) + } + + const stopFetching = () => { + interval.value.stop() + interval.value = null + } + + return { + fetchAndUpdate, + startFetching, + stopFetching, + } +} + +export default followRequestFetcher diff --git a/src/stores/follow_requests.js b/src/stores/follow_requests.js new file mode 100644 index 000000000..064d57253 --- /dev/null +++ b/src/stores/follow_requests.js @@ -0,0 +1,37 @@ +import { defineStore } from 'pinia' + +import followRequestFetcher from 'src/stores/fetchers/follow_requests.js' +import { useOAuthStore } from 'src/stores/oauth.js' + +export const useFollowRequestsStore = defineStore('followRequests', { + state: () => ({ + fetcher: null, + requests: new Map(), + }), + getters: { + followRequestsCount(state) { + return state.requests.size + }, + }, + actions: { + startFetching() { + if (this.fetcher) throw 'Fetcher already exists!' + + this.fetcher = followRequestFetcher({ + credentials: useOAuthStore().token, + }) + + this.fetcher.startFetching() + }, + stopFetching() { + if (!this.fetcher) throw "Fetcher doesn't exists!" + this.fetcher.stopFetching(), (this.fetcher = null) + }, + setFollowRequests(requests) { + this.requests = new Map(requests.map((user) => [user.id, user])) + }, + remove(id) { + this.requests.delete(id) + } + }, +}) diff --git a/src/stores/users.js b/src/stores/users.js index 90c87c0f3..71a9ddc94 100644 --- a/src/stores/users.js +++ b/src/stores/users.js @@ -6,6 +6,7 @@ import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js' import { useChatsStore } from 'src/stores/chats.js' import { useEmojiStore } from 'src/stores/emoji.js' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface.js' @@ -678,9 +679,10 @@ export const useUsersStore = defineStore('users', { useListsStore().startFetching() useBookmarkFoldersStore().startFetching() - if (user.locked) { - dispatch('startFetchingFollowRequests') - } + // if (user.locked) { + dispatch('startFetchingFollowRequests') + useFollowRequestsStore().startFetching() + // } if (useMergedConfigStore().mergedConfig.useStreamingApi) { useStreamingStore().initSocket(true) @@ -724,6 +726,9 @@ export const useUsersStore = defineStore('users', { useListsStore().stopFetching() useBookmarkFoldersStore().stopFetching() useChatsStore().stopFetching() + // if (this.currentUser.locked) { + useFollowRequestsStore().stopFetching() + // } store?.dispatch('stopFetchingFollowRequests') From 36cb7c1aa440df979ccaedce0263896bceb1b8da Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 31 Aug 2026 19:15:55 +0300 Subject: [PATCH 04/10] undo debug --- src/stores/users.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/stores/users.js b/src/stores/users.js index 71a9ddc94..78527b4c5 100644 --- a/src/stores/users.js +++ b/src/stores/users.js @@ -679,10 +679,10 @@ export const useUsersStore = defineStore('users', { useListsStore().startFetching() useBookmarkFoldersStore().startFetching() - // if (user.locked) { - dispatch('startFetchingFollowRequests') - useFollowRequestsStore().startFetching() - // } + if (user.locked) { + dispatch('startFetchingFollowRequests') + useFollowRequestsStore().startFetching() + } if (useMergedConfigStore().mergedConfig.useStreamingApi) { useStreamingStore().initSocket(true) @@ -726,9 +726,9 @@ export const useUsersStore = defineStore('users', { useListsStore().stopFetching() useBookmarkFoldersStore().stopFetching() useChatsStore().stopFetching() - // if (this.currentUser.locked) { - useFollowRequestsStore().stopFetching() - // } + if (this.currentUser.locked) { + useFollowRequestsStore().stopFetching() + } store?.dispatch('stopFetchingFollowRequests') From d6c6c44e894e0b1746a3d0d592f8a9458269cf4b Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 31 Aug 2026 19:22:12 +0300 Subject: [PATCH 05/10] move phoenix socket stuff into shoutstore, removing api vuex module --- src/modules/api.js | 53 -------------------------------------------- src/modules/index.js | 2 -- src/stores/shout.js | 29 +++++++++++++++++++++--- src/stores/users.js | 6 +++-- 4 files changed, 30 insertions(+), 60 deletions(-) delete mode 100644 src/modules/api.js diff --git a/src/modules/api.js b/src/modules/api.js deleted file mode 100644 index 1395752e0..000000000 --- a/src/modules/api.js +++ /dev/null @@ -1,53 +0,0 @@ -import { Socket } from 'phoenix' - -import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useShoutStore } from 'src/stores/shout.js' - -const api = { - state: { - fetchers: {}, - socket: null, - }, - mutations: { - addFetcher(state, { fetcherName, fetcher }) { - state.fetchers[fetcherName] = fetcher - }, - removeFetcher(state, { fetcherName }) { - state.fetchers[fetcherName].stop() - delete state.fetchers[fetcherName] - }, - setWsToken(state, token) { - state.wsToken = token - }, - setSocket(state, socket) { - state.socket = socket - }, - }, - actions: { - // Pleroma websocket - setWsToken(store, token) { - store.commit('setWsToken', token) - }, - initializeSocket({ commit, state, rootState }) { - // Set up websocket connection - const token = state.wsToken - if ( - useInstanceCapabilitiesStore().shoutAvailable && - token !== undefined && - state.socket === null - ) { - const socket = new Socket('/socket', { params: { token } }) - socket.connect() - - commit('setSocket', socket) - useShoutStore().initializeShout(socket) - } - }, - disconnectFromSocket({ commit, state }) { - state.socket?.disconnect() - commit('setSocket', null) - }, - }, -} - -export default api diff --git a/src/modules/index.js b/src/modules/index.js index 6aa236257..61b7fd996 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -1,9 +1,7 @@ -import api from './api.js' import drafts from './drafts.js' import profileConfig from './profileConfig.js' export default { - api, profileConfig, drafts, } diff --git a/src/stores/shout.js b/src/stores/shout.js index 79268bd57..f76b37f5e 100644 --- a/src/stores/shout.js +++ b/src/stores/shout.js @@ -1,14 +1,33 @@ -import { defineStore } from 'pinia' +import { Socket } from 'phoenix' +import { defineStore } from 'pinia' +import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' +import { useUsersStore } from 'src/stores/users.js' + +// Maybe rename it to PhoenixSocket if we ever utilize this socket more export const useShoutStore = defineStore('shout', { state: () => ({ messages: [], channel: { state: '' }, joined: false, + token: null, + socket: null, }), + getters: { + token: () => useUsersStore().currentUser?.token + }, actions: { - initializeShout(socket) { - const channel = socket.channel('chat:public') + initializeSocket() { + if (this.token === null) return + if (!useInstanceCapabilitiesStore().shoutAvailable) return + if (this.socket !== null) throw new Error('Shout socket already exist!') + + this.socket = new Socket('/socket', { params: { token: this.token } }) + this.socket.connect() + }, + initializeShout() { + const channel = this.socket.channel('chat:public') + channel.joinPush.receive('ok', () => { this.joined = true }) @@ -28,5 +47,9 @@ export const useShoutStore = defineStore('shout', { channel.join() this.channel = channel }, + disconnectSocket() { + this.socket?.disconnect() + this.socket = null + } }, }) diff --git a/src/stores/users.js b/src/stores/users.js index 78527b4c5..305d0fc5b 100644 --- a/src/stores/users.js +++ b/src/stores/users.js @@ -11,6 +11,7 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useListsStore } from 'src/stores/lists.js' +import { useShoutStore } from 'src/stores/shout.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useNotificationsStore } from 'src/stores/notifications.js' import { useOAuthStore } from 'src/stores/oauth.js' @@ -663,8 +664,8 @@ export const useUsersStore = defineStore('users', { if (user.token) { // Shoutbox - dispatch('setWsToken', user.token) - dispatch('initializeSocket') + useShoutStore().initializeSocket() + useShoutStore().initializeShout() } // DMs and Home @@ -747,6 +748,7 @@ export const useUsersStore = defineStore('users', { }) .then(() => { oauth.clearToken() + useShoutStore().disconnectSocket() this.currentUser = null From 316bf4c226faea994e6da703eefe3c5d95a8b22c Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 1 Sep 2026 18:02:38 +0300 Subject: [PATCH 06/10] migrate and fix profileConfig --- src/api/user.js | 6 +- .../follow_requests/follow_requests.js | 4 +- .../settings_modal/helpers/setting.js | 5 +- .../settings_modal/tabs/composing_tab.js | 6 +- .../settings_modal/tabs/composing_tab.vue | 6 +- src/modules/index.js | 2 - src/stores/follow_requests.js | 2 +- .../profile_config.js} | 113 ++++++++++-------- src/stores/shout.js | 6 +- src/stores/users.js | 6 +- 10 files changed, 85 insertions(+), 71 deletions(-) rename src/{modules/profileConfig.js => stores/profile_config.js} (62%) diff --git a/src/api/user.js b/src/api/user.js index ec1763bb4..430b2c895 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -17,7 +17,8 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' const MOVE_ACCOUNT_URL = '/api/pleroma/move_account' const ALIASES_URL = '/api/pleroma/aliases' -const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' +const NOTIFICATION_SETTINGS_URL = ({ blockFromStrangers, hideNotificationContents }) => + `/api/pleroma/notification_settings${paramsString({ blockFromStrangers, hideNotificationContents })}` export const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' @@ -432,10 +433,9 @@ export const exportFriends = ({ id, credentials }) => { // #Profile settings export const updateNotificationSettings = ({ credentials, settings }) => { return promisedRequest({ - url: NOTIFICATION_SETTINGS_URL, + url: NOTIFICATION_SETTINGS_URL(settings), credentials, method: 'PUT', - payload: settings, }) } diff --git a/src/components/follow_requests/follow_requests.js b/src/components/follow_requests/follow_requests.js index 5936e2303..181d5605e 100644 --- a/src/components/follow_requests/follow_requests.js +++ b/src/components/follow_requests/follow_requests.js @@ -1,7 +1,7 @@ -import { useFollowRequestsStore } from 'src/stores/follow_requests.js' - import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue' +import { useFollowRequestsStore } from 'src/stores/follow_requests.js' + const FollowRequests = { components: { FollowRequestCard, diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index a1a946fca..d0dce5950 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -9,6 +9,7 @@ import { useInterfaceStore } from 'src/stores/interface.js' import { useLocalConfigStore } from 'src/stores/local_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useProfileConfigStore } from 'src/stores/profile_config.js' export default { components: { @@ -236,7 +237,7 @@ export default { configSource() { switch (this.realSource) { case 'profile': - return this.$store.state.profileConfig + return useProfileConfigStore().config case 'admin': return useAdminSettingsStore().config default: @@ -253,7 +254,7 @@ export default { switch (this.realSource) { case 'profile': return (k, v) => - this.$store.dispatch('setProfileOption', { name: k, value: v }) + useProfileConfigStore().setProfileOption({ name: k, value: v }) case 'admin': return (k, v) => useAdminSettingsStore().pushAdminSetting({ path: k, value: v }) diff --git a/src/components/settings_modal/tabs/composing_tab.js b/src/components/settings_modal/tabs/composing_tab.js index 3e734e6e9..74f01a2e1 100644 --- a/src/components/settings_modal/tabs/composing_tab.js +++ b/src/components/settings_modal/tabs/composing_tab.js @@ -17,6 +17,7 @@ import { useInterfaceStore } from 'src/stores/interface.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useOAuthStore } from 'src/stores/oauth.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useProfileConfigStore } from 'src/stores/profile_config.js' import { useUsersStore } from 'src/stores/users.js' import { updateProfile } from 'src/api/user.js' @@ -109,6 +110,9 @@ const ComposingTab = { FontControl, }, computed: { + defaultScope() { + return useProfileConfigStore().config.defaultScope + }, postFormats() { return useInstanceCapabilitiesStore().postFormats }, @@ -135,7 +139,7 @@ const ComposingTab = { }, methods: { changeDefaultScope(value) { - this.$store.dispatch('setProfileOption', { name: 'defaultScope', value }) + useProfileConfigStore().setProfileOption({ name: 'defaultScope', value }) }, clearCache(key) { clearCache(key) diff --git a/src/components/settings_modal/tabs/composing_tab.vue b/src/components/settings_modal/tabs/composing_tab.vue index b8c156bd3..c110981e6 100644 --- a/src/components/settings_modal/tabs/composing_tab.vue +++ b/src/components/settings_modal/tabs/composing_tab.vue @@ -11,10 +11,10 @@ diff --git a/src/modules/index.js b/src/modules/index.js index 61b7fd996..436d7e844 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -1,7 +1,5 @@ import drafts from './drafts.js' -import profileConfig from './profileConfig.js' export default { - profileConfig, drafts, } diff --git a/src/stores/follow_requests.js b/src/stores/follow_requests.js index 064d57253..27f8cbc00 100644 --- a/src/stores/follow_requests.js +++ b/src/stores/follow_requests.js @@ -32,6 +32,6 @@ export const useFollowRequestsStore = defineStore('followRequests', { }, remove(id) { this.requests.delete(id) - } + }, }, }) diff --git a/src/modules/profileConfig.js b/src/stores/profile_config.js similarity index 62% rename from src/modules/profileConfig.js rename to src/stores/profile_config.js index 8b9c77425..7533575d0 100644 --- a/src/modules/profileConfig.js +++ b/src/stores/profile_config.js @@ -1,34 +1,36 @@ import { get, set } from 'lodash' +import { defineStore } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' import { useUsersStore } from 'src/stores/users.js' -import { updateNotificationSettings, updateProfile } from 'src/api/user.js' +import { updateNotificationSettings, updateProfileJSON } from 'src/api/user.js' -const defaultApi = ({ rootState, commit }, { path, value }) => { +const defaultApi = async ({ path, value }) => { const params = {} set(params, path, value) - return updateProfile({ + + return await updateProfileJSON({ params, credentials: useOAuthStore().token, - }).then((result) => { - useUsersStore().addNewUsers(result) }) } -const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => { +const notificationsApi = async ({ path, value, oldValue }) => { const settings = {} set(settings, path, value) - return updateNotificationSettings({ + + const result = await updateNotificationSettings({ settings, credentials: useOAuthStore().token, - }).then(({ data: result }) => { - if (result.status === 'success') { - commit('confirmProfileOption', { name, value }) - } else { - commit('confirmProfileOption', { name, value: oldValue }) - } }) + + if (result.data.status === 'success') { + // a bit of a hack + return { ...result, success: true } + } else { + throw new Error('Failed updating notification settings', result) + } } /** @@ -84,60 +86,65 @@ export const settingsMap = { // NotificationSettingsAPIs webPushHideContents: { get: 'pleroma.notification_settings.hide_notification_contents', - set: 'hide_notification_contents', + set: 'hideNotificationContents', api: notificationsApi, }, blockNotificationsFromStrangers: { get: 'pleroma.notification_settings.block_from_strangers', - set: 'block_from_strangers', + set: 'blockFromStrangers', api: notificationsApi, }, } -export const defaultState = Object.fromEntries( - Object.keys(settingsMap).map((key) => [key, null]), -) +export const defaultState = () => ({ + config: Object.fromEntries(Object.keys(settingsMap).map((key) => [key, null])) +}) -const profileConfig = { - state: { ...defaultState }, - mutations: { - confirmProfileOption(state, { name, value }) { - set(state, name, value) - }, - wipeProfileOption(state, { name }) { - set(state, name, null) - }, - wipeAllProfileOptions(state) { - Object.keys(settingsMap).forEach((key) => { - set(state, key, null) - }) +export const useProfileConfigStore = defineStore('profileConfig', { + state: defaultState, + actions: { + confirmProfileOption({ name, value }) { + set(this.config, name, value) }, // Set the settings based on their path location - setCurrentUser(state, user) { + async setProfileOption({ name, value }) { + const oldValue = get(this, name) + const map = settingsMap[name] + + if (!map) throw new Error('Invalid server-side setting') + const { set: path = map, api = defaultApi } = map + set(this.config, name, null) + + try { + const result = await api({ path, value, oldValue }) + const { success } = result + if (success) { + set(this.config, name, value) + return + } + + useUsersStore().addNewUsers(result) + this.update(user) + } catch (e) { + console.warn('Error setting server-side option:', e) + + set(this.config, name, oldValue) + } + }, + update(user) { Object.entries(settingsMap).forEach((map) => { const [name, value] = map const { get: path = value } = value - set(state, name, get(user._original, path)) + set(this.config, name, get(user._original, path)) + }) + }, + onLogin(user) { + this.update(user) + }, + onLogout() { + Object.keys(settingsMap).forEach((key) => { + set(this.config, key, null) }) }, }, - actions: { - setProfileOption({ rootState, state, commit }, { name, value }) { - const oldValue = get(state, name) - const map = settingsMap[name] - if (!map) throw new Error('Invalid server-side setting') - const { set: path = map, api = defaultApi } = map - commit('wipeProfileOption', { name }) - - api({ rootState, commit }, { path, value, oldValue }).catch((e) => { - console.warn('Error setting server-side option:', e) - commit('confirmProfileOption', { name, value: oldValue }) - }) - }, - logout({ commit }) { - commit('wipeAllProfileOptions') - }, - }, -} - -export default profileConfig +}) diff --git a/src/stores/shout.js b/src/stores/shout.js index f76b37f5e..487b04c4d 100644 --- a/src/stores/shout.js +++ b/src/stores/shout.js @@ -1,6 +1,6 @@ import { Socket } from 'phoenix' - import { defineStore } from 'pinia' + import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useUsersStore } from 'src/stores/users.js' @@ -14,7 +14,7 @@ export const useShoutStore = defineStore('shout', { socket: null, }), getters: { - token: () => useUsersStore().currentUser?.token + token: () => useUsersStore().currentUser?.token, }, actions: { initializeSocket() { @@ -50,6 +50,6 @@ export const useShoutStore = defineStore('shout', { disconnectSocket() { this.socket?.disconnect() this.socket = null - } + }, }, }) diff --git a/src/stores/users.js b/src/stores/users.js index 87b177dfa..da35dc3a0 100644 --- a/src/stores/users.js +++ b/src/stores/users.js @@ -11,13 +11,14 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useListsStore } from 'src/stores/lists.js' -import { useShoutStore } from 'src/stores/shout.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useNotificationsStore } from 'src/stores/notifications.js' import { useOAuthStore } from 'src/stores/oauth.js' +import { useShoutStore } from 'src/stores/shout.js' import { useStatusesStore } from 'src/stores/statuses.js' import { useStreamingStore } from 'src/stores/streaming.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' +import { useProfileConfigStore } from 'src/stores/profile_config.js' import { useTimelinesStore } from 'src/stores/timelines.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' @@ -142,6 +143,7 @@ export const useUsersStore = defineStore('users', { if (user.id === this.currentUser?.id) { this.currentUser = reactive + useProfileConfigStore().update(reactive) } // Initialize some stuff @@ -648,6 +650,7 @@ export const useUsersStore = defineStore('users', { console.error('Error setting theme', e) }) }) + useProfileConfigStore().onLogin(user) useUserHighlightStore().initUserHighlight(user) @@ -780,6 +783,7 @@ export const useUsersStore = defineStore('users', { Cookies.remove('__Host-pleroma_key', { path: '/' }) useInterfaceStore().onLogout() + useProfileConfigStore().onLogout() }) .catch((e) => { useInterfaceStore().pushGlobalNotice({ From b864dfb0569bf66fbda07a52000e6e15b18325ca Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 1 Sep 2026 18:14:08 +0300 Subject: [PATCH 07/10] whoops --- src/stores/users.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/stores/users.js b/src/stores/users.js index d3c8ad6cf..7c87d4f7f 100644 --- a/src/stores/users.js +++ b/src/stores/users.js @@ -274,9 +274,8 @@ export const useUsersStore = defineStore('users', { map.set(identifier, promise) - const result = await promise - try { + const result = await promise if (result) { const { id, screen_name } = result From 7b037ef60f91dc5760bebbc38ed1edffe61b8d22 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 1 Sep 2026 18:21:27 +0300 Subject: [PATCH 08/10] invert check --- src/stores/streaming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/streaming.js b/src/stores/streaming.js index dae047f46..7fa00bdbc 100644 --- a/src/stores/streaming.js +++ b/src/stores/streaming.js @@ -235,7 +235,7 @@ export const useStreamingStore = defineStore('streaming', { ) setTimeout(() => { - if (this.retrying) return // retry aborted (i.e. due to logout) + if (!this.retrying) return // retry aborted (i.e. due to logout) this.initSocket() }, retryTimeout(this.retryMultiplier)) From c8593a9668fed6b444ab380e8d0b0c7541660e30 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 1 Sep 2026 18:36:57 +0300 Subject: [PATCH 09/10] P3 --- src/stores/streaming.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/stores/streaming.js b/src/stores/streaming.js index 7fa00bdbc..a1cf0b885 100644 --- a/src/stores/streaming.js +++ b/src/stores/streaming.js @@ -108,7 +108,14 @@ export const useStreamingStore = defineStore('streaming', { } }, initSocket(initial) { - if (this.socket) throw new Error('Socket already exists!') + if (this.socket) { + console.error("Socket already exists! This shouldn't happen! Force-closing the socket just in case") + try { + this.socket.close() + } catch (e) { + console.error("Error closing existing socket that shouldn't be there. Welp. Moving on.", e) + } + } this.state = initial ? WSConnectionStatus.STARTING_INITIAL @@ -130,7 +137,8 @@ export const useStreamingStore = defineStore('streaming', { this.socket.addEventListener('error', this.onError) }, stopSocket() { - this.socket.close() + if (this.socket == null) console.error("Socket is already stopped! This shouldn't happen!") + this.socket?.close() this.socket = null this.state = WSConnectionStatus.CLOSED this.retrying = false From 7b58a96ecee24e97fe6e5daf7bba6484449e403a Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Tue, 1 Sep 2026 18:46:50 +0300 Subject: [PATCH 10/10] lint --- src/stores/streaming.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/stores/streaming.js b/src/stores/streaming.js index a1cf0b885..554d6a6b5 100644 --- a/src/stores/streaming.js +++ b/src/stores/streaming.js @@ -109,11 +109,16 @@ export const useStreamingStore = defineStore('streaming', { }, initSocket(initial) { if (this.socket) { - console.error("Socket already exists! This shouldn't happen! Force-closing the socket just in case") + console.error( + "Socket already exists! This shouldn't happen! Force-closing the socket just in case", + ) try { - this.socket.close() + this.socket.close() } catch (e) { - console.error("Error closing existing socket that shouldn't be there. Welp. Moving on.", e) + console.error( + "Error closing existing socket that shouldn't be there. Welp. Moving on.", + e, + ) } } @@ -137,7 +142,8 @@ export const useStreamingStore = defineStore('streaming', { this.socket.addEventListener('error', this.onError) }, stopSocket() { - if (this.socket == null) console.error("Socket is already stopped! This shouldn't happen!") + if (this.socket == null) + console.error("Socket is already stopped! This shouldn't happen!") this.socket?.close() this.socket = null this.state = WSConnectionStatus.CLOSED