@@ -47,6 +47,7 @@ import io.realm.kotlin.query.RealmResults
47
47
import io.sentry.Sentry
48
48
import io.sentry.SentryLevel
49
49
import kotlinx.coroutines.*
50
+ import kotlinx.coroutines.flow.Flow
50
51
import kotlinx.coroutines.flow.map
51
52
import javax.inject.Inject
52
53
import kotlin.collections.set
@@ -128,95 +129,78 @@ class ThreadViewModel @Inject constructor(
128
129
}
129
130
}
130
131
131
- fun reassignMessagesLive (threadUid : String , withSuperCollapsedBlock : Boolean = true) {
132
+ fun reassignMessagesLive (threadUid : String ) {
133
+ reassignMessages {
134
+ messageController.getSortedAndNotDeletedMessagesAsync(threadUid)?.map { mapRealmMessagesResult(it.list, threadUid) }
135
+ }
136
+ }
137
+
138
+ fun reassignMessagesLiveWithoutSuperCollapsedBlock (messageUid : String ) {
139
+ reassignMessages {
140
+ messageController.getMessagesAsync(messageUid).map { mapRealmMessagesResultWithoutSuperCollapsedBlock(it.list) }
141
+ }
142
+ }
143
+
144
+ private fun reassignMessages (messagesFlow : (() -> Flow <Pair <ThreadAdapterItems , MessagesWithoutHeavyData >>? )) {
132
145
messagesLiveJob?.cancel()
133
146
messagesLiveJob = viewModelScope.launch(ioCoroutineContext) {
134
- messageController.getSortedAndNotDeletedMessagesAsync(threadUid)
135
- ?.map { mapRealmMessagesResult(it.list, threadUid, withSuperCollapsedBlock) }
136
- ?.collect(messagesLive::postValue)
147
+ messagesFlow()?.collect(messagesLive::postValue)
137
148
}
138
149
}
139
150
140
151
private suspend fun mapRealmMessagesResult (
141
152
messages : RealmResults <Message >,
142
153
threadUid : String ,
143
- withSuperCollapsedBlock : Boolean ,
144
154
): Pair <ThreadAdapterItems , MessagesWithoutHeavyData > {
145
155
146
156
superCollapsedBlock = superCollapsedBlock ? : SuperCollapsedBlock ()
147
157
148
- val items = mutableListOf<Any >()
149
- val messagesToFetch = mutableListOf<Message >()
150
- val thread = messages.firstOrNull()?.threads?.firstOrNull { it.uid == threadUid } ? : return items to messagesToFetch
158
+ val thread = messages.first().threads.single { it.uid == threadUid }
151
159
val firstIndexAfterBlock = computeFirstIndexAfterBlock(thread, messages)
152
- superCollapsedBlock!! .shouldBeDisplayed =
153
- shouldBlockBeDisplayed(messages.count(), firstIndexAfterBlock, withSuperCollapsedBlock)
160
+ superCollapsedBlock!! .shouldBeDisplayed = shouldBlockBeDisplayed(messages.count(), firstIndexAfterBlock)
154
161
155
- suspend fun addMessage (message : Message ) {
156
- splitBody(message).let {
157
- items + = it
158
- if (! it.isFullyDownloaded()) messagesToFetch + = it
159
- }
160
- }
161
-
162
- suspend fun mapListWithNewBlock () {
163
- messages.forEachIndexed { index, message ->
162
+ suspend fun formatListWithNewBlock (): Pair <ThreadAdapterItems , MessagesWithoutHeavyData > {
163
+ return formatLists(messages) { index, _ ->
164
164
when (index) {
165
- 0 -> { // First Message
166
- addMessage(message)
167
- }
168
- in 1 .. < firstIndexAfterBlock -> { // All Messages that should go in block
169
- superCollapsedBlock!! .messagesUids.add(message.uid)
170
- }
171
- firstIndexAfterBlock -> { // First Message not in block
172
- items + = superCollapsedBlock!!
173
- addMessage(message.apply { shouldHideDivider = true })
174
- }
175
- else -> { // All following Messages
176
- addMessage(message)
177
- }
165
+ 0 -> MessageBehavior .DISPLAYED // First Message
166
+ in 1 until firstIndexAfterBlock -> MessageBehavior .COLLAPSED // All Messages that should go in block
167
+ firstIndexAfterBlock -> MessageBehavior .FIRST_AFTER_BLOCK // First Message not in block
168
+ else -> MessageBehavior .DISPLAYED // All following Messages
178
169
}
179
170
}
180
171
}
181
172
182
- suspend fun mapListWithExistingBlock () {
173
+ suspend fun formatListWithExistingBlock (): Pair < ThreadAdapterItems , MessagesWithoutHeavyData > {
183
174
184
175
var isStillInBlock = true
185
176
val previousBlock = superCollapsedBlock!! .messagesUids.toSet()
186
177
187
178
superCollapsedBlock!! .messagesUids.clear()
188
179
189
- messages.forEachIndexed { index, message ->
180
+ return formatLists( messages) { index, messageUid ->
190
181
when {
191
- index == 0 -> { // First Message
192
- addMessage(message)
193
- }
194
- previousBlock.contains(message.uid) && isStillInBlock -> { // All Messages already in block
195
- superCollapsedBlock!! .messagesUids.add(message.uid)
196
- }
197
- ! previousBlock.contains(message.uid) && isStillInBlock -> { // First Message not in block
182
+ index == 0 -> MessageBehavior .DISPLAYED // First Message
183
+ previousBlock.contains(messageUid) && isStillInBlock -> MessageBehavior .COLLAPSED // All Messages already in block
184
+ ! previousBlock.contains(messageUid) && isStillInBlock -> { // First Message not in block
198
185
isStillInBlock = false
199
- items + = superCollapsedBlock!!
200
- addMessage(message.apply { shouldHideDivider = true })
201
- }
202
- else -> { // All following Messages
203
- addMessage(message)
186
+ MessageBehavior .FIRST_AFTER_BLOCK
204
187
}
188
+ else -> MessageBehavior .DISPLAYED // All following Messages
205
189
}
206
190
}
207
191
}
208
192
209
- suspend fun mapFullList () {
210
- messages.forEach { addMessage(it) }
211
- }
212
-
213
- if (superCollapsedBlock!! .shouldBeDisplayed) {
214
- if (superCollapsedBlock!! .isFirstTime()) mapListWithNewBlock() else mapListWithExistingBlock()
193
+ return if (superCollapsedBlock!! .shouldBeDisplayed) {
194
+ if (superCollapsedBlock!! .isFirstTime()) formatListWithNewBlock() else formatListWithExistingBlock()
215
195
} else {
216
- mapFullList()
196
+ formatLists(messages) { _, _ -> MessageBehavior . DISPLAYED }
217
197
}
198
+ }
218
199
219
- return items to messagesToFetch
200
+ private suspend fun mapRealmMessagesResultWithoutSuperCollapsedBlock (
201
+ messages : RealmResults <Message >,
202
+ ): Pair <ThreadAdapterItems , MessagesWithoutHeavyData > {
203
+ return formatLists(messages) { _, _ -> MessageBehavior .DISPLAYED }
220
204
}
221
205
222
206
private fun computeFirstIndexAfterBlock (thread : Thread , list : RealmResults <Message >): Int {
@@ -235,14 +219,47 @@ class ThreadViewModel @Inject constructor(
235
219
* - If there's any unread Message in between, it will be displayed (hence, all following Messages will be displayed too).
236
220
* After all these Messages are displayed, if there's at least 2 remaining Messages, they're gonna be collapsed in the Block.
237
221
*/
238
- private fun shouldBlockBeDisplayed (messagesCount : Int , firstIndexAfterBlock : Int , withSuperCollapsedBlock : Boolean ): Boolean {
239
- return withSuperCollapsedBlock && // When we want to print a mail, we need the full list of Messages
240
- superCollapsedBlock?.shouldBeDisplayed == true && // If the Block was hidden for any reason, we mustn't ever display it again
222
+ private fun shouldBlockBeDisplayed (messagesCount : Int , firstIndexAfterBlock : Int ): Boolean {
223
+ return superCollapsedBlock?.shouldBeDisplayed == true && // If the Block was hidden for any reason, we mustn't ever display it again
241
224
! hasSuperCollapsedBlockBeenClicked && // Block hasn't been expanded by the user
242
225
messagesCount >= SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT && // At least 5 Messages in the Thread
243
226
firstIndexAfterBlock >= SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT // At least 2 Messages in the Block
244
227
}
245
228
229
+ // If we add a fourth case in the `when`, don't forget to add a fourth 'o' in the function name.
230
+ private suspend fun formatLists (
231
+ messages : List <Message >,
232
+ computeBehavior : (Int , String ) -> MessageBehavior ,
233
+ ): Pair <MutableList <Any >, MutableList<Message>> {
234
+
235
+ val items = mutableListOf<Any >()
236
+ val messagesToFetch = mutableListOf<Message >()
237
+
238
+ suspend fun addMessage (message : Message ) {
239
+ splitBody(message).let {
240
+ items + = it
241
+ if (! it.isFullyDownloaded()) messagesToFetch + = it
242
+ }
243
+ }
244
+
245
+ messages.forEachIndexed { index, message ->
246
+ when (computeBehavior(index, message.uid)) {
247
+ MessageBehavior .DISPLAYED -> {
248
+ addMessage(message)
249
+ }
250
+ MessageBehavior .COLLAPSED -> {
251
+ superCollapsedBlock!! .messagesUids.add(message.uid)
252
+ }
253
+ MessageBehavior .FIRST_AFTER_BLOCK -> {
254
+ items + = superCollapsedBlock!!
255
+ addMessage(message.apply { shouldHideDivider = true })
256
+ }
257
+ }
258
+ }
259
+
260
+ return items to messagesToFetch
261
+ }
262
+
246
263
private suspend fun splitBody (message : Message ): Message = withContext(ioDispatcher) {
247
264
if (message.body == null ) return @withContext message
248
265
@@ -437,6 +454,12 @@ class ThreadViewModel @Inject constructor(
437
454
val menuId : Int ,
438
455
)
439
456
457
+ private enum class MessageBehavior {
458
+ DISPLAYED ,
459
+ COLLAPSED ,
460
+ FIRST_AFTER_BLOCK ,
461
+ }
462
+
440
463
companion object {
441
464
private const val SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT = 5
442
465
private const val SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT = 3
0 commit comments