diff options
Diffstat (limited to 'src/modules')
| -rw-r--r-- | src/modules/api.js | 24 | ||||
| -rw-r--r-- | src/modules/chats.js | 228 | ||||
| -rw-r--r-- | src/modules/config.js | 3 | ||||
| -rw-r--r-- | src/modules/instance.js | 1 | ||||
| -rw-r--r-- | src/modules/interface.js | 9 | ||||
| -rw-r--r-- | src/modules/statuses.js | 5 | ||||
| -rw-r--r-- | src/modules/users.js | 5 |
7 files changed, 270 insertions, 5 deletions
diff --git a/src/modules/api.js b/src/modules/api.js index 04ef6ab4..68402602 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -1,4 +1,5 @@ import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' +import { WSConnectionStatus } from '../services/api/api.service.js' import { Socket } from 'phoenix' const api = { @@ -7,6 +8,7 @@ const api = { fetchers: {}, socket: null, mastoUserSocket: null, + mastoUserSocketStatus: null, followRequests: [] }, mutations: { @@ -28,6 +30,9 @@ const api = { }, setFollowRequests (state, value) { state.followRequests = value + }, + setMastoUserSocketStatus (state, value) { + state.mastoUserSocketStatus = value } }, actions: { @@ -47,7 +52,7 @@ const api = { startMastoUserSocket (store) { return new Promise((resolve, reject) => { try { - const { state, dispatch, rootState } = store + const { state, commit, dispatch, rootState } = store const timelineData = rootState.statuses.timelines.friends state.mastoUserSocket = state.backendInteractor.startUserSocket({ store }) state.mastoUserSocket.addEventListener( @@ -66,11 +71,22 @@ const api = { showImmediately: timelineData.visibleStatuses.length === 0, timeline: 'friends' }) + } else if (message.event === 'pleroma:chat_update') { + dispatch('addChatMessages', { + chatId: message.chatUpdate.id, + messages: [message.chatUpdate.lastMessage] + }) + dispatch('updateChat', { chat: message.chatUpdate }) } } ) + state.mastoUserSocket.addEventListener('open', () => { + commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED) + }) state.mastoUserSocket.addEventListener('error', ({ detail: error }) => { console.error('Error in MastoAPI websocket:', error) + commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR) + dispatch('clearOpenedChats') }) state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => { const ignoreCodes = new Set([ @@ -84,8 +100,11 @@ const api = { console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`) dispatch('startFetchingTimeline', { timeline: 'friends' }) dispatch('startFetchingNotifications') + dispatch('startFetchingChats') dispatch('restartMastoUserSocket') } + commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED) + dispatch('clearOpenedChats') }) resolve() } catch (e) { @@ -99,12 +118,13 @@ const api = { return dispatch('startMastoUserSocket').then(() => { dispatch('stopFetchingTimeline', { timeline: 'friends' }) dispatch('stopFetchingNotifications') + dispatch('stopFetchingChats') }) }, stopMastoUserSocket ({ state, dispatch }) { dispatch('startFetchingTimeline', { timeline: 'friends' }) dispatch('startFetchingNotifications') - console.log(state.mastoUserSocket) + dispatch('startFetchingChats') state.mastoUserSocket.close() }, diff --git a/src/modules/chats.js b/src/modules/chats.js new file mode 100644 index 00000000..f868ca0c --- /dev/null +++ b/src/modules/chats.js @@ -0,0 +1,228 @@ +import Vue from 'vue' +import { find, omitBy, orderBy, sumBy } from 'lodash' +import chatService from '../services/chat_service/chat_service.js' +import { parseChat, parseChatMessage } from '../services/entity_normalizer/entity_normalizer.service.js' + +const emptyChatList = () => ({ + data: [], + idStore: {} +}) + +const defaultState = { + chatList: emptyChatList(), + chatListFetcher: null, + openedChats: {}, + openedChatMessageServices: {}, + fetcher: undefined, + currentChatId: null +} + +const getChatById = (state, id) => { + return find(state.chatList.data, { id }) +} + +const sortedChatList = (state) => { + return orderBy(state.chatList.data, ['updated_at'], ['desc']) +} + +const unreadChatCount = (state) => { + return sumBy(state.chatList.data, 'unread') +} + +const chats = { + state: { ...defaultState }, + getters: { + currentChat: state => state.openedChats[state.currentChatId], + currentChatMessageService: state => state.openedChatMessageServices[state.currentChatId], + findOpenedChatByRecipientId: state => recipientId => find(state.openedChats, c => c.account.id === recipientId), + sortedChatList, + unreadChatCount + }, + actions: { + // Chat list + startFetchingChats ({ dispatch, commit }) { + const fetcher = () => { + dispatch('fetchChats', { latest: true }) + } + fetcher() + commit('setChatListFetcher', { + fetcher: () => setInterval(() => { fetcher() }, 5000) + }) + }, + stopFetchingChats ({ commit }) { + commit('setChatListFetcher', { fetcher: undefined }) + }, + fetchChats ({ dispatch, rootState, commit }, params = {}) { + return rootState.api.backendInteractor.chats() + .then(({ chats }) => { + dispatch('addNewChats', { chats }) + return chats + }) + }, + addNewChats ({ rootState, commit, dispatch, rootGetters }, { chats }) { + commit('addNewChats', { dispatch, chats, rootGetters }) + }, + updateChat ({ commit }, { chat }) { + commit('updateChat', { chat }) + }, + + // Opened Chats + startFetchingCurrentChat ({ commit, dispatch }, { fetcher }) { + dispatch('setCurrentChatFetcher', { fetcher }) + }, + setCurrentChatFetcher ({ rootState, commit }, { fetcher }) { + commit('setCurrentChatFetcher', { fetcher }) + }, + addOpenedChat ({ rootState, commit, dispatch }, { chat }) { + commit('addOpenedChat', { dispatch, chat: parseChat(chat) }) + dispatch('addNewUsers', [chat.account]) + }, + addChatMessages ({ commit }, value) { + commit('addChatMessages', { commit, ...value }) + }, + resetChatNewMessageCount ({ commit }, value) { + commit('resetChatNewMessageCount', value) + }, + removeFromCurrentChatStatuses ({ commit }, { id }) { + commit('removeFromCurrentChatStatuses', id) + }, + clearCurrentChat ({ rootState, commit, dispatch }, value) { + commit('setCurrentChatId', { chatId: undefined }) + commit('setCurrentChatFetcher', { fetcher: undefined }) + }, + readChat ({ rootState, commit, dispatch }, { id, lastReadId }) { + dispatch('resetChatNewMessageCount') + commit('readChat', { id }) + rootState.api.backendInteractor.readChat({ id, lastReadId }) + }, + deleteChatMessage ({ rootState, commit }, value) { + rootState.api.backendInteractor.deleteChatMessage(value) + commit('deleteChatMessage', { commit, ...value }) + }, + resetChats ({ commit, dispatch }) { + dispatch('clearCurrentChat') + commit('resetChats', { commit }) + }, + clearOpenedChats ({ rootState, commit, dispatch, rootGetters }) { + commit('clearOpenedChats', { commit }) + } + }, + mutations: { + setChatListFetcher (state, { commit, fetcher }) { + const prevFetcher = state.chatListFetcher + if (prevFetcher) { + clearInterval(prevFetcher) + } + state.chatListFetcher = fetcher && fetcher() + }, + setCurrentChatFetcher (state, { fetcher }) { + const prevFetcher = state.fetcher + if (prevFetcher) { + clearInterval(prevFetcher) + } + state.fetcher = fetcher && fetcher() + }, + addOpenedChat (state, { _dispatch, chat }) { + state.currentChatId = chat.id + Vue.set(state.openedChats, chat.id, chat) + + if (!state.openedChatMessageServices[chat.id]) { + Vue.set(state.openedChatMessageServices, chat.id, chatService.empty(chat.id)) + } + }, + setCurrentChatId (state, { chatId }) { + state.currentChatId = chatId + }, + addNewChats (state, { _dispatch, chats, _rootGetters }) { + chats.forEach((updatedChat) => { + const chat = getChatById(state, updatedChat.id) + + if (chat) { + chat.lastMessage = updatedChat.lastMessage + chat.unread = updatedChat.unread + } else { + state.chatList.data.push(updatedChat) + Vue.set(state.chatList.idStore, updatedChat.id, updatedChat) + } + }) + }, + updateChat (state, { _dispatch, chat: updatedChat, _rootGetters }) { + const chat = getChatById(state, updatedChat.id) + if (chat) { + chat.lastMessage = updatedChat.lastMessage + chat.unread = updatedChat.unread + chat.updated_at = updatedChat.updated_at + } + if (!chat) { state.chatList.data.unshift(updatedChat) } + Vue.set(state.chatList.idStore, updatedChat.id, updatedChat) + }, + deleteChat (state, { _dispatch, id, _rootGetters }) { + state.chats.data = state.chats.data.filter(conversation => + conversation.last_status.id !== id + ) + state.chats.idStore = omitBy(state.chats.idStore, conversation => conversation.last_status.id === id) + }, + resetChats (state, { commit }) { + state.chatList = emptyChatList() + state.currentChatId = null + commit('setChatListFetcher', { fetcher: undefined }) + for (const chatId in state.openedChats) { + chatService.clear(state.openedChatMessageServices[chatId]) + Vue.delete(state.openedChats, chatId) + Vue.delete(state.openedChatMessageServices, chatId) + } + }, + setChatsLoading (state, { value }) { + state.chats.loading = value + }, + addChatMessages (state, { commit, chatId, messages }) { + const chatMessageService = state.openedChatMessageServices[chatId] + if (chatMessageService) { + chatService.add(chatMessageService, { messages: messages.map(parseChatMessage) }) + commit('refreshLastMessage', { chatId }) + } + }, + refreshLastMessage (state, { chatId }) { + const chatMessageService = state.openedChatMessageServices[chatId] + if (chatMessageService) { + const chat = getChatById(state, chatId) + if (chat) { + chat.lastMessage = chatMessageService.lastMessage + if (chatMessageService.lastMessage) { + chat.updated_at = chatMessageService.lastMessage.created_at + } + } + } + }, + deleteChatMessage (state, { commit, chatId, messageId }) { + const chatMessageService = state.openedChatMessageServices[chatId] + if (chatMessageService) { + chatService.deleteMessage(chatMessageService, messageId) + commit('refreshLastMessage', { chatId }) + } + }, + resetChatNewMessageCount (state, _value) { + const chatMessageService = state.openedChatMessageServices[state.currentChatId] + chatService.resetNewMessageCount(chatMessageService) + }, + // Used when a connection loss occurs + clearOpenedChats (state) { + const currentChatId = state.currentChatId + for (const chatId in state.openedChats) { + if (currentChatId !== chatId) { + chatService.clear(state.openedChatMessageServices[chatId]) + Vue.delete(state.openedChats, chatId) + Vue.delete(state.openedChatMessageServices, chatId) + } + } + }, + readChat (state, { id }) { + const chat = getChatById(state, id) + if (chat) { + chat.unread = 0 + } + } + } +} + +export default chats diff --git a/src/modules/config.js b/src/modules/config.js index 47b24d77..e0fe72df 100644 --- a/src/modules/config.js +++ b/src/modules/config.js @@ -46,7 +46,8 @@ export const defaultState = { repeats: true, moves: true, emojiReactions: false, - followRequest: true + followRequest: true, + chatMention: true }, webPushNotifications: false, muteWords: [], diff --git a/src/modules/instance.js b/src/modules/instance.js index 45a8eeca..3fe3bbf3 100644 --- a/src/modules/instance.js +++ b/src/modules/instance.js @@ -55,6 +55,7 @@ const defaultState = { // Feature-set, apparently, not everything here is reported... chatAvailable: false, + pleromaChatMessagesAvailable: false, gopherAvailable: false, mediaProxyAvailable: false, suggestionsEnabled: false, diff --git a/src/modules/interface.js b/src/modules/interface.js index e31630fc..ec08ac0a 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -15,7 +15,8 @@ const defaultState = { ) }, mobileLayout: false, - globalNotices: [] + globalNotices: [], + layoutHeight: 0 } const interfaceMod = { @@ -65,6 +66,9 @@ const interfaceMod = { }, removeGlobalNotice (state, notice) { state.globalNotices = state.globalNotices.filter(n => n !== notice) + }, + setLayoutHeight (state, value) { + state.layoutHeight = value } }, actions: { @@ -110,6 +114,9 @@ const interfaceMod = { }, removeGlobalNotice ({ commit }, notice) { commit('removeGlobalNotice', notice) + }, + setLayoutHeight ({ commit }, value) { + commit('setLayoutHeight', value) } } } diff --git a/src/modules/statuses.js b/src/modules/statuses.js index 7fbf685c..64f5b587 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -478,7 +478,7 @@ export const mutations = { }, setDeleted (state, { status }) { const newStatus = state.allStatusesObject[status.id] - newStatus.deleted = true + if (newStatus) newStatus.deleted = true }, setManyDeleted (state, condition) { Object.values(state.allStatusesObject).forEach(status => { @@ -521,6 +521,9 @@ export const mutations = { dismissNotification (state, { id }) { state.notifications.data = state.notifications.data.filter(n => n.id !== id) }, + dismissNotifications (state, { finder }) { + state.notifications.data = state.notifications.data.filter(n => finder) + }, updateNotification (state, { id, updater }) { const notification = find(state.notifications.data, n => n.id === id) notification && updater(notification) diff --git a/src/modules/users.js b/src/modules/users.js index 7e136c61..16c1e566 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -498,6 +498,7 @@ const users = { store.dispatch('stopFetchingFollowRequests') store.commit('clearNotifications') store.commit('resetStatuses') + store.dispatch('resetChats') }) }, loginUser (store, accessToken) { @@ -537,6 +538,9 @@ const users = { // Start fetching notifications store.dispatch('startFetchingNotifications') + + // Start fetching chats + store.dispatch('startFetchingChats') } if (store.getters.mergedConfig.useStreamingApi) { @@ -544,6 +548,7 @@ const users = { console.error('Failed initializing MastoAPI Streaming socket', error) startPolling() }).then(() => { + store.dispatch('fetchChats', { latest: true }) setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000) }) } else { |
