@@ -40,9 +40,11 @@ import com.infomaniak.mail.utils.MessageBodyUtils.SplitBody
40
40
import com.infomaniak.mail.utils.extensions.MergedContactDictionary
41
41
import com.infomaniak.mail.utils.extensions.context
42
42
import com.infomaniak.mail.utils.extensions.getUids
43
+ import com.infomaniak.mail.utils.extensions.indexOfFirstOrNull
43
44
import dagger.hilt.android.lifecycle.HiltViewModel
44
45
import io.realm.kotlin.MutableRealm
45
46
import io.realm.kotlin.notifications.ResultsChange
47
+ import io.realm.kotlin.query.RealmResults
46
48
import io.sentry.Sentry
47
49
import io.sentry.SentryLevel
48
50
import kotlinx.coroutines.*
@@ -83,6 +85,7 @@ class ThreadViewModel @Inject constructor(
83
85
val threadLive = MutableLiveData <Thread ?>()
84
86
val messagesLive = MutableLiveData <Pair <ThreadAdapterItems , MessagesWithoutHeavyData >>()
85
87
88
+ private var indexOfFirstUnreadMessage: Int? = null
86
89
private var cachedSplitBodies = mutableMapOf<String , SplitBody >()
87
90
private var superCollapsedBlock: MutableSet <String >? = null
88
91
var hasUserClickedTheSuperCollapsedBlock = false
@@ -95,6 +98,7 @@ class ThreadViewModel @Inject constructor(
95
98
).map { it.obj }.asLiveData(ioCoroutineContext)
96
99
97
100
fun resetMessagesCache () {
101
+ indexOfFirstUnreadMessage = null
98
102
cachedSplitBodies = mutableMapOf ()
99
103
superCollapsedBlock = null
100
104
hasUserClickedTheSuperCollapsedBlock = false
@@ -118,15 +122,11 @@ class ThreadViewModel @Inject constructor(
118
122
119
123
private suspend fun mapRealmMessagesResult (results : ResultsChange <Message >): Pair <ThreadAdapterItems , MessagesWithoutHeavyData > {
120
124
121
- val messagesCount = results.list.count()
122
125
val items = mutableListOf<Any >()
123
126
val messagesToFetch = mutableListOf<Message >()
124
-
125
- val shouldDisplaySuperCollapsedBlock =
126
- messagesCount >= SUPER_COLLAPSE_BLOCK_MESSAGES_LIMIT && ! hasUserClickedTheSuperCollapsedBlock
127
- val shouldCreateSuperCollapsedBlock = (shouldDisplaySuperCollapsedBlock && superCollapsedBlock == null ).also {
128
- if (it) superCollapsedBlock = mutableSetOf ()
129
- }
127
+ val firstIndexAfterBlock = computeFirstIndexAfterBlock(results.list)
128
+ val shouldDisplaySuperCollapsedBlock = shouldSuperCollapsedBlockBeDisplayed(results.list.count(), firstIndexAfterBlock)
129
+ val shouldCreateSuperCollapsedBlock = shouldSuperCollapsedBlockBeCreated(shouldDisplaySuperCollapsedBlock)
130
130
131
131
suspend fun addMessage (message : Message ) {
132
132
splitBody(message).let {
@@ -137,18 +137,18 @@ class ThreadViewModel @Inject constructor(
137
137
138
138
suspend fun mapListWithNewSuperCollapsedBlock () {
139
139
results.list.forEachIndexed { index, message ->
140
- when {
141
- index == 0 -> { // First Message
140
+ when (index) {
141
+ 0 -> { // First Message
142
142
addMessage(message)
143
143
}
144
- index > 0 && index < messagesCount - 2 -> { // All Messages that should go in block
144
+ in 1 .. < firstIndexAfterBlock -> { // All Messages that should go in block
145
145
superCollapsedBlock!! .add(message.uid)
146
146
}
147
- index == messagesCount - 2 -> { // First Message not in block
147
+ firstIndexAfterBlock -> { // First Message not in block
148
148
items + = SuperCollapsedBlock (superCollapsedBlock!! )
149
149
addMessage(message.apply { shouldHideDivider = true })
150
150
}
151
- else -> { // All following Messages (theoretically, only 1 Message)
151
+ else -> { // All following Messages
152
152
addMessage(message)
153
153
}
154
154
}
@@ -191,6 +191,31 @@ class ThreadViewModel @Inject constructor(
191
191
return items to messagesToFetch
192
192
}
193
193
194
+ private fun computeFirstIndexAfterBlock (list : RealmResults <Message >): Int {
195
+ val fallbackIndex = list.count() - 2
196
+ val firstUnreadIndex = indexOfFirstUnreadMessage ? : fallbackIndex
197
+ return minOf(firstUnreadIndex, fallbackIndex)
198
+ }
199
+
200
+ /* *
201
+ * Before trying to create the SuperCollapsedBlock, we need these required Messages that will be displayed:
202
+ * - The 1st Message will always be displayed.
203
+ * - The last 2 Messages will always be displayed.
204
+ * - If there's any unread Message in between, it will be displayed (hence, all following Messages will be displayed too).
205
+ * After all these Messages are displayed, if there's at least 2 remaining Messages, they're gonna be collapsed in the SuperCollapsedBlock.
206
+ */
207
+ private fun shouldSuperCollapsedBlockBeDisplayed (messagesCount : Int , firstIndexAfterBlock : Int ): Boolean {
208
+ return messagesCount >= SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT && // At least 5 Messages in the Thread
209
+ firstIndexAfterBlock >= SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT && // At least 2 Messages in the SuperCollapsedBlock
210
+ ! hasUserClickedTheSuperCollapsedBlock // SuperCollapsedBlock hasn't been expanded by the user
211
+ }
212
+
213
+ private fun shouldSuperCollapsedBlockBeCreated (shouldDisplaySuperCollapsedBlock : Boolean ): Boolean {
214
+ return (shouldDisplaySuperCollapsedBlock && superCollapsedBlock == null ).also {
215
+ if (it) superCollapsedBlock = mutableSetOf ()
216
+ }
217
+ }
218
+
194
219
private suspend fun splitBody (message : Message ): Message = withContext(ioDispatcher) {
195
220
if (message.body == null ) return @withContext message
196
221
@@ -226,7 +251,10 @@ class ThreadViewModel @Inject constructor(
226
251
227
252
emit(OpenThreadResult (thread, isExpandedMap, initialSetOfExpandedMessagesUids, isThemeTheSameMap))
228
253
229
- if (thread.unseenMessagesCount > 0 ) sharedUtils.markAsSeen(mailbox, listOf (thread))
254
+ if (thread.unseenMessagesCount > 0 ) {
255
+ indexOfFirstUnreadMessage = thread.messages.indexOfFirstOrNull { ! it.isSeen }
256
+ sharedUtils.markAsSeen(mailbox, listOf (thread))
257
+ }
230
258
}
231
259
232
260
private fun sendMatomoAndSentryAboutThreadMessagesCount (thread : Thread ) {
@@ -393,6 +421,7 @@ class ThreadViewModel @Inject constructor(
393
421
)
394
422
395
423
companion object {
396
- private const val SUPER_COLLAPSE_BLOCK_MESSAGES_LIMIT = 5
424
+ private const val SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT = 5
425
+ private const val SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT = 3
397
426
}
398
427
}
0 commit comments