Skip to content

Delete Thread in Realm if it's empty and we're trying to display it in ThreadsList #1876

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ class RefreshController @Inject constructor(
setupConfiguration(refreshMode, mailbox, folder, realm, okHttpClient, callbacks)

return refreshWithRunCatching(refreshThreadsJob!!).also { (threads, _) ->

ThreadController.deleteEmptyThreadsInFolder(folder.id, realm)

if (threads != null) {
onStop?.invoke()
SentryLog.d("API", "End of refreshing threads with mode: $refreshMode | (${folder.displayForSentry()})")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.infomaniak.mail.data.models.thread.Thread.ThreadFilter
import com.infomaniak.mail.di.IoDispatcher
import com.infomaniak.mail.utils.ErrorCode
import com.infomaniak.mail.utils.SearchUtils.Companion.convertToSearchThreads
import com.infomaniak.mail.utils.SentryDebug
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.Realm
import io.realm.kotlin.TypedRealm
Expand Down Expand Up @@ -159,6 +160,12 @@ class ThreadController @Inject constructor(
}
}
}

fun deleteThread(threadUid: String) {
mailboxContentRealm().writeBlocking {
delete(getThreadQuery(threadUid, realm = this))
}
}
//endregion

companion object {
Expand Down Expand Up @@ -208,6 +215,12 @@ class ThreadController @Inject constructor(
private fun getThreadQuery(uid: String, realm: TypedRealm): RealmSingleQuery<Thread> {
return realm.query<Thread>("${Thread::uid.name} == $0", uid).first()
}

private fun getEmptyThreadsInFolderQuery(folderId: String, realm: TypedRealm): RealmQuery<Thread> {
val noMessages = "${Thread::messages.name}.@size == $0"
return FolderController.getFolder(folderId, realm)?.threads?.query(noMessages, 0)
?: realm.query<Thread>("$noMessages AND ${Thread::folderId.name} == $1", 0, folderId)
}
//endregion

//region Get data
Expand Down Expand Up @@ -319,7 +332,22 @@ class ThreadController @Inject constructor(
}

fun deleteSearchThreads(realm: MutableRealm) = with(realm) {
delete(query<Thread>("${Thread::isFromSearch.name} == true").find())
delete(query<Thread>("${Thread::isFromSearch.name} == true"))
}

fun deleteEmptyThreadsInFolder(folderId: String, realm: Realm) {
realm.writeBlocking {
val emptyThreadsQuery = getEmptyThreadsInFolderQuery(folderId, realm = this)
val emptyThreads = emptyThreadsQuery.find()
// TODO: Find why we are sometimes displaying empty Threads, and fix it instead of just deleting them.
// It's possibly because we are out of sync, and the situation will resolve by itself shortly?
if (emptyThreads.isNotEmpty()) {
emptyThreads.forEach {
SentryDebug.sendEmptyThread(it, "No Message in a Thread when refreshing a Folder")
}
delete(emptyThreadsQuery)
}
}
}

private fun verifyAttachmentsValues(hasAttachmentsInThread: Boolean, messages: List<Message>, realm: MutableRealm) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,9 @@ class Thread : RealmObject {

}.getOrElse { throwable ->
Sentry.withScope { scope ->
scope.setExtra("thread.folder.role", "${folder.role?.name}")
scope.setExtra("thread.folder.role", folder.role?.name.toString())
scope.setExtra("thread.folder.id", folder.id)
scope.setExtra("thread.folderId", folderId)
scope.setExtra("thread.uid", uid)
scope.setExtra("thread.messages.count", "${messages.count()}")
scope.setExtra("thread.duplicates.count", "${duplicates.count()}")
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,10 @@ class MainViewModel @Inject constructor(
snackbarManager.postValue(appContext.getString(snackbarTitleRes))
}

fun deleteThreadInRealm(threadUid: String) = viewModelScope.launch(ioCoroutineContext) {
threadController.deleteThread(threadUid)
}

private fun shouldAutoAdvance(message: Message?, threadsUids: List<String>): Boolean {
val isWorkingWithThread = message == null
return isWorkingWithThread || threadHasOnlyOneMessageLeftInCurrentFolder(threadsUids.first())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import com.infomaniak.mail.ui.main.folder.ThreadListAdapter.ThreadListViewHolder
import com.infomaniak.mail.ui.main.thread.SubjectFormatter
import com.infomaniak.mail.ui.main.thread.SubjectFormatter.TagColor
import com.infomaniak.mail.utils.RealmChangesBinding
import com.infomaniak.mail.utils.SentryDebug
import com.infomaniak.mail.utils.Utils.runCatchingRealm
import com.infomaniak.mail.utils.extensions.*
import dagger.hilt.android.qualifiers.ActivityContext
Expand Down Expand Up @@ -191,6 +192,15 @@ class ThreadListAdapter @Inject constructor(

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

// If we are trying to display an empty Thread, don't. Just delete it.
if (thread.messages.isEmpty()) {
// TODO: Find why we are sometimes displaying empty Threads, and fix it instead of just deleting them.
// It's possibly because we are out of sync, and the situation will resolve by itself shortly?
threadListAdapterCallback?.deleteThreadInRealm?.invoke(thread.uid)
SentryDebug.sendEmptyThread(thread, "No Message in the Thread when displaying it in ThreadList")
return
}

refreshCachedSelectedPosition(thread.uid, position) // If item changed position, update cached position.
setupThreadDensityDependentUi()
displayAvatar(thread)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ interface ThreadListAdapterCallback {
var onFlushClicked: ((dialogTitle: String) -> Unit)?
var onLoadMoreClicked: () -> Unit
var onPositionClickedChanged: (position: Int, previousPosition: Int) -> Unit
var deleteThreadInRealm: (threadUid: String) -> Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen
}

override var onPositionClickedChanged: (Int, Int) -> Unit = ::updateAutoAdvanceNaturalThread

override var deleteThreadInRealm: (String) -> Unit = { threadUid -> mainViewModel.deleteThreadInRealm(threadUid) }
},
multiSelection = object : MultiSelectionListener<Thread> {
override var isEnabled by mainViewModel::isMultiSelectOn
Expand Down Expand Up @@ -715,7 +717,7 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen
val currentFolder = mainViewModel.currentFolder.value
Sentry.withScope { scope ->
scope.setExtra("cursor", "$currentFolderCursor")
scope.setExtra("folderRole", "${currentFolder?.role?.name}")
scope.setExtra("folderRole", currentFolder?.role?.name.toString())
scope.setExtra("folderThreadsCount", "${currentFolder?.threads?.count()}")
Sentry.captureMessage(
"Should display threads is true but there are no threads to display",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ class SearchFragment : TwoPaneFragment() {
}

override var onPositionClickedChanged: (Int, Int) -> Unit = ::updateAutoAdvanceNaturalThread

override var deleteThreadInRealm: (String) -> Unit = { threadUid -> mainViewModel.deleteThreadInRealm(threadUid) }
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ class ThreadViewModel @Inject constructor(
appContext.trackUserInfo("nbMessagesInThread", nbMessages)

when (nbMessages) {
0 -> SentryDebug.sendEmptyThread(thread)
0 -> SentryDebug.sendEmptyThread(thread, "No Message in the Thread when opening it")
1 -> appContext.trackUserInfo("oneMessagesInThread")
else -> appContext.trackUserInfo("multipleMessagesInThread", nbMessages)
}
Expand Down
21 changes: 14 additions & 7 deletions app/src/main/java/com/infomaniak/mail/utils/SentryDebug.kt
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,21 @@ object SentryDebug {
//region Send Sentry
// TODO: Added the 04/09/23. It's not supposed to be possible, but we never know…
// If this doesn't trigger after a certain amount of time, you can remove it.
fun sendEmptyThread(thread: Thread) {
//
// Also added in ThreadListAdapter & ThreadController the 04/06/24.
fun sendEmptyThread(thread: Thread, message: String) = with(thread) {
Sentry.withScope { scope ->
scope.setExtra("currentUserId", "[${AccountUtils.currentUserId}]")
scope.setExtra("currentMailboxEmail", "[${AccountUtils.currentMailboxEmail}]")
scope.setExtra("folder.role", thread.folder.role?.name.toString())
scope.setExtra("folder.id", thread.folder.id)
scope.setExtra("thread.uid", "[${thread.uid}]")
Sentry.captureMessage("No Message in the Thread when opening it", SentryLevel.ERROR)
scope.setExtra("currentUserId", "${AccountUtils.currentUserId}")
scope.setExtra("currentMailboxEmail", AccountUtils.currentMailboxEmail.toString())
scope.setExtra("folderId", folderId)
scope.setExtra("folder.id", folder.id)
scope.setExtra("folder.role", folder.role?.name.toString())
scope.setExtra("uid", uid)
scope.setExtra("messages.count", "${messages.count()}")
scope.setExtra("duplicates.count", "${duplicates.count()}")
scope.setExtra("isFromSearch", "$isFromSearch")
scope.setExtra("hasDrafts", "$hasDrafts")
Sentry.captureMessage(message, SentryLevel.ERROR)
}
}

Expand Down
Loading