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