migrate follow requests to separate pinia store

This commit is contained in:
Henry Jameson 2026-08-31 19:05:26 +03:00
commit 8a743c648c
17 changed files with 121 additions and 99 deletions

View file

@ -1,8 +1,8 @@
import { mapState } from 'pinia' import { mapState } from 'pinia'
import { mapGetters } from 'vuex'
import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js' import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInterfaceStore } from 'src/stores/interface.js' import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js'
@ -37,7 +37,7 @@ const ExtraNotifications = {
return ( return (
this.mergedConfig.showExtraNotifications && this.mergedConfig.showExtraNotifications &&
this.mergedConfig.showFollowRequestsInExtraNotifications && this.mergedConfig.showFollowRequestsInExtraNotifications &&
this.followRequestCount this.followRequestsCount
) )
}, },
hasAnythingToShow() { hasAnythingToShow() {
@ -55,12 +55,12 @@ const ExtraNotifications = {
currentUser() { currentUser() {
return useUsersStore().currentUser return useUsersStore().currentUser
}, },
...mapGetters(['followRequestCount']),
...mapState(useAnnouncementsStore, { ...mapState(useAnnouncementsStore, {
unreadAnnouncementCount: 'unreadAnnouncementCount', unreadAnnouncementCount: 'unreadAnnouncementCount',
}), }),
...mapState(useMergedConfigStore, ['mergedConfig']), ...mapState(useMergedConfigStore, ['mergedConfig']),
...mapState(useChatsStore, ['unreadChatsCount']), ...mapState(useChatsStore, ['unreadChatsCount']),
...mapState(useFollowRequestsStore, ['followRequestsCount']),
}, },
methods: { methods: {
openNotificationSettings() { openNotificationSettings() {

View file

@ -48,7 +48,7 @@
class="fa-scale-110 icon" class="fa-scale-110 icon"
icon="user-plus" icon="user-plus"
/> />
{{ $t('notifications.unread_follow_requests', { num: followRequestCount }, followRequestCount) }} {{ $t('notifications.unread_follow_requests', { num: followRequestsCount }, followRequestsCount) }}
</router-link> </router-link>
</div> </div>
<i18n-t <i18n-t

View file

@ -2,6 +2,7 @@ import { defineAsyncComponent } from 'vue'
import BasicUserCard from '../basic_user_card/basic_user_card.vue' import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js' import { useNotificationsStore } from 'src/stores/notifications.js'
import { useOAuthStore } from 'src/stores/oauth.js' import { useOAuthStore } from 'src/stores/oauth.js'
@ -54,12 +55,11 @@ const FollowRequestCard = {
approveUser({ approveUser({
id: this.user.id, id: this.user.id,
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}).then(() => {
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() this.hideApproveConfirmDialog()
}, },
denyUser() { denyUser() {
@ -70,15 +70,13 @@ const FollowRequestCard = {
} }
}, },
doDeny() { doDeny() {
const notifId = this.findFollowRequestNotificationId()
denyUser({ denyUser({
id: this.user.id, id: this.user.id,
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}).then(() => { }).then(() => {
useNotificationsStore().dismissNotificationLocal(notifId) const notifId = this.findFollowRequestNotificationId()
// TODO fix useFollowRequestsStore().remove(this.user.id)
this.$store.dispatch('removeFollowRequest', this.user) notifId && useNotificationsStore().markSingleNotificationAsSeen(notifId)
}) })
this.hideDenyConfirmDialog() this.hideDenyConfirmDialog()
}, },

View file

@ -1,3 +1,5 @@
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue' import FollowRequestCard from 'src/components/follow_request_card/follow_request_card.vue'
const FollowRequests = { const FollowRequests = {
@ -6,7 +8,7 @@ const FollowRequests = {
}, },
computed: { computed: {
requests() { requests() {
return this.$store.state.api.followRequests return useFollowRequestsStore().requests.values()
}, },
}, },
} }

View file

@ -10,6 +10,7 @@ import {
import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js' import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js' import { useNotificationsStore } from 'src/stores/notifications.js'
@ -67,10 +68,10 @@ const MobileNav = {
return ( return (
this.unseenNotifications.length + this.unseenNotifications.length +
countExtraNotifications( countExtraNotifications(
this.$store,
useMergedConfigStore().mergedConfig, useMergedConfigStore().mergedConfig,
useChatsStore().unreadChatsCount, useChatsStore().unreadChatsCount,
useAnnouncementsStore().unreadAnnouncementCount, useAnnouncementsStore().unreadAnnouncementCount,
useFollowRequestsStore().followRequestsCount,
) )
) )
}, },

View file

@ -1,5 +1,4 @@
import { mapState } from 'pinia' import { mapState } from 'pinia'
import { mapState as mapVuexState } from 'vuex'
import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue' import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
import Checkbox from 'src/components/checkbox/checkbox.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 { useAnnouncementsStore } from 'src/stores/announcements'
import { useChatsStore } from 'src/stores/chats.js' import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useSyncConfigStore } from 'src/stores/sync_config.js'
@ -130,9 +130,7 @@ const NavPanel = {
new Set(store.prefsStorage.collections.pinnedNavItems), new Set(store.prefsStorage.collections.pinnedNavItems),
}), }),
...mapState(useUsersStore, ['currentUser']), ...mapState(useUsersStore, ['currentUser']),
...mapVuexState({ ...mapState(useFollowRequestsStore, ['followRequestsCount']),
followRequestCount: (state) => state.api.followRequests.length,
}),
...mapState(useChatsStore, ['unreadChatsCount']), ...mapState(useChatsStore, ['unreadChatsCount']),
timelinesItems() { timelinesItems() {
return filterNavigation( return filterNavigation(

View file

@ -85,7 +85,7 @@ export const ROOT_ITEMS = {
label: 'nav.friend_requests', label: 'nav.friend_requests',
badgeStyle: 'notification', badgeStyle: 'notification',
criteria: ['lockedUser'], criteria: ['lockedUser'],
badgeGetter: 'followRequestCount', badgeGetter: 'followRequestsCount',
}, },
about: { about: {
route: 'about', route: 'about',

View file

@ -1,5 +1,4 @@
import { mapState } from 'pinia' import { mapState } from 'pinia'
import { mapState as mapVuexState } from 'vuex'
import { import {
filterNavigation, filterNavigation,
@ -14,6 +13,7 @@ import {
import { useAnnouncementsStore } from 'src/stores/announcements' import { useAnnouncementsStore } from 'src/stores/announcements'
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders' import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useListsStore } from 'src/stores/lists' import { useListsStore } from 'src/stores/lists'
@ -78,9 +78,7 @@ const NavPanel = {
'localBubble', 'localBubble',
]), ]),
...mapState(useUsersStore, ['currentUser']), ...mapState(useUsersStore, ['currentUser']),
...mapVuexState({ ...mapState(useFollowRequestsStore, ['followRequestsCount']),
followRequestCount: (state) => state.api.followRequests.length,
}),
pinnedList() { pinnedList() {
if (!this.currentUser) { if (!this.currentUser) {
return filterNavigation( return filterNavigation(

View file

@ -14,6 +14,7 @@ import NotificationFilters from './notification_filters.vue'
import { useAnnouncementsStore } from 'src/stores/announcements.js' import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js' import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInterfaceStore } from 'src/stores/interface.js' import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useNotificationsStore } from 'src/stores/notifications.js' import { useNotificationsStore } from 'src/stores/notifications.js'
@ -107,10 +108,10 @@ const Notifications = {
}, },
extraNotificationsCount() { extraNotificationsCount() {
return countExtraNotifications( return countExtraNotifications(
this.$store,
useMergedConfigStore().mergedConfig, useMergedConfigStore().mergedConfig,
useChatsStore().unreadChatsCount, useChatsStore().unreadChatsCount,
useAnnouncementsStore().unreadAnnouncementCount, useAnnouncementsStore().unreadAnnouncementCount,
useFollowRequestsStore().followRequestsCount,
) )
}, },
unseenCountTitle() { unseenCountTitle() {

View file

@ -8,6 +8,7 @@ import { unseenNotifications } from '../../services/notification_utils/notificat
import { useAnnouncementsStore } from 'src/stores/announcements' import { useAnnouncementsStore } from 'src/stores/announcements'
import { useChatsStore } from 'src/stores/chats.js' import { useChatsStore } from 'src/stores/chats.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
@ -85,9 +86,6 @@ const SideDrawer = {
unseenNotificationsCount() { unseenNotificationsCount() {
return this.unseenNotifications.length return this.unseenNotifications.length
}, },
followRequestCount() {
return this.$store.state.api.followRequests.length
},
timelinesRoute() { timelinesRoute() {
let name let name
if (useInterfaceStore().lastTimeline) { if (useInterfaceStore().lastTimeline) {
@ -100,6 +98,7 @@ const SideDrawer = {
return { name } return { name }
} }
}, },
...mapState(useFollowRequestsStore, ['followRequestsCount']),
...mapState(useAnnouncementsStore, [ ...mapState(useAnnouncementsStore, [
'supportsAnnouncements', 'supportsAnnouncements',
'unreadAnnouncementCount', 'unreadAnnouncementCount',

View file

@ -141,10 +141,10 @@
icon="user-plus" icon="user-plus"
/> {{ $t("nav.friend_requests") }} /> {{ $t("nav.friend_requests") }}
<span <span
v-if="followRequestCount > 0" v-if="followRequestsCount > 0"
class="badge -notification" class="badge -notification"
> >
{{ followRequestCount }} {{ followRequestsCount }}
</span> </span>
</router-link> </router-link>
</li> </li>

View file

@ -1,19 +1,12 @@
import { Socket } from 'phoenix' import { Socket } from 'phoenix'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useShoutStore } from 'src/stores/shout.js' import { useShoutStore } from 'src/stores/shout.js'
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
const api = { const api = {
state: { state: {
fetchers: {}, fetchers: {},
socket: null, socket: null,
followRequests: [],
},
getters: {
followRequestCount: (state) => state.followRequests.length,
}, },
mutations: { mutations: {
addFetcher(state, { fetcherName, fetcher }) { addFetcher(state, { fetcherName, fetcher }) {
@ -29,27 +22,8 @@ const api = {
setSocket(state, socket) { setSocket(state, socket) {
state.socket = socket state.socket = socket
}, },
setFollowRequests(state, value) {
state.followRequests = value
},
}, },
actions: { 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 // Pleroma websocket
setWsToken(store, token) { setWsToken(store, token) {
store.commit('setWsToken', token) store.commit('setWsToken', token)

View file

@ -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

View file

@ -98,13 +98,11 @@ export const unseenNotifications = (
} }
export const countExtraNotifications = ( export const countExtraNotifications = (
store,
mergedConfig, mergedConfig,
unreadChatsCount, unreadChatsCount,
unreadAnnouncementCount, unreadAnnouncementsCount,
followRequestsCount,
) => { ) => {
const rootGetters = store.rootGetters || store.getters
if (!mergedConfig.showExtraNotifications) { if (!mergedConfig.showExtraNotifications) {
return 0 return 0
} }
@ -112,10 +110,10 @@ export const countExtraNotifications = (
return [ return [
mergedConfig.showChatsInExtraNotifications ? unreadChatsCount : 0, mergedConfig.showChatsInExtraNotifications ? unreadChatsCount : 0,
mergedConfig.showAnnouncementsInExtraNotifications mergedConfig.showAnnouncementsInExtraNotifications
? unreadAnnouncementCount ? unreadAnnouncementsCount
: 0, : 0,
mergedConfig.showFollowRequestsInExtraNotifications mergedConfig.showFollowRequestsInExtraNotifications
? rootGetters.followRequestCount ? followRequestsCount
: 0, : 0,
].reduce((a, c) => a + c, 0) ].reduce((a, c) => a + c, 0)
} }

View file

@ -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

View file

@ -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)
}
},
})

View file

@ -6,6 +6,7 @@ import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js' import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js'
import { useChatsStore } from 'src/stores/chats.js' import { useChatsStore } from 'src/stores/chats.js'
import { useEmojiStore } from 'src/stores/emoji.js' import { useEmojiStore } from 'src/stores/emoji.js'
import { useFollowRequestsStore } from 'src/stores/follow_requests.js'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface.js' import { useInterfaceStore } from 'src/stores/interface.js'
@ -678,9 +679,10 @@ export const useUsersStore = defineStore('users', {
useListsStore().startFetching() useListsStore().startFetching()
useBookmarkFoldersStore().startFetching() useBookmarkFoldersStore().startFetching()
if (user.locked) { // if (user.locked) {
dispatch('startFetchingFollowRequests') dispatch('startFetchingFollowRequests')
} useFollowRequestsStore().startFetching()
// }
if (useMergedConfigStore().mergedConfig.useStreamingApi) { if (useMergedConfigStore().mergedConfig.useStreamingApi) {
useStreamingStore().initSocket(true) useStreamingStore().initSocket(true)
@ -724,6 +726,9 @@ export const useUsersStore = defineStore('users', {
useListsStore().stopFetching() useListsStore().stopFetching()
useBookmarkFoldersStore().stopFetching() useBookmarkFoldersStore().stopFetching()
useChatsStore().stopFetching() useChatsStore().stopFetching()
// if (this.currentUser.locked) {
useFollowRequestsStore().stopFetching()
// }
store?.dispatch('stopFetchingFollowRequests') store?.dispatch('stopFetchingFollowRequests')