migrate chats module to pinia

This commit is contained in:
Henry Jameson 2026-07-08 20:29:59 +03:00
commit 5bc802932e
21 changed files with 192 additions and 198 deletions

View file

@ -1,9 +1,12 @@
import { mapGetters, mapState } from 'vuex'
import { mapState as mapPiniaState } from 'pinia'
import { mapState } from 'vuex'
import ChatListItem from 'src/components/chat_list_item/chat_list_item.vue'
import ChatNew from 'src/components/chat_new/chat_new.vue'
import List from 'src/components/list/list.vue'
import { useChatsStore } from 'src/stores/chats.js'
const ChatList = {
components: {
ChatListItem,
@ -14,7 +17,7 @@ const ChatList = {
...mapState({
currentUser: (state) => state.users.currentUser,
}),
...mapGetters(['sortedChatList']),
...mapPiniaState(useChatsStore, ['sortedChatList']),
},
data() {
return {
@ -22,12 +25,12 @@ const ChatList = {
}
},
created() {
this.$store.dispatch('fetchChats', { latest: true })
useChatsStore().fetchChats()
},
methods: {
cancelNewChat() {
this.isNew = false
this.$store.dispatch('fetchChats', { latest: true })
useChatsStore().fetchChats()
},
newChat() {
this.isNew = true

View file

@ -15,7 +15,11 @@ import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faEllipsisH, faTimes, faCircleNotch } from '@fortawesome/free-solid-svg-icons'
import {
faCircleNotch,
faEllipsisH,
faTimes,
} from '@fortawesome/free-solid-svg-icons'
library.add(faTimes, faEllipsisH, faCircleNotch)

View file

@ -1,6 +1,4 @@
import { orderBy, throttle, uniqueId } from 'lodash'
import { mapState as mapPiniaState } from 'pinia'
import { mapGetters, mapState } from 'vuex'
import { orderBy, uniqueId } from 'lodash'
import ChatMessage from 'src/components/chat_message/chat_message.vue'
@ -34,7 +32,6 @@ const ChatMessageList = {
const date = new Date(message.created_at)
const olderMessage = messages[index - 1]
const newerMessage = messages[index + 1]
const newerItem = acc[acc.length - 1]
const diff = olderMessage

View file

@ -1,7 +1,7 @@
import { throttle } from 'lodash'
import { nextTick } from 'vue'
import { maxBy, minBy, sortBy, throttle } from 'lodash'
import { mapState as mapPiniaState } from 'pinia'
import { mapGetters, mapState } from 'vuex'
import { nextTick } from 'vue'
import { mapState } from 'vuex'
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
import ChatTitle from 'src/components/chat_title/chat_title.vue'
@ -15,16 +15,17 @@ import {
isScrollable,
} from './chat_layout_utils.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import {
chatMessages,
deleteChatMessage,
getOrCreateChat,
readChat,
sendChatMessage,
deleteChatMessage,
} from 'src/api/chats.js'
import { WSConnectionStatus } from 'src/api/websocket.js'
@ -215,7 +216,7 @@ const Chat = {
credentials: useOAuthStore().token,
})
this.$store.commit('readChat', { id: this.chat.id, lastId: lastReadId })
useChatsStore().readChat(this.chat.id)
},
bottomedOut(offset) {
return isBottomedOut(offset)
@ -378,7 +379,10 @@ const Chat = {
// Sanity check
if (message.chat_id !== this.chat.id) {
console.warn(`Chat message doesn't belong to current chat (id: ${this.chat.id})!!`, message)
console.warn(
`Chat message doesn't belong to current chat (id: ${this.chat.id})!!`,
message,
)
return
}
@ -386,7 +390,10 @@ const Chat = {
if (message.idempotency_key) {
if (this.pendingMessagesIndex[message.idempotencyKeyIndex]) {
delete this.pendingMessagesIndex[message.idempotencyKeyIndex]
this.pendingMessages = this.pendingMessages.filter(({ idempotency_key }) => idempotency_key !== message.idempotency_key)
this.pendingMessages = this.pendingMessages.filter(
({ idempotency_key }) =>
idempotency_key !== message.idempotency_key,
)
}
}
@ -445,7 +452,7 @@ const Chat = {
retriesLeft: MAX_RETRIES,
})
},
async doSendMessage({ params, pendingId, retriesLeft = MAX_RETRIES }) {
async doSendMessage({ params, retriesLeft = MAX_RETRIES }) {
if (retriesLeft <= 0) return
try {
@ -458,7 +465,11 @@ const Chat = {
messages: [{ ...data }],
})
} catch (error) {
if (error.name !== 'StatusCodeError' || error.message === 'Failed to fetch') throw error
if (
error.name !== 'StatusCodeError' ||
error.message === 'Failed to fetch'
)
throw error
console.error('Error sending message', error)
this.handleMessageError({
@ -471,11 +482,10 @@ const Chat = {
(error.statusCode >= 500 && error.statusCode < 600) ||
error.message === 'Failed to fetch'
) {
this.messageRetriers[fakeMessage.id] = setTimeout(
this.messageRetriers[params.idempotencyKey] = setTimeout(
() => {
this.doSendMessage({
params,
fakeMessage,
retriesLeft: retriesLeft - 1,
})
},
@ -484,7 +494,7 @@ const Chat = {
}
}
},
handleMessageError(idempotencyKey, isRetry) {
handleMessageError(idempotencyKey, isRetry) {
const fakeMessage = this.pendingMessagesIndex[idempotencyKey]
if (fakeMessage) {

View file

@ -1,7 +1,8 @@
import { mapState as mapPiniaState } from 'pinia'
import { mapState } from 'pinia'
import { mapGetters } from 'vuex'
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
@ -21,7 +22,7 @@ const ExtraNotifications = {
return (
this.mergedConfig.showExtraNotifications &&
this.mergedConfig.showChatsInExtraNotifications &&
this.unreadChatCount
this.unreadChatsCount
)
},
shouldShowAnnouncements() {
@ -53,11 +54,12 @@ const ExtraNotifications = {
currentUser() {
return this.$store.state.users.currentUser
},
...mapGetters(['unreadChatCount', 'followRequestCount']),
...mapPiniaState(useAnnouncementsStore, {
...mapGetters(['followRequestCount']),
...mapState(useAnnouncementsStore, {
unreadAnnouncementCount: 'unreadAnnouncementCount',
}),
...mapPiniaState(useMergedConfigStore, ['mergedConfig']),
...mapState(useMergedConfigStore, ['mergedConfig']),
...mapState(useChatsStore, ['unreadChatsCount']),
},
methods: {
openNotificationSettings() {

View file

@ -14,7 +14,7 @@
class="fa-scale-110 icon"
icon="comments"
/>
{{ $t('notifications.unread_chats', { num: unreadChatCount }, unreadChatCount) }}
{{ $t('notifications.unread_chats', { num: unreadChatsCount }, unreadChatsCount) }}
</router-link>
</div>
<div

View file

@ -1,6 +1,5 @@
import { mapState } from 'pinia'
import { defineAsyncComponent } from 'vue'
import { mapGetters } from 'vuex'
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
import GestureService from '../../services/gesture_service/gesture_service'
@ -10,6 +9,7 @@ import {
} from '../../services/notification_utils/notification_utils'
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
@ -68,6 +68,7 @@ const MobileNav = {
countExtraNotifications(
this.$store,
useMergedConfigStore().mergedConfig,
useChatsStore().unreadChatsCount,
useAnnouncementsStore().unreadAnnouncementCount,
)
)
@ -87,18 +88,18 @@ const MobileNav = {
isChat() {
return this.$route.name === 'chat'
},
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
...mapState(useMergedConfigStore, {
pinnedItems: (store) =>
new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'),
}),
shouldConfirmLogout() {
return useMergedConfigStore().mergedConfig.modalOnLogout
},
closingDrawerMarksAsSeen() {
return useMergedConfigStore().mergedConfig.closingDrawerMarksAsSeen
},
...mapGetters(['unreadChatCount']),
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
...mapState(useMergedConfigStore, {
pinnedItems: (store) =>
new Set(store.prefsStorage.collections.pinnedNavItems).has('chats'),
}),
...mapState(useChatsStore, ['unreadChatsCount']),
},
methods: {
toggleMobileSidebar() {

View file

@ -19,7 +19,7 @@
icon="bars"
/>
<div
v-if="(unreadChatCount && !chatsPinned) || unreadAnnouncementCount"
v-if="(unreadChatsCount && !chatsPinned) || unreadAnnouncementCount"
class="badge -dot -notification"
/>
</button>

View file

@ -1,5 +1,5 @@
import { mapState as mapPiniaState } from 'pinia'
import { mapGetters, mapState } from 'vuex'
import { mapState } from 'vuex'
import BookmarkFoldersMenuContent from 'src/components/bookmark_folders_menu/bookmark_folders_menu_content.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
@ -10,6 +10,7 @@ import NavigationEntry from 'src/components/navigation/navigation_entry.vue'
import NavigationPins from 'src/components/navigation/navigation_pins.vue'
import { useAnnouncementsStore } from 'src/stores/announcements'
import { useChatsStore } from 'src/stores/chats.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useSyncConfigStore } from 'src/stores/sync_config.js'
@ -131,6 +132,7 @@ const NavPanel = {
currentUser: (state) => state.users.currentUser,
followRequestCount: (state) => state.api.followRequests.length,
}),
...mapPiniaState(useChatsStore, ['unreadChatsCount']),
timelinesItems() {
return filterNavigation(
Object.entries({ ...TIMELINES })
@ -162,7 +164,6 @@ const NavPanel = {
},
)
},
...mapGetters(['unreadChatCount']),
},
}

View file

@ -76,7 +76,7 @@ export const ROOT_ITEMS = {
icon: 'comments',
label: 'nav.chats',
badgeStyle: 'notification',
badgeGetter: 'unreadChatCount',
badgeGetter: 'unreadChatsCount',
criteria: ['chats'],
},
friendRequests: {

View file

@ -1,6 +1,5 @@
import { mapState } from 'pinia'
import { computed } from 'vue'
import { mapGetters } from 'vuex'
import ExtraNotifications from 'src/components/extra_notifications/extra_notifications.vue'
import Notification from 'src/components/notification/notification.vue'
@ -16,6 +15,7 @@ import notificationsFetcher from '../../services/notifications_fetcher/notificat
import NotificationFilters from './notification_filters.vue'
import { useAnnouncementsStore } from 'src/stores/announcements.js'
import { useChatsStore } from 'src/stores/chats.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
@ -115,13 +115,14 @@ const Notifications = {
return countExtraNotifications(
this.$store,
useMergedConfigStore().mergedConfig,
useChatsStore().unreadChatsCount,
useAnnouncementsStore().unreadAnnouncementCount,
)
},
unseenCountTitle() {
return (
this.unseenNotifications.length +
this.unreadChatCount +
this.unreadChatsCount +
this.unreadAnnouncementCount
)
},
@ -160,7 +161,7 @@ const Notifications = {
return !this.noExtra
},
...mapState(useAnnouncementsStore, ['unreadAnnouncementCount']),
...mapGetters(['unreadChatCount']),
...mapState(useChatsStore, ['unreadChatsCount']),
},
mounted() {
this.scrollerRef = this.$refs.root.closest('.column.-scrollable')

View file

@ -12,6 +12,7 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useShoutStore } from 'src/stores/shout'
import { useChatsStore } from 'src/stores/chats.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
@ -113,7 +114,8 @@ const SideDrawer = {
sitename: (store) => store.instanceIdentity.name,
hideSitename: (store) => store.instanceIdentity.hideSitename,
}),
...mapGetters(['unreadChatCount', 'draftCount']),
...mapState(useChatsStore, ['unreadChatsCount']),
...mapGetters(['draftCount']),
},
methods: {
toggleDrawer() {

View file

@ -106,10 +106,10 @@
icon="comments"
/> {{ $t("nav.chats") }}
<span
v-if="unreadChatCount"
v-if="unreadChatsCount"
class="badge -notification"
>
{{ unreadChatCount }}
{{ unreadChatsCount }}
</span>
</router-link>
</li>