aboutsummaryrefslogtreecommitdiff
path: root/src/services/chat_service
diff options
context:
space:
mode:
authoreugenijm <eugenijm@protonmail.com>2020-10-29 13:33:06 +0300
committereugenijm <eugenijm@protonmail.com>2020-11-01 23:36:58 +0300
commite798e9a4177f025dda2b40d109fa40c2ebfd814e (patch)
treee9916a0cf615fbff6cc5296494476cea89ac7996 /src/services/chat_service
parent148789767a28a988751af9635cd53fe6d85a4c75 (diff)
Optimistic message sending for chat
Diffstat (limited to 'src/services/chat_service')
-rw-r--r--src/services/chat_service/chat_service.js63
1 files changed, 56 insertions, 7 deletions
diff --git a/src/services/chat_service/chat_service.js b/src/services/chat_service/chat_service.js
index 95c69482..815af82e 100644
--- a/src/services/chat_service/chat_service.js
+++ b/src/services/chat_service/chat_service.js
@@ -3,6 +3,7 @@ import _ from 'lodash'
const empty = (chatId) => {
return {
idIndex: {},
+ idempotencyKeyIndex: {},
messages: [],
newMessageCount: 0,
lastSeenTimestamp: 0,
@@ -13,8 +14,18 @@ const empty = (chatId) => {
}
const clear = (storage) => {
- storage.idIndex = {}
- storage.messages.splice(0, storage.messages.length)
+ const failedMessageIds = []
+
+ for (const message of storage.messages) {
+ if (message.error) {
+ failedMessageIds.push(message.id)
+ } else {
+ delete storage.idIndex[message.id]
+ delete storage.idempotencyKeyIndex[message.id]
+ }
+ }
+
+ storage.messages = storage.messages.filter(m => failedMessageIds.includes(m.id))
storage.newMessageCount = 0
storage.lastSeenTimestamp = 0
storage.minId = undefined
@@ -37,6 +48,25 @@ const deleteMessage = (storage, messageId) => {
}
}
+const handleMessageError = (storage, fakeId, isRetry) => {
+ if (!storage) { return }
+ const fakeMessage = storage.idIndex[fakeId]
+ if (fakeMessage) {
+ fakeMessage.error = true
+ fakeMessage.pending = false
+ if (!isRetry) {
+ // Ensure the failed message doesn't stay at the bottom of the list.
+ const lastPersistedMessage = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'desc'])[0]
+ if (lastPersistedMessage) {
+ const oldId = fakeMessage.id
+ fakeMessage.id = `${lastPersistedMessage.id}-${new Date().getTime()}`
+ storage.idIndex[fakeMessage.id] = fakeMessage
+ delete storage.idIndex[oldId]
+ }
+ }
+ }
+}
+
const add = (storage, { messages: newMessages, updateMaxId = true }) => {
if (!storage) { return }
for (let i = 0; i < newMessages.length; i++) {
@@ -45,7 +75,19 @@ const add = (storage, { messages: newMessages, updateMaxId = true }) => {
// sanity check
if (message.chat_id !== storage.chatId) { return }
- if (!storage.minId || message.id < storage.minId) {
+ if (message.fakeId) {
+ const fakeMessage = storage.idIndex[message.fakeId]
+ if (fakeMessage) {
+ Object.assign(fakeMessage, message, { error: false })
+ delete fakeMessage['fakeId']
+ storage.idIndex[fakeMessage.id] = fakeMessage
+ delete storage.idIndex[message.fakeId]
+
+ return
+ }
+ }
+
+ if (!storage.minId || (!message.pending && message.id < storage.minId)) {
storage.minId = message.id
}
@@ -55,16 +97,22 @@ const add = (storage, { messages: newMessages, updateMaxId = true }) => {
}
}
- if (!storage.idIndex[message.id]) {
+ if (!storage.idIndex[message.id] && !isConfirmation(storage, message)) {
if (storage.lastSeenTimestamp < message.created_at) {
storage.newMessageCount++
}
- storage.messages.push(message)
storage.idIndex[message.id] = message
+ storage.messages.push(storage.idIndex[message.id])
+ storage.idempotencyKeyIndex[message.idempotency_key] = true
}
}
}
+const isConfirmation = (storage, message) => {
+ if (!message.idempotency_key) return
+ return storage.idempotencyKeyIndex[message.idempotency_key]
+}
+
const resetNewMessageCount = (storage) => {
if (!storage) { return }
storage.newMessageCount = 0
@@ -76,7 +124,7 @@ const getView = (storage) => {
if (!storage) { return [] }
const result = []
- const messages = _.sortBy(storage.messages, ['id', 'desc'])
+ const messages = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'asc'])
const firstMessage = messages[0]
let previousMessage = messages[messages.length - 1]
let currentMessageChainId
@@ -148,7 +196,8 @@ const ChatService = {
getView,
deleteMessage,
resetNewMessageCount,
- clear
+ clear,
+ handleMessageError
}
export default ChatService