diff --git a/.babelrc b/.babelrc index 48f99d6ca..4ec104161 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,5 @@ { "presets": ["@babel/preset-env"], - "plugins": ["@babel/plugin-transform-runtime", "@vue/babel-plugin-jsx"], + "plugins": ["@babel/plugin-transform-runtime", "lodash", "@vue/babel-plugin-jsx"], "comments": true } diff --git a/package.json b/package.json index c10f86b84..50138e6a2 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@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", @@ -96,7 +97,7 @@ "function-bind": "1.1.2", "http-proxy-middleware": "3.0.5", "iso-639-1": "3.1.5", - "lodash-es": "4.17.21", + "lodash": "4.17.21", "msw": "2.14.6", "nightwatch": "3.12.2", "oxc": "^1.0.1", diff --git a/src/api/user.js b/src/api/user.js index 430b2c895..ec1763bb4 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -17,8 +17,7 @@ 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 = ({ blockFromStrangers, hideNotificationContents }) => - `/api/pleroma/notification_settings${paramsString({ blockFromStrangers, hideNotificationContents })}` +const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' export const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' @@ -433,9 +432,10 @@ export const exportFriends = ({ id, credentials }) => { // #Profile settings export const updateNotificationSettings = ({ credentials, settings }) => { return promisedRequest({ - url: NOTIFICATION_SETTINGS_URL(settings), + url: NOTIFICATION_SETTINGS_URL, credentials, method: 'PUT', + payload: settings, }) } diff --git a/src/components/extra_notifications/extra_notifications.js b/src/components/extra_notifications/extra_notifications.js index d20cbb67e..851656ae5 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.followRequestsCount + this.followRequestCount ) }, 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 329c5c1c3..388435950 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: followRequestsCount }, followRequestsCount) }} + {{ $t('notifications.unread_follow_requests', { num: followRequestCount }, followRequestCount) }} { - 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,13 +70,15 @@ const FollowRequestCard = { } }, doDeny() { + const notifId = this.findFollowRequestNotificationId() + denyUser({ id: this.user.id, credentials: useOAuthStore().token, }).then(() => { - const notifId = this.findFollowRequestNotificationId() - useFollowRequestsStore().remove(this.user.id) - notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId) + useNotificationsStore().dismissNotificationLocal(notifId) + // TODO fix + this.$store.dispatch('removeFollowRequest', this.user) }) this.hideDenyConfirmDialog() }, diff --git a/src/components/follow_requests/follow_requests.js b/src/components/follow_requests/follow_requests.js index 181d5605e..513298afc 100644 --- a/src/components/follow_requests/follow_requests.js +++ b/src/components/follow_requests/follow_requests.js @@ -1,14 +1,12 @@ import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue' -import { useFollowRequestsStore } from 'src/stores/follow_requests.js' - const FollowRequests = { components: { FollowRequestCard, }, computed: { requests() { - return useFollowRequestsStore().requests.values() + return this.$store.state.api.followRequests }, }, } diff --git a/src/components/mobile_nav/mobile_nav.js b/src/components/mobile_nav/mobile_nav.js index e7fe404e0..97645ca73 100644 --- a/src/components/mobile_nav/mobile_nav.js +++ b/src/components/mobile_nav/mobile_nav.js @@ -10,7 +10,6 @@ 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' @@ -68,10 +67,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 f5de8bb2c..c3e760efb 100644 --- a/src/components/nav_panel/nav_panel.js +++ b/src/components/nav_panel/nav_panel.js @@ -1,4 +1,5 @@ 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' @@ -10,7 +11,6 @@ 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,7 +130,9 @@ const NavPanel = { new Set(store.prefsStorage.collections.pinnedNavItems), }), ...mapState(useUsersStore, ['currentUser']), - ...mapState(useFollowRequestsStore, ['followRequestsCount']), + ...mapVuexState({ + followRequestCount: (state) => state.api.followRequests.length, + }), ...mapState(useChatsStore, ['unreadChatsCount']), timelinesItems() { return filterNavigation( diff --git a/src/components/navigation/navigation.js b/src/components/navigation/navigation.js index 7a2e29646..39fa2c993 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: 'followRequestsCount', + badgeGetter: 'followRequestCount', }, about: { route: 'about', diff --git a/src/components/navigation/navigation_pins.js b/src/components/navigation/navigation_pins.js index f16336ef3..2efa94ee4 100644 --- a/src/components/navigation/navigation_pins.js +++ b/src/components/navigation/navigation_pins.js @@ -1,4 +1,5 @@ import { mapState } from 'pinia' +import { mapState as mapVuexState } from 'vuex' import { filterNavigation, @@ -13,7 +14,6 @@ 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,7 +78,9 @@ const NavPanel = { 'localBubble', ]), ...mapState(useUsersStore, ['currentUser']), - ...mapState(useFollowRequestsStore, ['followRequestsCount']), + ...mapVuexState({ + followRequestCount: (state) => state.api.followRequests.length, + }), pinnedList() { if (!this.currentUser) { return filterNavigation( diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 734671521..2272c78af 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -14,7 +14,6 @@ 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' @@ -108,10 +107,10 @@ const Notifications = { }, extraNotificationsCount() { return countExtraNotifications( + this.$store, useMergedConfigStore().mergedConfig, useChatsStore().unreadChatsCount, useAnnouncementsStore().unreadAnnouncementCount, - useFollowRequestsStore().followRequestsCount, ) }, unseenCountTitle() { diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index d0dce5950..a1a946fca 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -9,7 +9,6 @@ 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: { @@ -237,7 +236,7 @@ export default { configSource() { switch (this.realSource) { case 'profile': - return useProfileConfigStore().config + return this.$store.state.profileConfig case 'admin': return useAdminSettingsStore().config default: @@ -254,7 +253,7 @@ export default { switch (this.realSource) { case 'profile': return (k, v) => - useProfileConfigStore().setProfileOption({ name: k, value: v }) + this.$store.dispatch('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 74f01a2e1..3e734e6e9 100644 --- a/src/components/settings_modal/tabs/composing_tab.js +++ b/src/components/settings_modal/tabs/composing_tab.js @@ -17,7 +17,6 @@ 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' @@ -110,9 +109,6 @@ const ComposingTab = { FontControl, }, computed: { - defaultScope() { - return useProfileConfigStore().config.defaultScope - }, postFormats() { return useInstanceCapabilitiesStore().postFormats }, @@ -139,7 +135,7 @@ const ComposingTab = { }, methods: { changeDefaultScope(value) { - useProfileConfigStore().setProfileOption({ name: 'defaultScope', value }) + this.$store.dispatch('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 c110981e6..b8c156bd3 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/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js index 40d922228..2d2886d58 100644 --- a/src/components/side_drawer/side_drawer.js +++ b/src/components/side_drawer/side_drawer.js @@ -8,7 +8,6 @@ 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' @@ -86,6 +85,9 @@ const SideDrawer = { unseenNotificationsCount() { return this.unseenNotifications.length }, + followRequestCount() { + return this.$store.state.api.followRequests.length + }, timelinesRoute() { let name if (useInterfaceStore().lastTimeline) { @@ -98,7 +100,6 @@ 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 a42287b3c..c810d93a0 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") }} - {{ followRequestsCount }} + {{ followRequestCount }} diff --git a/src/modules/api.js b/src/modules/api.js new file mode 100644 index 000000000..290fbfc27 --- /dev/null +++ b/src/modules/api.js @@ -0,0 +1,79 @@ +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 }) { + 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 + }, + 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) + }, + 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 436d7e844..6aa236257 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -1,5 +1,9 @@ +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/profile_config.js b/src/modules/profileConfig.js similarity index 62% rename from src/stores/profile_config.js rename to src/modules/profileConfig.js index 7533575d0..8b9c77425 100644 --- a/src/stores/profile_config.js +++ b/src/modules/profileConfig.js @@ -1,36 +1,34 @@ 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, updateProfileJSON } from 'src/api/user.js' +import { updateNotificationSettings, updateProfile } from 'src/api/user.js' -const defaultApi = async ({ path, value }) => { +const defaultApi = ({ rootState, commit }, { path, value }) => { const params = {} set(params, path, value) - - return await updateProfileJSON({ + return updateProfile({ params, credentials: useOAuthStore().token, + }).then((result) => { + useUsersStore().addNewUsers(result) }) } -const notificationsApi = async ({ path, value, oldValue }) => { +const notificationsApi = ({ rootState, commit }, { path, value, oldValue }) => { const settings = {} set(settings, path, value) - - const result = await updateNotificationSettings({ + return 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) - } } /** @@ -86,65 +84,60 @@ export const settingsMap = { // NotificationSettingsAPIs webPushHideContents: { get: 'pleroma.notification_settings.hide_notification_contents', - set: 'hideNotificationContents', + set: 'hide_notification_contents', api: notificationsApi, }, blockNotificationsFromStrangers: { get: 'pleroma.notification_settings.block_from_strangers', - set: 'blockFromStrangers', + set: 'block_from_strangers', api: notificationsApi, }, } -export const defaultState = () => ({ - config: Object.fromEntries(Object.keys(settingsMap).map((key) => [key, null])) -}) +export const defaultState = Object.fromEntries( + Object.keys(settingsMap).map((key) => [key, null]), +) -export const useProfileConfigStore = defineStore('profileConfig', { - state: defaultState, - actions: { - confirmProfileOption({ name, value }) { - set(this.config, name, value) +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) + }) }, // Set the settings based on their path location - 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) { + setCurrentUser(state, user) { Object.entries(settingsMap).forEach((map) => { const [name, value] = map const { get: path = value } = value - set(this.config, name, get(user._original, path)) - }) - }, - onLogin(user) { - this.update(user) - }, - onLogout() { - Object.keys(settingsMap).forEach((key) => { - set(this.config, key, null) + set(state, name, get(user._original, path)) }) }, }, -}) + 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/services/follow_request_fetcher/follow_request_fetcher.service.js b/src/services/follow_request_fetcher/follow_request_fetcher.service.js new file mode 100644 index 000000000..492c4e648 --- /dev/null +++ b/src/services/follow_request_fetcher/follow_request_fetcher.service.js @@ -0,0 +1,33 @@ +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 803a56322..1fdb7bec1 100644 --- a/src/services/notification_utils/notification_utils.js +++ b/src/services/notification_utils/notification_utils.js @@ -98,11 +98,13 @@ export const unseenNotifications = ( } export const countExtraNotifications = ( + store, mergedConfig, unreadChatsCount, - unreadAnnouncementsCount, - followRequestsCount, + unreadAnnouncementCount, ) => { + const rootGetters = store.rootGetters || store.getters + if (!mergedConfig.showExtraNotifications) { return 0 } @@ -110,10 +112,10 @@ export const countExtraNotifications = ( return [ mergedConfig.showChatsInExtraNotifications ? unreadChatsCount : 0, mergedConfig.showAnnouncementsInExtraNotifications - ? unreadAnnouncementsCount + ? unreadAnnouncementCount : 0, mergedConfig.showFollowRequestsInExtraNotifications - ? followRequestsCount + ? rootGetters.followRequestCount : 0, ].reduce((a, c) => a + c, 0) } diff --git a/src/stores/fetchers/follow_requests.js b/src/stores/fetchers/follow_requests.js deleted file mode 100644 index 8794ab07e..000000000 --- a/src/stores/fetchers/follow_requests.js +++ /dev/null @@ -1,44 +0,0 @@ -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 deleted file mode 100644 index 27f8cbc00..000000000 --- a/src/stores/follow_requests.js +++ /dev/null @@ -1,37 +0,0 @@ -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/shout.js b/src/stores/shout.js index 487b04c4d..79268bd57 100644 --- a/src/stores/shout.js +++ b/src/stores/shout.js @@ -1,33 +1,14 @@ -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: { - 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') - + initializeShout(socket) { + const channel = socket.channel('chat:public') channel.joinPush.receive('ok', () => { this.joined = true }) @@ -47,9 +28,5 @@ export const useShoutStore = defineStore('shout', { channel.join() this.channel = channel }, - disconnectSocket() { - this.socket?.disconnect() - this.socket = null - }, }, }) diff --git a/src/stores/streaming.js b/src/stores/streaming.js index 554d6a6b5..dae047f46 100644 --- a/src/stores/streaming.js +++ b/src/stores/streaming.js @@ -108,19 +108,7 @@ 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", - ) - try { - this.socket.close() - } catch (e) { - console.error( - "Error closing existing socket that shouldn't be there. Welp. Moving on.", - e, - ) - } - } + if (this.socket) throw new Error('Socket already exists!') this.state = initial ? WSConnectionStatus.STARTING_INITIAL @@ -142,9 +130,7 @@ 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!") - this.socket?.close() + this.socket.close() this.socket = null this.state = WSConnectionStatus.CLOSED this.retrying = false @@ -249,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)) diff --git a/src/stores/users.js b/src/stores/users.js index c9445a65d..d3c8ad6cf 100644 --- a/src/stores/users.js +++ b/src/stores/users.js @@ -6,7 +6,6 @@ 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' @@ -14,11 +13,9 @@ import { useListsStore } from 'src/stores/lists.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' @@ -143,7 +140,6 @@ export const useUsersStore = defineStore('users', { if (user.id === this.currentUser?.id) { this.currentUser = reactive - useProfileConfigStore().update(reactive) } // Initialize some stuff @@ -278,8 +274,9 @@ 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 @@ -649,7 +646,6 @@ export const useUsersStore = defineStore('users', { console.error('Error setting theme', e) }) }) - useProfileConfigStore().onLogin(user) useUserHighlightStore().initUserHighlight(user) @@ -672,8 +668,8 @@ export const useUsersStore = defineStore('users', { if (user.token) { // Shoutbox - useShoutStore().initializeSocket() - useShoutStore().initializeShout() + dispatch('setWsToken', user.token) + dispatch('initializeSocket') } // DMs and Home @@ -690,7 +686,6 @@ export const useUsersStore = defineStore('users', { if (user.locked) { dispatch('startFetchingFollowRequests') - useFollowRequestsStore().startFetching() } if (useMergedConfigStore().mergedConfig.useStreamingApi) { @@ -735,9 +730,6 @@ export const useUsersStore = defineStore('users', { useListsStore().stopFetching() useBookmarkFoldersStore().stopFetching() useChatsStore().stopFetching() - if (this.currentUser.locked) { - useFollowRequestsStore().stopFetching() - } store?.dispatch('stopFetchingFollowRequests') @@ -756,7 +748,6 @@ export const useUsersStore = defineStore('users', { }) .then(() => { oauth.clearToken() - useShoutStore().disconnectSocket() this.currentUser = null @@ -782,7 +773,6 @@ export const useUsersStore = defineStore('users', { Cookies.remove('__Host-pleroma_key', { path: '/' }) useInterfaceStore().onLogout() - useProfileConfigStore().onLogout() }) .catch((e) => { useInterfaceStore().pushGlobalNotice({ diff --git a/yarn.lock b/yarn.lock index 4913bcfb7..8644d2d53 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.0.0", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -113,6 +113,17 @@ 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" @@ -270,6 +281,14 @@ "@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" @@ -460,6 +479,13 @@ 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" @@ -1105,6 +1131,15 @@ 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" @@ -1123,6 +1158,19 @@ "@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" @@ -1175,7 +1223,7 @@ "@babel/types" "^7.29.7" debug "^4.3.1" -"@babel/types@^7.25.9", "@babel/types@^7.4.4": +"@babel/types@^7.0.0-beta.49", "@babel/types@^7.25.9", "@babel/types@^7.27.0", "@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== @@ -1756,7 +1804,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.28": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@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== @@ -3345,6 +3393,17 @@ 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" @@ -5345,7 +5404,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@7.2.3, glob@^7.1.4, glob@^7.2.3: +glob@7.2.3, glob@^7.1.1, 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== @@ -5396,6 +5455,11 @@ 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" @@ -6379,11 +6443,6 @@ 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" @@ -6429,7 +6488,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.10, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7704,6 +7763,11 @@ 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"