direct_conversation_service.js (4245B)
1 import { uniqueId } from 'lodash' 2 3 const empty = () => { 4 return { 5 ids: new Set(), 6 prevIds: new Set(), 7 collection: [], 8 markedForDeletion: {}, 9 latestTimestamp: 0, 10 newMessagesCount: 0, 11 conversationId: undefined 12 } 13 } 14 15 const clear = (storage) => { 16 storage.ids.clear() 17 storage.prevIds.clear() 18 storage.collection.splice(0, storage.collection.length) 19 storage.markedForDeletion = {} 20 storage.latestTimestamp = 0 21 storage.newMessagesCount = 0 22 storage.conversationId = undefined 23 } 24 25 const remove = (storage, id) => { 26 storage.ids.delete(id) 27 storage.collection = storage.collection.filter(c => c.id !== id) 28 delete storage.markedForDeletion[status.id] 29 } 30 31 const setConversationId = (storage, conversationId) => { 32 storage.conversationId = conversationId 33 } 34 35 const add = (storage, { statuses: newStatuses, check = false, isFirstFetch = false }) => { 36 for (let i = 0; i < newStatuses.length; i++) { 37 let status = newStatuses[i] 38 // sanity check 39 if (status.direct_conversation_id !== storage.conversationId) { return } 40 if (check && storage.markedForDeletion[status.id]) { 41 delete storage.markedForDeletion[status.id] 42 } 43 44 if (!storage.ids.has(status.id)) { 45 if (storage.latestTimestamp < status.created_at) { 46 storage.newMessagesCount++ 47 storage.latestTimestamp = status.created_at 48 } 49 storage.collection.push(status) 50 storage.ids.add(status.id) 51 } 52 } 53 54 // Mark for deletion first to avoid race conditions with streaming and regular fetch (used as a fallback) 55 if (check && !isFirstFetch) { 56 storage.ids.forEach(id => { 57 if (!storage.prevIds.has(id)) { 58 if (storage.markedForDeletion[id] && storage.markedForDeletion[id] > 2) { 59 remove(storage, id) 60 } else { 61 if (storage.markedForDeletion[id]) { 62 storage.markedForDeletion[id]++ 63 } else { 64 storage.markedForDeletion[id] = 1 65 } 66 } 67 } 68 }) 69 } 70 71 if (check) { 72 storage.prevIds = new Set(newStatuses.map(s => s.id)) 73 } 74 } 75 76 const resetNewMessageCount = (storage) => { 77 storage.newMessagesCount = 0 78 } 79 80 const sortById = (a, b) => { 81 const seqA = Number(a.id) 82 const seqB = Number(b.id) 83 const isSeqA = !Number.isNaN(seqA) 84 const isSeqB = !Number.isNaN(seqB) 85 if (isSeqA && isSeqB) { 86 return seqA > seqB ? 1 : -1 87 } else if (isSeqA && !isSeqB) { 88 return -1 89 } else if (!isSeqA && isSeqB) { 90 return 1 91 } else { 92 return a.id > b.id ? 1 : -1 93 } 94 } 95 96 // Inserts date separators and marks the head and tail if it's the sequence of messages made by the same user 97 const getView = (storage) => { 98 let { collection } = storage 99 100 let coll = collection.sort(sortById) 101 let res = [] 102 103 let prev = coll[coll.length - 1] 104 let currentSequenceId 105 106 let firstStatus = coll[0] 107 if (firstStatus) { 108 let date = new Date(firstStatus.created_at) 109 date.setHours(0, 0, 0, 0) 110 res.push({ type: 'date', date: date, id: date.getTime().toString() }) 111 } 112 113 let afterDate = false 114 115 for (let i = 0; i < coll.length; i++) { 116 let status = coll[i] 117 let nextStatus = coll[i + 1] 118 119 let date = new Date(status.created_at) 120 date.setHours(0, 0, 0, 0) 121 122 // insert date separator and start a new sequence 123 if (prev && prev.date < date) { 124 res.push({ type: 'date', date: date, id: date.getTime().toString() }) 125 prev['isTail'] = true 126 currentSequenceId = undefined 127 afterDate = true 128 } 129 130 let object = { type: 'status', data: status, date: date, id: status.id, sequenceId: currentSequenceId } 131 // end a message sequence 132 if ((nextStatus && nextStatus.user.id) !== status.user.id) { 133 object['isTail'] = true 134 currentSequenceId = undefined 135 } 136 // start a new message sequence 137 if ((prev && prev.data && prev.data.user && prev.data.user.id) !== status.user.id || afterDate) { 138 currentSequenceId = uniqueId() 139 object['isHead'] = true 140 object['sequenceId'] = currentSequenceId 141 } 142 res.push(object) 143 prev = object 144 afterDate = false 145 } 146 147 return res 148 } 149 150 const DirectConversationStatuses = { 151 add, 152 empty, 153 remove, 154 resetNewMessageCount, 155 getView, 156 clear, 157 setConversationId 158 } 159 160 export default DirectConversationStatuses