logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://anongit.hacktivis.me/git/pleroma-fe.git/

chat_service.js (6220B)


  1. import _ from 'lodash'
  2. const empty = (chatId) => {
  3. return {
  4. idIndex: {},
  5. idempotencyKeyIndex: {},
  6. messages: [],
  7. newMessageCount: 0,
  8. lastSeenMessageId: '0',
  9. chatId,
  10. minId: undefined,
  11. maxId: undefined
  12. }
  13. }
  14. const clear = (storage) => {
  15. const failedMessageIds = []
  16. for (const message of storage.messages) {
  17. if (message.error) {
  18. failedMessageIds.push(message.id)
  19. } else {
  20. delete storage.idIndex[message.id]
  21. delete storage.idempotencyKeyIndex[message.idempotency_key]
  22. }
  23. }
  24. storage.messages = storage.messages.filter(m => failedMessageIds.includes(m.id))
  25. storage.newMessageCount = 0
  26. storage.lastSeenMessageId = '0'
  27. storage.minId = undefined
  28. storage.maxId = undefined
  29. }
  30. const deleteMessage = (storage, messageId) => {
  31. if (!storage) { return }
  32. storage.messages = storage.messages.filter(m => m.id !== messageId)
  33. delete storage.idIndex[messageId]
  34. if (storage.maxId === messageId) {
  35. const lastMessage = _.maxBy(storage.messages, 'id')
  36. storage.maxId = lastMessage.id
  37. }
  38. if (storage.minId === messageId) {
  39. const firstMessage = _.minBy(storage.messages, 'id')
  40. storage.minId = firstMessage.id
  41. }
  42. }
  43. const cullOlderMessages = (storage) => {
  44. const maxIndex = storage.messages.length
  45. const minIndex = maxIndex - 50
  46. if (maxIndex <= 50) return
  47. storage.messages = _.sortBy(storage.messages, ['id'])
  48. storage.minId = storage.messages[minIndex].id
  49. for (const message of storage.messages) {
  50. if (message.id < storage.minId) {
  51. delete storage.idIndex[message.id]
  52. delete storage.idempotencyKeyIndex[message.idempotency_key]
  53. }
  54. }
  55. storage.messages = storage.messages.slice(minIndex, maxIndex)
  56. }
  57. const handleMessageError = (storage, fakeId, isRetry) => {
  58. if (!storage) { return }
  59. const fakeMessage = storage.idIndex[fakeId]
  60. if (fakeMessage) {
  61. fakeMessage.error = true
  62. fakeMessage.pending = false
  63. if (!isRetry) {
  64. // Ensure the failed message doesn't stay at the bottom of the list.
  65. const lastPersistedMessage = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'desc'])[0]
  66. if (lastPersistedMessage) {
  67. const oldId = fakeMessage.id
  68. fakeMessage.id = `${lastPersistedMessage.id}-${new Date().getTime()}`
  69. storage.idIndex[fakeMessage.id] = fakeMessage
  70. delete storage.idIndex[oldId]
  71. }
  72. }
  73. }
  74. }
  75. const add = (storage, { messages: newMessages, updateMaxId = true }) => {
  76. if (!storage) { return }
  77. for (let i = 0; i < newMessages.length; i++) {
  78. const message = newMessages[i]
  79. // sanity check
  80. if (message.chat_id !== storage.chatId) { return }
  81. if (message.fakeId) {
  82. const fakeMessage = storage.idIndex[message.fakeId]
  83. if (fakeMessage) {
  84. // In case the same id exists (chat update before POST response)
  85. // make sure to remove the older duplicate message.
  86. if (storage.idIndex[message.id]) {
  87. delete storage.idIndex[message.id]
  88. storage.messages = storage.messages.filter(msg => msg.id !== message.id)
  89. }
  90. Object.assign(fakeMessage, message, { error: false })
  91. delete fakeMessage.fakeId
  92. storage.idIndex[fakeMessage.id] = fakeMessage
  93. delete storage.idIndex[message.fakeId]
  94. return
  95. }
  96. }
  97. if (!storage.minId || (!message.pending && message.id < storage.minId)) {
  98. storage.minId = message.id
  99. }
  100. if (!storage.maxId || message.id > storage.maxId) {
  101. if (updateMaxId) {
  102. storage.maxId = message.id
  103. }
  104. }
  105. if (!storage.idIndex[message.id] && !isConfirmation(storage, message)) {
  106. if (storage.lastSeenMessageId < message.id) {
  107. storage.newMessageCount++
  108. }
  109. storage.idIndex[message.id] = message
  110. storage.messages.push(storage.idIndex[message.id])
  111. storage.idempotencyKeyIndex[message.idempotency_key] = true
  112. }
  113. }
  114. }
  115. const isConfirmation = (storage, message) => {
  116. if (!message.idempotency_key) return
  117. return storage.idempotencyKeyIndex[message.idempotency_key]
  118. }
  119. const resetNewMessageCount = (storage) => {
  120. if (!storage) { return }
  121. storage.newMessageCount = 0
  122. storage.lastSeenMessageId = storage.maxId
  123. }
  124. // Inserts date separators and marks the head and tail if it's the chain of messages made by the same user
  125. const getView = (storage) => {
  126. if (!storage) { return [] }
  127. const result = []
  128. const messages = _.orderBy(storage.messages, ['pending', 'id'], ['asc', 'asc'])
  129. const firstMessage = messages[0]
  130. let previousMessage = messages[messages.length - 1]
  131. let currentMessageChainId
  132. if (firstMessage) {
  133. const date = new Date(firstMessage.created_at)
  134. date.setHours(0, 0, 0, 0)
  135. result.push({
  136. type: 'date',
  137. date,
  138. id: date.getTime().toString()
  139. })
  140. }
  141. let afterDate = false
  142. for (let i = 0; i < messages.length; i++) {
  143. const message = messages[i]
  144. const nextMessage = messages[i + 1]
  145. const date = new Date(message.created_at)
  146. date.setHours(0, 0, 0, 0)
  147. // insert date separator and start a new message chain
  148. if (previousMessage && previousMessage.date < date) {
  149. result.push({
  150. type: 'date',
  151. date,
  152. id: date.getTime().toString()
  153. })
  154. previousMessage.isTail = true
  155. currentMessageChainId = undefined
  156. afterDate = true
  157. }
  158. const object = {
  159. type: 'message',
  160. data: message,
  161. date,
  162. id: message.id,
  163. messageChainId: currentMessageChainId
  164. }
  165. // end a message chian
  166. if ((nextMessage && nextMessage.account_id) !== message.account_id) {
  167. object.isTail = true
  168. currentMessageChainId = undefined
  169. }
  170. // start a new message chain
  171. if ((previousMessage && previousMessage.data && previousMessage.data.account_id) !== message.account_id || afterDate) {
  172. currentMessageChainId = _.uniqueId()
  173. object.isHead = true
  174. object.messageChainId = currentMessageChainId
  175. }
  176. result.push(object)
  177. previousMessage = object
  178. afterDate = false
  179. }
  180. return result
  181. }
  182. const ChatService = {
  183. add,
  184. empty,
  185. getView,
  186. deleteMessage,
  187. cullOlderMessages,
  188. resetNewMessageCount,
  189. clear,
  190. handleMessageError
  191. }
  192. export default ChatService