@@ -43,7 +43,6 @@ import com.infomaniak.mail.utils.extensions.getUids
43
43
import com.infomaniak.mail.utils.extensions.indexOfFirstOrNull
44
44
import dagger.hilt.android.lifecycle.HiltViewModel
45
45
import io.realm.kotlin.MutableRealm
46
- import io.realm.kotlin.notifications.ResultsChange
47
46
import io.realm.kotlin.query.RealmResults
48
47
import io.sentry.Sentry
49
48
import io.sentry.SentryLevel
@@ -85,9 +84,10 @@ class ThreadViewModel @Inject constructor(
85
84
val threadLive = MutableLiveData <Thread ?>()
86
85
val messagesLive = MutableLiveData <Pair <ThreadAdapterItems , MessagesWithoutHeavyData >>()
87
86
88
- private var indexOfFirstUnreadMessage: Int? = null
89
87
private var cachedSplitBodies = mutableMapOf<String , SplitBody >()
88
+ private var hasMarkedThreadAsSeen: Boolean = false
90
89
private var superCollapsedBlock: MutableSet <String >? = null
90
+ private var shouldDisplaySuperCollapsedBlock: Boolean? = null
91
91
var hasUserClickedTheSuperCollapsedBlock = false
92
92
93
93
private val mailbox by lazy { mailboxController.getMailbox(AccountUtils .currentUserId, AccountUtils .currentMailboxId)!! }
@@ -98,9 +98,10 @@ class ThreadViewModel @Inject constructor(
98
98
).map { it.obj }.asLiveData(ioCoroutineContext)
99
99
100
100
fun resetMessagesCache () {
101
- indexOfFirstUnreadMessage = null
102
101
cachedSplitBodies = mutableMapOf ()
102
+ hasMarkedThreadAsSeen = false
103
103
superCollapsedBlock = null
104
+ shouldDisplaySuperCollapsedBlock = null
104
105
hasUserClickedTheSuperCollapsedBlock = false
105
106
}
106
107
@@ -115,18 +116,22 @@ class ThreadViewModel @Inject constructor(
115
116
messagesLiveJob?.cancel()
116
117
messagesLiveJob = viewModelScope.launch(ioCoroutineContext) {
117
118
messageController.getSortedAndNotDeletedMessagesAsync(threadUid)
118
- ?.map(:: mapRealmMessagesResult)
119
+ ?.map { mapRealmMessagesResult(it.list, threadUid) }
119
120
?.collect(messagesLive::postValue)
120
121
}
121
122
}
122
123
123
- private suspend fun mapRealmMessagesResult (results : ResultsChange <Message >): Pair <ThreadAdapterItems , MessagesWithoutHeavyData > {
124
+ private suspend fun mapRealmMessagesResult (
125
+ messages : RealmResults <Message >,
126
+ threadUid : String ,
127
+ ): Pair <ThreadAdapterItems , MessagesWithoutHeavyData > {
124
128
125
129
val items = mutableListOf<Any >()
126
130
val messagesToFetch = mutableListOf<Message >()
127
- val firstIndexAfterBlock = computeFirstIndexAfterBlock(results.list)
128
- val shouldDisplaySuperCollapsedBlock = shouldSuperCollapsedBlockBeDisplayed(results.list.count(), firstIndexAfterBlock)
129
- val shouldCreateSuperCollapsedBlock = shouldSuperCollapsedBlockBeCreated(shouldDisplaySuperCollapsedBlock)
131
+ val thread = messages.firstOrNull()?.threads?.firstOrNull { it.uid == threadUid } ? : return items to messagesToFetch
132
+ val firstIndexAfterBlock = computeFirstIndexAfterBlock(thread, messages)
133
+ shouldDisplaySuperCollapsedBlock = shouldSuperCollapsedBlockBeDisplayed(messages.count(), firstIndexAfterBlock)
134
+ val shouldCreateSuperCollapsedBlock = shouldSuperCollapsedBlockBeCreated()
130
135
131
136
suspend fun addMessage (message : Message ) {
132
137
splitBody(message).let {
@@ -136,7 +141,7 @@ class ThreadViewModel @Inject constructor(
136
141
}
137
142
138
143
suspend fun mapListWithNewSuperCollapsedBlock () {
139
- results.list .forEachIndexed { index, message ->
144
+ messages .forEachIndexed { index, message ->
140
145
when (index) {
141
146
0 -> { // First Message
142
147
addMessage(message)
@@ -156,45 +161,55 @@ class ThreadViewModel @Inject constructor(
156
161
}
157
162
158
163
suspend fun mapListWithExistingSuperCollapsedBlock () {
159
- var blockHasNotBeenBroken = true
160
- results.list.forEachIndexed { index, message ->
164
+
165
+ var isStillInBlock = true
166
+ val previousBlock = superCollapsedBlock!! .toSet()
167
+
168
+ superCollapsedBlock!! .clear()
169
+
170
+ messages.forEachIndexed { index, message ->
161
171
when {
162
172
index == 0 -> { // First Message
163
173
addMessage(message)
164
174
}
165
- superCollapsedBlock? .contains(message.uid) == true && blockHasNotBeenBroken -> { // All Messages already in block
166
- // No-op
175
+ previousBlock .contains(message.uid) && isStillInBlock -> { // All Messages already in block
176
+ superCollapsedBlock !! .add(message.uid)
167
177
}
168
- superCollapsedBlock? .contains(message.uid) == false && blockHasNotBeenBroken -> { // First Message not in block
169
- blockHasNotBeenBroken = false
178
+ ! previousBlock .contains(message.uid) && isStillInBlock -> { // First Message not in block
179
+ isStillInBlock = false
170
180
items + = SuperCollapsedBlock (superCollapsedBlock!! )
171
181
addMessage(message.apply { shouldHideDivider = true })
172
182
}
173
183
else -> { // All following Messages
174
- if (superCollapsedBlock?.contains(message.uid) == true ) superCollapsedBlock?.remove(message.uid)
175
184
addMessage(message)
176
185
}
177
186
}
178
187
}
179
188
}
180
189
181
190
suspend fun mapFullList () {
182
- results.list .forEach { addMessage(it) }
191
+ messages .forEach { addMessage(it) }
183
192
}
184
193
185
- if (shouldDisplaySuperCollapsedBlock) {
194
+ if (shouldDisplaySuperCollapsedBlock == true ) {
186
195
if (shouldCreateSuperCollapsedBlock) mapListWithNewSuperCollapsedBlock() else mapListWithExistingSuperCollapsedBlock()
187
196
} else {
188
197
mapFullList()
189
198
}
190
199
200
+ if (thread.unseenMessagesCount > 0 && ! hasMarkedThreadAsSeen) {
201
+ hasMarkedThreadAsSeen = true
202
+ sharedUtils.markAsSeen(mailbox, listOf (thread))
203
+ }
204
+
191
205
return items to messagesToFetch
192
206
}
193
207
194
- private fun computeFirstIndexAfterBlock (list : RealmResults <Message >): Int {
195
- val fallbackIndex = list.count() - 2
196
- val firstUnreadIndex = indexOfFirstUnreadMessage ? : fallbackIndex
197
- return minOf(firstUnreadIndex, fallbackIndex)
208
+ private fun computeFirstIndexAfterBlock (thread : Thread , list : RealmResults <Message >): Int {
209
+ val firstDefaultIndex = list.count() - 2
210
+ val firstUnreadIndex = if (thread.unseenMessagesCount > 0 ) list.indexOfFirstOrNull { ! it.isSeen } else null
211
+ val notNullFirstUnreadIndex = firstUnreadIndex ? : firstDefaultIndex
212
+ return minOf(notNullFirstUnreadIndex, firstDefaultIndex)
198
213
}
199
214
200
215
/* *
@@ -205,13 +220,16 @@ class ThreadViewModel @Inject constructor(
205
220
* After all these Messages are displayed, if there's at least 2 remaining Messages, they're gonna be collapsed in the SuperCollapsedBlock.
206
221
*/
207
222
private fun shouldSuperCollapsedBlockBeDisplayed (messagesCount : Int , firstIndexAfterBlock : Int ): Boolean {
223
+
224
+ if (shouldDisplaySuperCollapsedBlock == false ) return false
225
+
208
226
return messagesCount >= SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT && // At least 5 Messages in the Thread
209
227
firstIndexAfterBlock >= SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT && // At least 2 Messages in the SuperCollapsedBlock
210
228
! hasUserClickedTheSuperCollapsedBlock // SuperCollapsedBlock hasn't been expanded by the user
211
229
}
212
230
213
- private fun shouldSuperCollapsedBlockBeCreated (shouldDisplaySuperCollapsedBlock : Boolean ): Boolean {
214
- return (shouldDisplaySuperCollapsedBlock && superCollapsedBlock == null ).also {
231
+ private fun shouldSuperCollapsedBlockBeCreated (): Boolean {
232
+ return (shouldDisplaySuperCollapsedBlock == true && superCollapsedBlock == null ).also {
215
233
if (it) superCollapsedBlock = mutableSetOf ()
216
234
}
217
235
}
@@ -250,11 +268,6 @@ class ThreadViewModel @Inject constructor(
250
268
}
251
269
252
270
emit(OpenThreadResult (thread, isExpandedMap, initialSetOfExpandedMessagesUids, isThemeTheSameMap))
253
-
254
- if (thread.unseenMessagesCount > 0 ) {
255
- indexOfFirstUnreadMessage = thread.messages.indexOfFirstOrNull { ! it.isSeen }
256
- sharedUtils.markAsSeen(mailbox, listOf (thread))
257
- }
258
271
}
259
272
260
273
private fun sendMatomoAndSentryAboutThreadMessagesCount (thread : Thread ) {
0 commit comments