Skip to content

Commit 462f42e

Browse files
Correctly handle unread Messages & re-triggering of Messages liveData
1 parent 8f931cd commit 462f42e

File tree

1 file changed

+42
-29
lines changed

1 file changed

+42
-29
lines changed

app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt

+42-29
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import com.infomaniak.mail.utils.extensions.getUids
4343
import com.infomaniak.mail.utils.extensions.indexOfFirstOrNull
4444
import dagger.hilt.android.lifecycle.HiltViewModel
4545
import io.realm.kotlin.MutableRealm
46-
import io.realm.kotlin.notifications.ResultsChange
4746
import io.realm.kotlin.query.RealmResults
4847
import io.sentry.Sentry
4948
import io.sentry.SentryLevel
@@ -85,9 +84,10 @@ class ThreadViewModel @Inject constructor(
8584
val threadLive = MutableLiveData<Thread?>()
8685
val messagesLive = MutableLiveData<Pair<ThreadAdapterItems, MessagesWithoutHeavyData>>()
8786

88-
private var indexOfFirstUnreadMessage: Int? = null
8987
private var cachedSplitBodies = mutableMapOf<String, SplitBody>()
88+
private var hasMarkedThreadAsSeen: Boolean = false
9089
private var superCollapsedBlock: MutableSet<String>? = null
90+
private var shouldDisplaySuperCollapsedBlock: Boolean? = null
9191
var hasUserClickedTheSuperCollapsedBlock = false
9292

9393
private val mailbox by lazy { mailboxController.getMailbox(AccountUtils.currentUserId, AccountUtils.currentMailboxId)!! }
@@ -98,9 +98,10 @@ class ThreadViewModel @Inject constructor(
9898
).map { it.obj }.asLiveData(ioCoroutineContext)
9999

100100
fun resetMessagesCache() {
101-
indexOfFirstUnreadMessage = null
102101
cachedSplitBodies = mutableMapOf()
102+
hasMarkedThreadAsSeen = false
103103
superCollapsedBlock = null
104+
shouldDisplaySuperCollapsedBlock = null
104105
hasUserClickedTheSuperCollapsedBlock = false
105106
}
106107

@@ -115,18 +116,22 @@ class ThreadViewModel @Inject constructor(
115116
messagesLiveJob?.cancel()
116117
messagesLiveJob = viewModelScope.launch(ioCoroutineContext) {
117118
messageController.getSortedAndNotDeletedMessagesAsync(threadUid)
118-
?.map(::mapRealmMessagesResult)
119+
?.map { mapRealmMessagesResult(it.list, threadUid) }
119120
?.collect(messagesLive::postValue)
120121
}
121122
}
122123

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> {
124128

125129
val items = mutableListOf<Any>()
126130
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()
130135

131136
suspend fun addMessage(message: Message) {
132137
splitBody(message).let {
@@ -136,7 +141,7 @@ class ThreadViewModel @Inject constructor(
136141
}
137142

138143
suspend fun mapListWithNewSuperCollapsedBlock() {
139-
results.list.forEachIndexed { index, message ->
144+
messages.forEachIndexed { index, message ->
140145
when (index) {
141146
0 -> { // First Message
142147
addMessage(message)
@@ -156,45 +161,55 @@ class ThreadViewModel @Inject constructor(
156161
}
157162

158163
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 ->
161171
when {
162172
index == 0 -> { // First Message
163173
addMessage(message)
164174
}
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)
167177
}
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
170180
items += SuperCollapsedBlock(superCollapsedBlock!!)
171181
addMessage(message.apply { shouldHideDivider = true })
172182
}
173183
else -> { // All following Messages
174-
if (superCollapsedBlock?.contains(message.uid) == true) superCollapsedBlock?.remove(message.uid)
175184
addMessage(message)
176185
}
177186
}
178187
}
179188
}
180189

181190
suspend fun mapFullList() {
182-
results.list.forEach { addMessage(it) }
191+
messages.forEach { addMessage(it) }
183192
}
184193

185-
if (shouldDisplaySuperCollapsedBlock) {
194+
if (shouldDisplaySuperCollapsedBlock == true) {
186195
if (shouldCreateSuperCollapsedBlock) mapListWithNewSuperCollapsedBlock() else mapListWithExistingSuperCollapsedBlock()
187196
} else {
188197
mapFullList()
189198
}
190199

200+
if (thread.unseenMessagesCount > 0 && !hasMarkedThreadAsSeen) {
201+
hasMarkedThreadAsSeen = true
202+
sharedUtils.markAsSeen(mailbox, listOf(thread))
203+
}
204+
191205
return items to messagesToFetch
192206
}
193207

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)
198213
}
199214

200215
/**
@@ -205,13 +220,16 @@ class ThreadViewModel @Inject constructor(
205220
* After all these Messages are displayed, if there's at least 2 remaining Messages, they're gonna be collapsed in the SuperCollapsedBlock.
206221
*/
207222
private fun shouldSuperCollapsedBlockBeDisplayed(messagesCount: Int, firstIndexAfterBlock: Int): Boolean {
223+
224+
if (shouldDisplaySuperCollapsedBlock == false) return false
225+
208226
return messagesCount >= SUPER_COLLAPSED_BLOCK_MINIMUM_MESSAGES_LIMIT && // At least 5 Messages in the Thread
209227
firstIndexAfterBlock >= SUPER_COLLAPSED_BLOCK_FIRST_INDEX_LIMIT && // At least 2 Messages in the SuperCollapsedBlock
210228
!hasUserClickedTheSuperCollapsedBlock // SuperCollapsedBlock hasn't been expanded by the user
211229
}
212230

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 {
215233
if (it) superCollapsedBlock = mutableSetOf()
216234
}
217235
}
@@ -250,11 +268,6 @@ class ThreadViewModel @Inject constructor(
250268
}
251269

252270
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-
}
258271
}
259272

260273
private fun sendMatomoAndSentryAboutThreadMessagesCount(thread: Thread) {

0 commit comments

Comments
 (0)