Skip to content

Commit

Permalink
[PAY-1082] DMs: Dedupe sent messages (#3066)
Browse files Browse the repository at this point in the history
  • Loading branch information
rickyrombo committed Mar 22, 2023
1 parent 50a11c3 commit eb8d47e
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 392 deletions.
5 changes: 5 additions & 0 deletions packages/common/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"query-string": "8.1.0",
"reselect": "4.0.0",
"typed-redux-saga": "1.3.1",
"typesafe-actions": "5.1.0"
"typesafe-actions": "5.1.0",
"ulid": "2.3.0"
},
"devDependencies": {
"@rollup/plugin-image": "^2.1.1",
Expand Down
9 changes: 6 additions & 3 deletions packages/common/src/store/pages/chat/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChatEvents, type sdk } from '@audius/sdk'
import { Status } from 'models/Status'
import { Middleware } from 'redux'

import { actions as chatActions } from './slice'
Expand Down Expand Up @@ -30,7 +31,9 @@ export const chatMiddleware =
console.debug('[chats] WebSocket opened. Listening for chats...')
}
messageListener = ({ chatId, message }) => {
store.dispatch(addMessage({ chatId, message }))
store.dispatch(
addMessage({ chatId, message, status: Status.SUCCESS })
)
store.dispatch(incrementUnreadCount({ chatId }))
}
reactionListener = ({ chatId, messageId, reaction }) => {
Expand All @@ -42,9 +45,9 @@ export const chatMiddleware =
})
)
}
closeListener = () => {
closeListener = async () => {
console.debug('[chats] WebSocket closed. Reconnecting...')
sdk.chats.listen()
await sdk.chats.listen()
}
sdk.chats.addEventListener('open', openListener)
sdk.chats.addEventListener('message', messageListener)
Expand Down
46 changes: 14 additions & 32 deletions packages/common/src/store/pages/chat/sagas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChatMessage, ChatMessageRPC, TypedCommsResponse } from '@audius/sdk'
import { ChatMessage, TypedCommsResponse } from '@audius/sdk'
import dayjs from 'dayjs'
import { call, put, select, takeEvery, takeLatest } from 'typed-redux-saga'
import { ulid } from 'ulid'

import { getAccountUser, getUserId } from 'store/account/selectors'
import { setVisibility } from 'store/ui/modals/slice'
Expand All @@ -11,6 +12,7 @@ import { getContext } from '../../effects'

import * as chatSelectors from './selectors'
import { actions as chatActions } from './slice'
import { Status } from 'models/Status'

const {
createChat,
Expand All @@ -30,11 +32,10 @@ const {
markChatAsReadSucceeded,
markChatAsReadFailed,
sendMessage,
sendMessageSucceeded,
sendMessageFailed,
addMessage
} = chatActions
const { getChatsSummary, getChatMessagesSummary, getChat } = chatSelectors
const { getChatsSummary, getChat } = chatSelectors

function* doFetchMoreChats() {
try {
Expand Down Expand Up @@ -73,17 +74,14 @@ function* doFetchMoreMessages(action: ReturnType<typeof fetchMoreMessages>) {
// Ensure we get a chat so we can check the unread count
yield* call(fetchChatIfNecessary, { chatId })
const chat = yield* select((state) => getChat(state, chatId))
const summary = yield* select((state) =>
getChatMessagesSummary(state, chatId)
)

// Paginate through messages until we get to the unread indicator
let lastResponse: TypedCommsResponse<ChatMessage[]> | undefined
let before = summary?.prev_cursor
let before = chat?.messagesSummary?.prev_cursor
let hasMoreUnread = true
let data: ChatMessage[] = []
while (hasMoreUnread) {
const limit = 50
const limit = 10
const response = yield* call([sdk.chats, sdk.chats!.getMessages], {
chatId,
before,
Expand Down Expand Up @@ -208,10 +206,9 @@ function* doMarkChatAsRead(action: ReturnType<typeof markChatAsRead>) {
}
}

let tempMessageIdCounter = 1
function* doSendMessage(action: ReturnType<typeof sendMessage>) {
const { chatId, message } = action.payload
const temporaryMessageId = `temp-${tempMessageIdCounter++}`
const messageId = ulid()
try {
const audiusSdk = yield* getContext('audiusSdk')
const sdk = yield* call(audiusSdk)
Expand All @@ -227,38 +224,23 @@ function* doSendMessage(action: ReturnType<typeof sendMessage>) {
chatId,
message: {
sender_user_id: currentUserId,
message_id: temporaryMessageId,
message_id: messageId,
message,
reactions: [],
created_at: dayjs().toISOString()
}
},
status: Status.LOADING
})
)

const response = (yield* call([sdk.chats, sdk.chats.message], {
yield* call([sdk.chats, sdk.chats.message], {
chatId,
messageId,
message
})) as ChatMessageRPC

// After successful RPC, replace with real message
yield* put(
sendMessageSucceeded({
chatId,
oldMessageId: temporaryMessageId,
message: {
sender_user_id: currentUserId,
message_id: response.params.message_id,
message,
reactions: [],
created_at: dayjs().toISOString()
}
})
)
})
} catch (e) {
console.error('sendMessageFailed', e)
yield* put(
sendMessageFailed({ chatId, attemptedMessageId: temporaryMessageId })
)
yield* put(sendMessageFailed({ chatId, messageId }))
}
}

Expand Down
49 changes: 19 additions & 30 deletions packages/common/src/store/pages/chat/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,34 @@
import type { UserChat } from '@audius/sdk'
import { createSelector } from 'reselect'

import { Status } from 'models/Status'
import { accountSelectors } from 'store/account'
import { cacheUsersSelectors } from 'store/cache'
import { CommonState } from 'store/reducers'
import { decodeHashId } from 'utils/hashIds'
import { chatMessagesAdapter, chatsAdapter } from './slice'
const { getUserId } = accountSelectors
const { getUsers } = cacheUsersSelectors

const { selectById: selectChatById, selectAll: selectAllChats } =
chatsAdapter.getSelectors<CommonState>((state) => state.pages.chat.chats)

export const getChat = selectChatById

// Selectors for UserChat (all chats for a user)
export const getChatsStatus = (state: CommonState) =>
state.pages.chat.chatList.status
state.pages.chat.chats.status

export const getChatsSummary = (state: CommonState) =>
state.pages.chat.chatList.summary
state.pages.chat.chats.summary

export const getOptimisticReads = (state: CommonState) =>
state.pages.chat.optimisticChatRead

export const getAllChatMessages = (state: CommonState) =>
state.pages.chat.chatMessages

// Selectors for ChatMessage (specific chat conversations)
export const getChatMessagesSummary = (state: CommonState, chatId: string) =>
state.pages.chat.chatMessages[chatId]?.summary

export const getChatMessagesRaw = (state: CommonState, chatId: string) =>
state.pages.chat.chatMessages[chatId]?.data

export const getChatMessagesStatus = (state: CommonState, chatId: string) =>
state.pages.chat.chatMessages[chatId]?.status ?? Status.IDLE

export const getOptimisticReactions = (state: CommonState) =>
state.pages.chat.optimisticReactions

// Returns a UserChat that contains the ChatMessage with the given chatId
export const getChat = (state: CommonState, chatId?: string) =>
chatId ? state.pages.chat.chatList.map[chatId] : undefined

export const getChatsRaw = createSelector(
(state: CommonState) => state.pages.chat.chatList.order,
(state) => state.pages.chat.chatList.map,
(order, map) => {
return order.map((chatId) => map[chatId])
}
)

export const getChats = createSelector(
[getChatsRaw, getAllChatMessages, getOptimisticReads],
[selectAllChats, getOptimisticReads],
(chats, optimisticReads) => {
return chats?.map((chat) => {
// If have a clientside optimistic read status, override the server status
Expand All @@ -64,7 +44,16 @@ export const getChats = createSelector(
)

export const getChatMessages = createSelector(
[getChatMessagesRaw, getOptimisticReactions],
[
(state: CommonState, chatId: string) =>
chatMessagesAdapter
.getSelectors()
.selectAll(
state.pages.chat.messages[chatId] ??
chatMessagesAdapter.getInitialState()
),
getOptimisticReactions
],
(messages, optimisticReactions) => {
return messages?.map((message) => {
const optimisticReaction = optimisticReactions[message.message_id]
Expand Down
Loading

0 comments on commit eb8d47e

Please sign in to comment.