Skip to content

Commit d5d2e21

Browse files
Merge pull request #1876 from Infomaniak/empty-thread
Delete Thread in Realm if it's empty and we're trying to display it in ThreadsList
2 parents 2f96571 + 24043e3 commit d5d2e21

File tree

10 files changed

+69
-11
lines changed

10 files changed

+69
-11
lines changed

app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/RefreshController.kt

+3
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class RefreshController @Inject constructor(
102102
setupConfiguration(refreshMode, mailbox, folder, realm, okHttpClient, callbacks)
103103

104104
return refreshWithRunCatching(refreshThreadsJob!!).also { (threads, _) ->
105+
106+
ThreadController.deleteEmptyThreadsInFolder(folder.id, realm)
107+
105108
if (threads != null) {
106109
onStop?.invoke()
107110
SentryLog.d("API", "End of refreshing threads with mode: $refreshMode | (${folder.displayForSentry()})")

app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/ThreadController.kt

+29-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.infomaniak.mail.data.models.thread.Thread.ThreadFilter
2828
import com.infomaniak.mail.di.IoDispatcher
2929
import com.infomaniak.mail.utils.ErrorCode
3030
import com.infomaniak.mail.utils.SearchUtils.Companion.convertToSearchThreads
31+
import com.infomaniak.mail.utils.SentryDebug
3132
import io.realm.kotlin.MutableRealm
3233
import io.realm.kotlin.Realm
3334
import io.realm.kotlin.TypedRealm
@@ -159,6 +160,12 @@ class ThreadController @Inject constructor(
159160
}
160161
}
161162
}
163+
164+
fun deleteThread(threadUid: String) {
165+
mailboxContentRealm().writeBlocking {
166+
delete(getThreadQuery(threadUid, realm = this))
167+
}
168+
}
162169
//endregion
163170

164171
companion object {
@@ -208,6 +215,12 @@ class ThreadController @Inject constructor(
208215
private fun getThreadQuery(uid: String, realm: TypedRealm): RealmSingleQuery<Thread> {
209216
return realm.query<Thread>("${Thread::uid.name} == $0", uid).first()
210217
}
218+
219+
private fun getEmptyThreadsInFolderQuery(folderId: String, realm: TypedRealm): RealmQuery<Thread> {
220+
val noMessages = "${Thread::messages.name}.@size == $0"
221+
return FolderController.getFolder(folderId, realm)?.threads?.query(noMessages, 0)
222+
?: realm.query<Thread>("$noMessages AND ${Thread::folderId.name} == $1", 0, folderId)
223+
}
211224
//endregion
212225

213226
//region Get data
@@ -319,7 +332,22 @@ class ThreadController @Inject constructor(
319332
}
320333

321334
fun deleteSearchThreads(realm: MutableRealm) = with(realm) {
322-
delete(query<Thread>("${Thread::isFromSearch.name} == true").find())
335+
delete(query<Thread>("${Thread::isFromSearch.name} == true"))
336+
}
337+
338+
fun deleteEmptyThreadsInFolder(folderId: String, realm: Realm) {
339+
realm.writeBlocking {
340+
val emptyThreadsQuery = getEmptyThreadsInFolderQuery(folderId, realm = this)
341+
val emptyThreads = emptyThreadsQuery.find()
342+
// TODO: Find why we are sometimes displaying empty Threads, and fix it instead of just deleting them.
343+
// It's possibly because we are out of sync, and the situation will resolve by itself shortly?
344+
if (emptyThreads.isNotEmpty()) {
345+
emptyThreads.forEach {
346+
SentryDebug.sendEmptyThread(it, "No Message in a Thread when refreshing a Folder")
347+
}
348+
delete(emptyThreadsQuery)
349+
}
350+
}
323351
}
324352

325353
private fun verifyAttachmentsValues(hasAttachmentsInThread: Boolean, messages: List<Message>, realm: MutableRealm) {

app/src/main/java/com/infomaniak/mail/data/models/thread/Thread.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,9 @@ class Thread : RealmObject {
228228

229229
}.getOrElse { throwable ->
230230
Sentry.withScope { scope ->
231-
scope.setExtra("thread.folder.role", "${folder.role?.name}")
231+
scope.setExtra("thread.folder.role", folder.role?.name.toString())
232232
scope.setExtra("thread.folder.id", folder.id)
233+
scope.setExtra("thread.folderId", folderId)
233234
scope.setExtra("thread.uid", uid)
234235
scope.setExtra("thread.messages.count", "${messages.count()}")
235236
scope.setExtra("thread.duplicates.count", "${duplicates.count()}")

app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt

+4
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,10 @@ class MainViewModel @Inject constructor(
10471047
snackbarManager.postValue(appContext.getString(snackbarTitleRes))
10481048
}
10491049

1050+
fun deleteThreadInRealm(threadUid: String) = viewModelScope.launch(ioCoroutineContext) {
1051+
threadController.deleteThread(threadUid)
1052+
}
1053+
10501054
private fun shouldAutoAdvance(message: Message?, threadsUids: List<String>): Boolean {
10511055
val isWorkingWithThread = message == null
10521056
return isWorkingWithThread || threadHasOnlyOneMessageLeftInCurrentFolder(threadsUids.first())

app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapter.kt

+10
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import com.infomaniak.mail.ui.main.folder.ThreadListAdapter.ThreadListViewHolder
6060
import com.infomaniak.mail.ui.main.thread.SubjectFormatter
6161
import com.infomaniak.mail.ui.main.thread.SubjectFormatter.TagColor
6262
import com.infomaniak.mail.utils.RealmChangesBinding
63+
import com.infomaniak.mail.utils.SentryDebug
6364
import com.infomaniak.mail.utils.Utils.runCatchingRealm
6465
import com.infomaniak.mail.utils.extensions.*
6566
import dagger.hilt.android.qualifiers.ActivityContext
@@ -191,6 +192,15 @@ class ThreadListAdapter @Inject constructor(
191192

192193
private fun CardviewThreadItemBinding.displayThread(thread: Thread, position: Int) {
193194

195+
// If we are trying to display an empty Thread, don't. Just delete it.
196+
if (thread.messages.isEmpty()) {
197+
// TODO: Find why we are sometimes displaying empty Threads, and fix it instead of just deleting them.
198+
// It's possibly because we are out of sync, and the situation will resolve by itself shortly?
199+
threadListAdapterCallback?.deleteThreadInRealm?.invoke(thread.uid)
200+
SentryDebug.sendEmptyThread(thread, "No Message in the Thread when displaying it in ThreadList")
201+
return
202+
}
203+
194204
refreshCachedSelectedPosition(thread.uid, position) // If item changed position, update cached position.
195205
setupThreadDensityDependentUi()
196206
displayAvatar(thread)

app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListAdapterCallback.kt

+1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ interface ThreadListAdapterCallback {
2525
var onFlushClicked: ((dialogTitle: String) -> Unit)?
2626
var onLoadMoreClicked: () -> Unit
2727
var onPositionClickedChanged: (position: Int, previousPosition: Int) -> Unit
28+
var deleteThreadInRealm: (threadUid: String) -> Unit
2829
}

app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen
322322
}
323323

324324
override var onPositionClickedChanged: (Int, Int) -> Unit = ::updateAutoAdvanceNaturalThread
325+
326+
override var deleteThreadInRealm: (String) -> Unit = { threadUid -> mainViewModel.deleteThreadInRealm(threadUid) }
325327
},
326328
multiSelection = object : MultiSelectionListener<Thread> {
327329
override var isEnabled by mainViewModel::isMultiSelectOn
@@ -715,7 +717,7 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen
715717
val currentFolder = mainViewModel.currentFolder.value
716718
Sentry.withScope { scope ->
717719
scope.setExtra("cursor", "$currentFolderCursor")
718-
scope.setExtra("folderRole", "${currentFolder?.role?.name}")
720+
scope.setExtra("folderRole", currentFolder?.role?.name.toString())
719721
scope.setExtra("folderThreadsCount", "${currentFolder?.threads?.count()}")
720722
Sentry.captureMessage(
721723
"Should display threads is true but there are no threads to display",

app/src/main/java/com/infomaniak/mail/ui/main/search/SearchFragment.kt

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ class SearchFragment : TwoPaneFragment() {
172172
}
173173

174174
override var onPositionClickedChanged: (Int, Int) -> Unit = ::updateAutoAdvanceNaturalThread
175+
176+
override var deleteThreadInRealm: (String) -> Unit = { threadUid -> mainViewModel.deleteThreadInRealm(threadUid) }
175177
},
176178
)
177179

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ class ThreadViewModel @Inject constructor(
274274
appContext.trackUserInfo("nbMessagesInThread", nbMessages)
275275

276276
when (nbMessages) {
277-
0 -> SentryDebug.sendEmptyThread(thread)
277+
0 -> SentryDebug.sendEmptyThread(thread, "No Message in the Thread when opening it")
278278
1 -> appContext.trackUserInfo("oneMessagesInThread")
279279
else -> appContext.trackUserInfo("multipleMessagesInThread", nbMessages)
280280
}

app/src/main/java/com/infomaniak/mail/utils/SentryDebug.kt

+14-7
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,21 @@ object SentryDebug {
117117
//region Send Sentry
118118
// TODO: Added the 04/09/23. It's not supposed to be possible, but we never know…
119119
// If this doesn't trigger after a certain amount of time, you can remove it.
120-
fun sendEmptyThread(thread: Thread) {
120+
//
121+
// Also added in ThreadListAdapter & ThreadController the 04/06/24.
122+
fun sendEmptyThread(thread: Thread, message: String) = with(thread) {
121123
Sentry.withScope { scope ->
122-
scope.setExtra("currentUserId", "[${AccountUtils.currentUserId}]")
123-
scope.setExtra("currentMailboxEmail", "[${AccountUtils.currentMailboxEmail}]")
124-
scope.setExtra("folder.role", thread.folder.role?.name.toString())
125-
scope.setExtra("folder.id", thread.folder.id)
126-
scope.setExtra("thread.uid", "[${thread.uid}]")
127-
Sentry.captureMessage("No Message in the Thread when opening it", SentryLevel.ERROR)
124+
scope.setExtra("currentUserId", "${AccountUtils.currentUserId}")
125+
scope.setExtra("currentMailboxEmail", AccountUtils.currentMailboxEmail.toString())
126+
scope.setExtra("folderId", folderId)
127+
scope.setExtra("folder.id", folder.id)
128+
scope.setExtra("folder.role", folder.role?.name.toString())
129+
scope.setExtra("uid", uid)
130+
scope.setExtra("messages.count", "${messages.count()}")
131+
scope.setExtra("duplicates.count", "${duplicates.count()}")
132+
scope.setExtra("isFromSearch", "$isFromSearch")
133+
scope.setExtra("hasDrafts", "$hasDrafts")
134+
Sentry.captureMessage(message, SentryLevel.ERROR)
128135
}
129136
}
130137

0 commit comments

Comments
 (0)