Skip to content

Commit

Permalink
feat: Filter snooze in folders (#2221)
Browse files Browse the repository at this point in the history
  • Loading branch information
LunarX authored Mar 7, 2025
2 parents 7a45397 + 8fc41c1 commit b227e62
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Infomaniak Mail - Android
* Copyright (C) 2025 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.mail.data.cache.mailboxContent

import com.infomaniak.mail.data.models.message.Message
import com.infomaniak.mail.data.models.thread.Thread
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.TypedRealm

val defaultRefreshStrategy = object : DefaultRefreshStrategy {}

val inboxRefreshStrategy = object : DefaultRefreshStrategy {
override fun queryFolderThreads(folderId: String, realm: TypedRealm): List<Thread> {
return ThreadController.getInboxThreadsWithSnoozeFilter(withSnooze = false, realm = realm)
}
}

val snoozeRefreshStrategy = object : DefaultRefreshStrategy {
override fun queryFolderThreads(folderId: String, realm: TypedRealm): List<Thread> {
return ThreadController.getInboxThreadsWithSnoozeFilter(withSnooze = true, realm = realm)
}

override fun updateExistingMessageWhenAdded(remoteMessage: Message, realm: MutableRealm) {
MessageController.updateMessage(remoteMessage.uid, realm = realm) { localMessage ->
localMessage?.snoozeState = remoteMessage.snoozeState
localMessage?.snoozeEndDate = remoteMessage.snoozeEndDate
localMessage?.snoozeAction = remoteMessage.snoozeAction
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ class RefreshController @Inject constructor(
var inboxUnreadCount: Int? = null
FolderController.updateFolder(folder.id, realm = this) { mutableRealm, it ->

val allCurrentFolderThreads = ThreadController.getThreadsByFolderId(it.id, realm = mutableRealm)
val allCurrentFolderThreads = folder.refreshStrategy().queryFolderThreads(folder.id, mutableRealm)
it.threads.replaceContent(list = allCurrentFolderThreads)

inboxUnreadCount = updateFoldersUnreadCount(
Expand Down Expand Up @@ -512,6 +512,7 @@ class RefreshController @Inject constructor(

val impactedThreadsManaged = mutableSetOf<Thread>()
val addedMessagesUids = mutableListOf<Int>()
val refreshStrategy = folder.refreshStrategy()

remoteMessages.forEach { remoteMessage ->
scope.ensureActive()
Expand All @@ -520,6 +521,8 @@ class RefreshController @Inject constructor(

addedMessagesUids.add(remoteMessage.shortUid)

refreshStrategy.updateExistingMessageWhenAdded(remoteMessage, realm = this)

val newThread = if (isConversationMode) {
handleAddedMessage(scope, remoteMessage, impactedThreadsManaged)
} else {
Expand Down Expand Up @@ -764,11 +767,14 @@ class RefreshController @Inject constructor(
}
//endregion

// SCHEDULED_DRAFTS and SNOOZED need to be refreshed often because these folders
// only appear in the MenuDrawer when there is at least 1 email in it.
private val FOLDER_ROLES_TO_REFRESH_TOGETHER = setOf(
FolderRole.INBOX,
FolderRole.SENT,
FolderRole.DRAFT,
FolderRole.SCHEDULED_DRAFTS,
FolderRole.SNOOZED,
)

enum class RefreshMode {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Infomaniak Mail - Android
* Copyright (C) 2025 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.mail.data.cache.mailboxContent

import com.infomaniak.mail.data.models.message.Message
import com.infomaniak.mail.data.models.thread.Thread
import io.realm.kotlin.MutableRealm
import io.realm.kotlin.TypedRealm

interface RefreshStrategy {
fun queryFolderThreads(folderId: String, realm: TypedRealm): List<Thread>
fun updateExistingMessageWhenAdded(remoteMessage: Message, realm: MutableRealm)
}

interface DefaultRefreshStrategy : RefreshStrategy {
override fun queryFolderThreads(folderId: String, realm: TypedRealm): List<Thread> {
return ThreadController.getThreadsByFolderId(folderId, realm)
}

override fun updateExistingMessageWhenAdded(remoteMessage: Message, realm: MutableRealm) = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.infomaniak.mail.data.api.ApiRepository
import com.infomaniak.mail.data.cache.RealmDatabase
import com.infomaniak.mail.data.models.Folder
import com.infomaniak.mail.data.models.Folder.FolderRole
import com.infomaniak.mail.data.models.SnoozeState
import com.infomaniak.mail.data.models.SwissTransferContainer
import com.infomaniak.mail.data.models.message.Message
import com.infomaniak.mail.data.models.message.Message.MessageInitialState
Expand Down Expand Up @@ -242,6 +243,19 @@ class ThreadController @Inject constructor(
return realm.query<Thread>("${Thread::folderId.name} == $0", folderId)
}

private fun getThreadsWithSnoozeFilterQuery(
folderId: String,
withSnooze: Boolean,
realm: TypedRealm,
): RealmQuery<Thread> {
// Checking for snoozeEndDate and snoozeAction on top of _snoozeState mimics the webmail's behavior
// and helps to avoid displaying threads that are in an incoherent state on the API
val isSnoozedState = "_snoozeState == $1 AND snoozeEndDate != null AND snoozeAction != null"
val snoozeQuery = if (withSnooze) isSnoozedState else "NOT($isSnoozedState)"

return realm.query<Thread>("${Thread::folderId.name} == $0 AND $snoozeQuery", folderId, SnoozeState.Snoozed.apiValue)
}

private fun getThreadQuery(uid: String, realm: TypedRealm): RealmSingleQuery<Thread> {
return realm.query<Thread>("${Thread::uid.name} == $0", uid).first()
}
Expand Down Expand Up @@ -277,6 +291,11 @@ class ThreadController @Inject constructor(
fun getThreadsByFolderId(folderId: String, realm: TypedRealm): RealmResults<Thread> {
return getThreadsByFolderIdQuery(folderId, realm).find()
}

fun getInboxThreadsWithSnoozeFilter(withSnooze: Boolean, realm: TypedRealm): List<Thread> {
val inboxId = FolderController.getFolder(FolderRole.INBOX, realm)?.id ?: return emptyList()
return getThreadsWithSnoozeFilterQuery(inboxId, withSnooze, realm).find()
}
//endregion

//region Edit data
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/java/com/infomaniak/mail/data/models/Folder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import androidx.annotation.StringRes
import com.infomaniak.lib.core.utils.Utils.enumValueOfOrNull
import com.infomaniak.lib.core.utils.removeAccents
import com.infomaniak.mail.R
import com.infomaniak.mail.data.cache.mailboxContent.MessageController
import com.infomaniak.mail.data.cache.mailboxContent.*
import com.infomaniak.mail.data.models.message.Message
import com.infomaniak.mail.data.models.thread.Thread
import com.infomaniak.mail.utils.SentryDebug
Expand Down Expand Up @@ -146,6 +146,12 @@ class Folder : RealmObject, Cloneable {

fun messages(realm: TypedRealm): List<Message> = MessageController.getMessagesByFolderId(id, realm)

fun refreshStrategy(): RefreshStrategy = when (role) {
FolderRole.INBOX -> inboxRefreshStrategy
FolderRole.SNOOZED -> snoozeRefreshStrategy
else -> defaultRefreshStrategy
}

fun getLocalizedName(context: Context): String {
return role?.folderNameRes?.let(context::getString) ?: name
}
Expand Down

0 comments on commit b227e62

Please sign in to comment.