diff --git a/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt b/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt index c44f727c9a..93d7ce7cbc 100644 --- a/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt +++ b/app/src/main/java/com/infomaniak/mail/data/cache/RealmDatabase.kt @@ -162,7 +162,7 @@ object RealmDatabase { //region Configurations versions const val USER_INFO_SCHEMA_VERSION = 1L const val MAILBOX_INFO_SCHEMA_VERSION = 5L - const val MAILBOX_CONTENT_SCHEMA_VERSION = 13L + const val MAILBOX_CONTENT_SCHEMA_VERSION = 14L //endregion //region Configurations names diff --git a/app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/FolderController.kt b/app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/FolderController.kt index f8f248a321..50bef9fca0 100644 --- a/app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/FolderController.kt +++ b/app/src/main/java/com/infomaniak/mail/data/cache/mailboxContent/FolderController.kt @@ -45,8 +45,8 @@ class FolderController @Inject constructor( ) { //region Get data - fun getCustomFolders(): RealmResults { - return getCustomFoldersQuery(mailboxContentRealm()).find() + fun getMoveFolders(): RealmResults { + return getMoveFoldersQuery(mailboxContentRealm()).find() } fun getRootsFoldersAsync(): Flow> { @@ -147,7 +147,9 @@ class FolderController @Inject constructor( private fun getDefaultFoldersQuery(realm: TypedRealm): RealmQuery { val hasRole = "${Folder.rolePropertyName} != nil" - return realm.query("$isNotSearch AND $hasRole") + return realm + .query("$isNotSearch AND $hasRole") + .sort(Folder::roleOrder.name, Sort.DESCENDING) } private fun getCustomFoldersQuery(realm: TypedRealm): RealmQuery { @@ -162,6 +164,15 @@ class FolderController @Inject constructor( return realm.query("NOT ${Folder::id.name} IN $0 AND $isNotSearch", exceptionsFoldersIds) } + private fun getMoveFoldersQuery(realm: TypedRealm): RealmQuery { + val isNotDraft = "${Folder.rolePropertyName} != '${FolderRole.DRAFT.name}'" + return realm + .query("$isNotSearch AND $isRootFolder AND $isNotDraft") + .sort(Folder::name.name, Sort.ASCENDING) + .sort(Folder::isFavorite.name, Sort.DESCENDING) + .sort(Folder::roleOrder.name, Sort.DESCENDING) + } + private fun getFolderQuery(key: String, value: String, realm: TypedRealm): RealmSingleQuery { return realm.query("$key == $0", value).first() } diff --git a/app/src/main/java/com/infomaniak/mail/data/models/Folder.kt b/app/src/main/java/com/infomaniak/mail/data/models/Folder.kt index 84525fc9a6..34278e4edd 100644 --- a/app/src/main/java/com/infomaniak/mail/data/models/Folder.kt +++ b/app/src/main/java/com/infomaniak/mail/data/models/Folder.kt @@ -35,6 +35,7 @@ import io.realm.kotlin.serializers.RealmListKSerializer import io.realm.kotlin.types.RealmInstant import io.realm.kotlin.types.RealmList import io.realm.kotlin.types.RealmObject +import io.realm.kotlin.types.annotations.Ignore import io.realm.kotlin.types.annotations.PrimaryKey import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -43,7 +44,7 @@ import kotlinx.serialization.UseSerializers import kotlin.math.max @Serializable -class Folder : RealmObject { +class Folder : RealmObject, Cloneable { //region Remote data @PrimaryKey @@ -81,6 +82,14 @@ class Folder : RealmObject { var isHidden: Boolean = false // For children only (a children Folder is hidden if its parent is collapsed) @Transient var isCollapsed: Boolean = false // For parents only (collapsing a parent Folder will hide its children) + @Transient + var roleOrder: Int = CUSTOM_FOLDER_ROLE_ORDER + //endregion + + //region UI data (Transient & Ignore) + @Transient + @Ignore + var shouldDisplayDivider: Boolean = false //endregion private val _parents by backlinks(Folder::children) @@ -122,6 +131,7 @@ class Folder : RealmObject { this.isHistoryComplete = isHistoryComplete this.isHidden = isHidden this.isCollapsed = isCollapsed + this.roleOrder = role?.order ?: CUSTOM_FOLDER_ROLE_ORDER } fun getLocalizedName(context: Context): String { @@ -137,20 +147,26 @@ class Folder : RealmObject { override fun hashCode(): Int = id.hashCode() + /** + * We've had a reference issue when modifying the `shouldDisplayDivider` of a Folder before sending it to the Adapter. + * Cloning it to do a deep copy resolved the issue. + */ + public override fun clone() = super.clone() as Folder + enum class FolderRole( @StringRes val folderNameRes: Int, @DrawableRes val folderIconRes: Int, val order: Int, val matomoValue: String, ) { - INBOX(R.string.inboxFolder, R.drawable.ic_drawer_inbox, 0, "inboxFolder"), + INBOX(R.string.inboxFolder, R.drawable.ic_drawer_inbox, 8, "inboxFolder"), + COMMERCIAL(R.string.commercialFolder, R.drawable.ic_promotions, 7, "commercialFolder"), + SOCIALNETWORKS(R.string.socialNetworksFolder, R.drawable.ic_social_media, 6, "socialNetworksFolder"), + SENT(R.string.sentFolder, R.drawable.ic_sent_messages, 5, "sentFolder"), DRAFT(R.string.draftFolder, R.drawable.ic_draft, 4, "draftFolder"), - SENT(R.string.sentFolder, R.drawable.ic_sent_messages, 3, "sentFolder"), - SPAM(R.string.spamFolder, R.drawable.ic_spam, 5, "spamFolder"), - TRASH(R.string.trashFolder, R.drawable.ic_bin, 6, "trashFolder"), - ARCHIVE(R.string.archiveFolder, R.drawable.ic_archive_folder, 7, "archiveFolder"), - COMMERCIAL(R.string.commercialFolder, R.drawable.ic_promotions, 1, "commercialFolder"), - SOCIALNETWORKS(R.string.socialNetworksFolder, R.drawable.ic_social_media, 2, "socialNetworksFolder"), + SPAM(R.string.spamFolder, R.drawable.ic_spam, 3, "spamFolder"), + TRASH(R.string.trashFolder, R.drawable.ic_bin, 2, "trashFolder"), + ARCHIVE(R.string.archiveFolder, R.drawable.ic_archive_folder, 1, "archiveFolder"), } companion object { @@ -161,5 +177,6 @@ class Folder : RealmObject { const val DEFAULT_IS_HISTORY_COMPLETE = false const val INBOX_FOLDER_ID = "eJzz9HPyjwAABGYBgQ--" + private const val CUSTOM_FOLDER_ROLE_ORDER = 0 } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 83ee34d535..707977a836 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -62,10 +62,9 @@ import com.infomaniak.mail.data.models.draft.Draft.DraftAction import com.infomaniak.mail.databinding.ActivityMainBinding import com.infomaniak.mail.firebase.RegisterFirebaseBroadcastReceiver import com.infomaniak.mail.ui.alertDialogs.DescriptionAlertDialog -import com.infomaniak.mail.ui.alertDialogs.TitleAlertDialog import com.infomaniak.mail.ui.main.SnackbarManager import com.infomaniak.mail.ui.main.folder.TwoPaneFragment -import com.infomaniak.mail.ui.main.menu.MenuDrawerFragment +import com.infomaniak.mail.ui.main.menuDrawer.MenuDrawerFragment import com.infomaniak.mail.ui.main.onboarding.PermissionsOnboardingPagerFragment import com.infomaniak.mail.ui.main.search.SearchFragmentArgs import com.infomaniak.mail.ui.newMessage.NewMessageActivity @@ -139,9 +138,6 @@ class MainActivity : BaseActivity() { @Inject lateinit var descriptionDialog: DescriptionAlertDialog - @Inject - lateinit var titleDialog: TitleAlertDialog - @Inject lateinit var permissionUtils: PermissionUtils diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 8eb2eecb6f..fe6ca02af0 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -138,12 +138,12 @@ class MainViewModel @Inject constructor( }.asLiveData(ioCoroutineContext) val currentDefaultFoldersLive = _currentMailboxObjectId.flatMapLatest { objectId -> - objectId?.let { folderController.getDefaultFoldersAsync().map { it.list.getDefaultMenuFolders() } } ?: emptyFlow() + objectId?.let { folderController.getDefaultFoldersAsync().map { it.list.flattenFolderChildren() } } ?: emptyFlow() }.asLiveData(ioCoroutineContext) val currentCustomFoldersLive = _currentMailboxObjectId.flatMapLatest { objectId -> objectId - ?.let { folderController.getCustomFoldersAsync().map { it.list.getCustomMenuFolders(dismissHiddenChildren = true) } } + ?.let { folderController.getCustomFoldersAsync().map { it.list.flattenFolderChildren(dismissHiddenChildren = true) } } ?: emptyFlow() }.asLiveData(ioCoroutineContext) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/MailboxListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/MailboxListFragment.kt index f25975329b..3601569c25 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/MailboxListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/MailboxListFragment.kt @@ -23,7 +23,7 @@ import com.infomaniak.lib.core.utils.safeNavigate import com.infomaniak.mail.R import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.ui.bottomSheetDialogs.LockedMailboxBottomSheetDialogArgs -import com.infomaniak.mail.ui.main.menu.MailboxesAdapter +import com.infomaniak.mail.ui.main.menuDrawer.MailboxesAdapter import com.infomaniak.mail.utils.AccountUtils import kotlinx.coroutines.launch @@ -49,7 +49,7 @@ interface MailboxListFragment { ) } - fun Fragment.onValidMailboxClicked(mailboxId: Int) { - lifecycleScope.launch { AccountUtils.switchToMailbox(mailboxId) } + fun Fragment.onValidMailboxClicked(mailboxId: Int) = lifecycleScope.launch { + AccountUtils.switchToMailbox(mailboxId) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MailboxesAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MailboxesAdapter.kt similarity index 92% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MailboxesAdapter.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MailboxesAdapter.kt index 91beb933fd..43d0fc30b2 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MailboxesAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MailboxesAdapter.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import android.view.LayoutInflater import android.view.View.OnClickListener @@ -29,9 +29,9 @@ import com.infomaniak.mail.MatomoMail.trackMenuDrawerEvent import com.infomaniak.mail.R import com.infomaniak.mail.data.models.mailbox.Mailbox import com.infomaniak.mail.databinding.ItemInvalidMailboxBinding -import com.infomaniak.mail.databinding.ItemMailboxMenuDrawerBinding +import com.infomaniak.mail.databinding.ItemMenuDrawerMailboxBinding import com.infomaniak.mail.databinding.ItemSelectableMailboxBinding -import com.infomaniak.mail.ui.main.menu.MailboxesAdapter.MailboxesViewHolder +import com.infomaniak.mail.ui.main.menuDrawer.MailboxesAdapter.MailboxesViewHolder import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.Utils.runCatchingRealm import com.infomaniak.mail.views.itemViews.DecoratedItemView.SelectionStyle @@ -50,7 +50,7 @@ class MailboxesAdapter( val layoutInflater = LayoutInflater.from(parent.context) val binding = when (viewType) { DisplayType.SIMPLE_MAILBOX.layout -> ItemSelectableMailboxBinding.inflate(layoutInflater, parent, false) - DisplayType.MENU_DRAWER_MAILBOX.layout -> ItemMailboxMenuDrawerBinding.inflate(layoutInflater, parent, false) + DisplayType.MENU_DRAWER_MAILBOX.layout -> ItemMenuDrawerMailboxBinding.inflate(layoutInflater, parent, false) else -> ItemInvalidMailboxBinding.inflate(layoutInflater, parent, false) } @@ -67,7 +67,7 @@ class MailboxesAdapter( (this as ItemSelectableMailboxBinding).displaySimpleMailbox(mailbox, isCurrentMailbox) } DisplayType.MENU_DRAWER_MAILBOX.layout -> { - (this as ItemMailboxMenuDrawerBinding).displayMenuDrawerMailbox(mailbox, isCurrentMailbox) + (this as ItemMenuDrawerMailboxBinding).displayMenuDrawerMailbox(mailbox, isCurrentMailbox) } DisplayType.INVALID_MAILBOX.layout -> (this as ItemInvalidMailboxBinding).displayInvalidMailbox(mailbox) } @@ -94,7 +94,7 @@ class MailboxesAdapter( setSelectedState(isCurrentMailbox) } - private fun ItemMailboxMenuDrawerBinding.displayMenuDrawerMailbox(mailbox: Mailbox, isCurrentMailbox: Boolean) = with(root) { + private fun ItemMenuDrawerMailboxBinding.displayMenuDrawerMailbox(mailbox: Mailbox, isCurrentMailbox: Boolean) = with(root) { displayValidMailbox(mailbox, isCurrentMailbox) { context.trackMenuDrawerEvent(SWITCH_MAILBOX_NAME) } unreadCount = mailbox.unreadCountDisplay.count @@ -141,7 +141,7 @@ class MailboxesAdapter( private enum class DisplayType(val layout: Int) { INVALID_MAILBOX(R.layout.item_invalid_mailbox), - MENU_DRAWER_MAILBOX(R.layout.item_mailbox_menu_drawer), + MENU_DRAWER_MAILBOX(R.layout.item_menu_drawer_mailbox), SIMPLE_MAILBOX(R.layout.item_selectable_mailbox), } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerDropdownView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerDropdownView.kt similarity index 98% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerDropdownView.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerDropdownView.kt index 90dac98361..37ceb92b07 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerDropdownView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerDropdownView.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerFragment.kt similarity index 99% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerFragment.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerFragment.kt index 2c0730a63b..9bc491814c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerFragment.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import android.annotation.SuppressLint import android.content.Intent diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt similarity index 97% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerViewModel.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt index daddd8ad65..2264856ff5 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuDrawerViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuDrawerViewModel.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuFoldersFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuFoldersFragment.kt similarity index 97% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuFoldersFragment.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuFoldersFragment.kt index 04121926ec..8970446578 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MenuFoldersFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/MenuFoldersFragment.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import android.os.Bundle import android.view.View @@ -26,6 +26,7 @@ import com.infomaniak.mail.R import com.infomaniak.mail.data.cache.mailboxContent.FolderController import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.alertDialogs.InputAlertDialog +import com.infomaniak.mail.ui.main.move.FolderAdapter import com.infomaniak.mail.utils.extensions.bindAlertToViewLifecycle import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/RestoreEmailsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/RestoreEmailsBottomSheetDialog.kt similarity index 99% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/RestoreEmailsBottomSheetDialog.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/RestoreEmailsBottomSheetDialog.kt index 9264fabfbf..08e9dbf45c 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/RestoreEmailsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/RestoreEmailsBottomSheetDialog.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import android.os.Bundle import android.view.LayoutInflater diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/RestoreEmailsViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/RestoreEmailsViewModel.kt similarity index 97% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/RestoreEmailsViewModel.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/RestoreEmailsViewModel.kt index 55c5245053..6e06fd5a04 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/RestoreEmailsViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/RestoreEmailsViewModel.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/SimpleSettingView.kt b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/SimpleSettingView.kt similarity index 98% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/SimpleSettingView.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/SimpleSettingView.kt index b25695e791..62c08a4f6b 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/SimpleSettingView.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/menuDrawer/SimpleSettingView.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.menuDrawer import android.content.Context import android.util.AttributeSet diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/FolderAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/move/FolderAdapter.kt similarity index 79% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/FolderAdapter.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/move/FolderAdapter.kt index ff9f408802..bf08bf7f2d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/FolderAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/move/FolderAdapter.kt @@ -15,25 +15,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.move import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.ViewGroup import androidx.annotation.DrawableRes import androidx.appcompat.content.res.AppCompatResources -import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView.Adapter +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.viewbinding.ViewBinding import com.infomaniak.mail.MatomoMail.trackMenuDrawerEvent import com.infomaniak.mail.R import com.infomaniak.mail.data.models.Folder import com.infomaniak.mail.data.models.Folder.FolderRole -import com.infomaniak.mail.databinding.ItemFolderMenuDrawerBinding +import com.infomaniak.mail.databinding.ItemMenuDrawerFolderBinding import com.infomaniak.mail.databinding.ItemSelectableFolderBinding -import com.infomaniak.mail.ui.main.menu.FolderAdapter.FolderViewHolder +import com.infomaniak.mail.ui.main.move.FolderAdapter.FolderViewHolder +import com.infomaniak.mail.utils.UiUtils import com.infomaniak.mail.utils.UnreadDisplay import com.infomaniak.mail.utils.Utils.runCatchingRealm import com.infomaniak.mail.views.itemViews.* @@ -43,23 +43,21 @@ import kotlin.math.min class FolderAdapter @Inject constructor( private val globalCoroutineScope: CoroutineScope, -) : Adapter() { +) : ListAdapter(FolderDiffCallback()) { - private val foldersDiffer = AsyncListDiffer(this, FolderDiffCallback()) + private inline val folders: List get() = currentList - private inline val folders get() = foldersDiffer.currentList + private var setFoldersJob: Job? = null private var currentFolderId: String? = null private var hasCollapsableFolder: Boolean? = null - private var isInMenuDrawer: Boolean = true private var shouldIndent: Boolean = true + private lateinit var onFolderClicked: (folderId: String) -> Unit private var onCollapseClicked: ((folderId: String, shouldCollapse: Boolean) -> Unit)? = null private var onCollapseTransition: ((Boolean) -> Unit)? = null - private var setFoldersJob: Job? = null - operator fun invoke( isInMenuDrawer: Boolean, shouldIndent: Boolean = true, @@ -76,12 +74,18 @@ class FolderAdapter @Inject constructor( return this } + override fun getItemCount(): Int = runCatchingRealm { folders.size }.getOrDefault(0) + + override fun getItemViewType(position: Int): Int { + return if (isInMenuDrawer) DisplayType.MENU_DRAWER.layout else DisplayType.SELECTABLE_FOLDER.layout + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder { val layoutInflater = LayoutInflater.from(parent.context) val binding = if (viewType == DisplayType.SELECTABLE_FOLDER.layout) { ItemSelectableFolderBinding.inflate(layoutInflater, parent, false) } else { - ItemFolderMenuDrawerBinding.inflate(layoutInflater, parent, false) + ItemMenuDrawerFolderBinding.inflate(layoutInflater, parent, false) } return FolderViewHolder(binding) @@ -103,16 +107,10 @@ class FolderAdapter @Inject constructor( when (getItemViewType(position)) { DisplayType.SELECTABLE_FOLDER.layout -> (this as ItemSelectableFolderBinding).root.displayFolder(folder) - DisplayType.MENU_DRAWER.layout -> (this as ItemFolderMenuDrawerBinding).root.displayMenuDrawerFolder(folder) + DisplayType.MENU_DRAWER.layout -> (this as ItemMenuDrawerFolderBinding).root.displayMenuDrawerFolder(folder) } } - override fun getItemViewType(position: Int): Int { - return if (isInMenuDrawer) DisplayType.MENU_DRAWER.layout else DisplayType.SELECTABLE_FOLDER.layout - } - - override fun getItemCount(): Int = runCatchingRealm { folders.size }.getOrDefault(0) - private fun UnreadFolderItemView.displayMenuDrawerFolder(folder: Folder) { val unread = when (folder.role) { @@ -150,6 +148,7 @@ class FolderAdapter @Inject constructor( ) { val folderName = folder.getLocalizedName(context) + tag = if (folder.shouldDisplayDivider) null else UiUtils.IGNORE_DIVIDER_TAG text = folderName icon = AppCompatResources.getDrawable(context, iconId) setSelectedState(currentFolderId == folder.id) @@ -181,21 +180,41 @@ class FolderAdapter @Inject constructor( @SuppressLint("NotifyDataSetChanged") fun setFolders(newFolders: List, newCurrentFolderId: String? = null) = runCatchingRealm { - foldersDiffer.submitList(newFolders) - - setFoldersJob?.cancel() - setFoldersJob = globalCoroutineScope.launch { - newCurrentFolderId?.let { currentFolderId = it } - val newHasCollapsableFolder = newFolders.any { it.canBeCollapsed } - - val isFirstTime = hasCollapsableFolder == null - val collapsableFolderExistenceHasChanged = newHasCollapsableFolder != hasCollapsableFolder - hasCollapsableFolder = newHasCollapsableFolder + fun foldersWithDivider(): List { + var isFirstCustomFolder = true + return newFolders.map { folder -> + folder.clone().apply { + shouldDisplayDivider = if (folder.role == null && isFirstCustomFolder) { + isFirstCustomFolder = false + true + } else { + false + } + } + } + } - if (!isFirstTime && collapsableFolderExistenceHasChanged) { - Dispatchers.Main { notifyDataSetChanged() } + /** + * If there was no Folder with children, and then now there's at least one, we need to indent the whole Folders list. + */ + fun notifyCollapsableFolders() { + setFoldersJob?.cancel() + setFoldersJob = globalCoroutineScope.launch { + val newHasCollapsableFolder = newFolders.any { it.canBeCollapsed } + + val isFirstTime = hasCollapsableFolder == null + val collapsableFolderExistenceHasChanged = newHasCollapsableFolder != hasCollapsableFolder + hasCollapsableFolder = newHasCollapsableFolder + + if (!isFirstTime && collapsableFolderExistenceHasChanged) { + Dispatchers.Main { notifyDataSetChanged() } + } } } + + newCurrentFolderId?.let { currentFolderId = it } + submitList(foldersWithDivider()) + if (isInMenuDrawer) notifyCollapsableFolders() } fun updateSelectedState(newCurrentFolderId: String) { @@ -212,7 +231,7 @@ class FolderAdapter @Inject constructor( private enum class DisplayType(val layout: Int) { SELECTABLE_FOLDER(R.layout.item_selectable_folder), - MENU_DRAWER(R.layout.item_folder_menu_drawer), + MENU_DRAWER(R.layout.item_menu_drawer_folder), } companion object { @@ -234,7 +253,8 @@ class FolderAdapter @Inject constructor( oldFolder.unreadCountDisplay == newFolder.unreadCountDisplay && oldFolder.threads.count() == newFolder.threads.count() && oldFolder.isHidden == newFolder.isHidden && - oldFolder.canBeCollapsed == newFolder.canBeCollapsed + oldFolder.canBeCollapsed == newFolder.canBeCollapsed && + oldFolder.shouldDisplayDivider == newFolder.shouldDisplayDivider }.getOrDefault(false) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MoveFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/move/MoveFragment.kt similarity index 61% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MoveFragment.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/move/MoveFragment.kt index 94aeee2201..727c02c1df 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MoveFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/move/MoveFragment.kt @@ -15,29 +15,34 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.move +import android.graphics.drawable.InsetDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.view.isGone -import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.RecyclerView import com.infomaniak.lib.core.utils.hideKeyboard import com.infomaniak.lib.core.utils.safeBinding +import com.infomaniak.lib.core.views.DividerItemDecorator import com.infomaniak.mail.MatomoMail.SEARCH_DELETE_NAME import com.infomaniak.mail.MatomoMail.SEARCH_VALIDATE_NAME import com.infomaniak.mail.MatomoMail.trackCreateFolderEvent import com.infomaniak.mail.MatomoMail.trackMoveSearchEvent import com.infomaniak.mail.R +import com.infomaniak.mail.data.cache.mailboxContent.FolderController import com.infomaniak.mail.data.models.Folder -import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.databinding.FragmentMoveBinding +import com.infomaniak.mail.ui.MainViewModel +import com.infomaniak.mail.ui.alertDialogs.InputAlertDialog +import com.infomaniak.mail.utils.UiUtils +import com.infomaniak.mail.utils.extensions.bindAlertToViewLifecycle import com.infomaniak.mail.utils.extensions.handleEditorSearchAction import com.infomaniak.mail.utils.extensions.setOnClearTextClickListener import com.infomaniak.mail.utils.extensions.setSystemBarsColors @@ -45,27 +50,24 @@ import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @AndroidEntryPoint -class MoveFragment : MenuFoldersFragment() { +class MoveFragment : Fragment() { private var binding: FragmentMoveBinding by safeBinding() private val navigationArgs: MoveFragmentArgs by navArgs() private val moveViewModel: MoveViewModel by viewModels() + private val mainViewModel: MainViewModel by activityViewModels() @Inject - lateinit var searchFolderAdapter: FolderAdapter + lateinit var inputDialog: InputAlertDialog - private val searchResultsAdapter by lazy { - searchFolderAdapter(isInMenuDrawer, shouldIndent = false, onFolderClicked = ::onFolderSelected) - } - - private var hasAlreadyTrackedFolderSearch = false - - private var currentFolderId: String? = null + @Inject + lateinit var folderController: FolderController - override val defaultFoldersList: RecyclerView by lazy { binding.defaultFoldersList } - override val customFoldersList: RecyclerView by lazy { binding.customFoldersList } + @Inject + lateinit var folderAdapter: FolderAdapter - override val isInMenuDrawer = false + private var isSearching = false + private var hasAlreadyTrackedSearch = false override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return FragmentMoveBinding.inflate(inflater, container, false).also { binding = it }.root @@ -75,13 +77,28 @@ class MoveFragment : MenuFoldersFragment() { super.onViewCreated(view, savedInstanceState) setSystemBarsColors() + bindAlertToViewLifecycle(inputDialog) + setupRecyclerView() setupListeners() - setupFolderAdapters() + observeFolders() setupCreateFolderDialog() observeNewFolderCreation() observeSearchResults() } + private fun setupRecyclerView() = with(binding.foldersRecyclerView) { + + adapter = folderAdapter(isInMenuDrawer = false, onFolderClicked = ::onFolderSelected) + + val margin = resources.getDimensionPixelSize(R.dimen.dividerHorizontalPadding) + addItemDecoration( + DividerItemDecorator( + divider = InsetDrawable(UiUtils.dividerDrawable(requireContext()), margin, 0, margin, 0), + shouldIgnoreView = { view -> view.tag == UiUtils.IGNORE_DIVIDER_TAG }, + ), + ) + } + override fun onStop() { binding.searchTextInput.hideKeyboard() moveViewModel.cancelSearch() @@ -110,51 +127,26 @@ class MoveFragment : MenuFoldersFragment() { ) } - private fun setupFolderAdapters() { - moveViewModel.getFolderIdAndCustomFolders().observe(viewLifecycleOwner) { (folderId, customFolders) -> - - currentFolderId = folderId - - val defaultFoldersWithoutDraft = mainViewModel.currentDefaultFoldersLive.value!!.let { folders -> - folders.filterNot { it.role == FolderRole.DRAFT } - } - - defaultFoldersAdapter.setFolders(defaultFoldersWithoutDraft, folderId) - customFoldersAdapter.setFolders(customFolders, folderId) - setSearchBarUi(allFolders = defaultFoldersWithoutDraft + customFolders) - } - } - - private fun observeNewFolderCreation() = with(mainViewModel) { - newFolderResultTrigger.observe(viewLifecycleOwner) { inputDialog.resetLoadingAndDismiss() } - isMovedToNewFolder.observe(viewLifecycleOwner) { isFolderCreated -> - if (isFolderCreated) findNavController().popBackStack() - } + private fun observeFolders() { + moveViewModel.getFolders().observe(viewLifecycleOwner, ::setSearchBarUi) } - override fun onFolderSelected(folderId: String): Unit = with(navigationArgs) { - mainViewModel.moveThreadsOrMessageTo(folderId, threadsUids.toList(), messageUid) - findNavController().popBackStack() - } - - override fun onFolderCollapse(folderId: String, shouldCollapse: Boolean) = Unit - private fun setSearchBarUi(allFolders: List) = with(binding) { - searchResultsList.adapter = searchResultsAdapter searchInputLayout.setOnClearTextClickListener { trackMoveSearchEvent(SEARCH_DELETE_NAME) } searchTextInput.apply { - toggleFolderListsVisibility(!text.isNullOrBlank()) + toggleFolderListsVisibility(text?.toString(), allFolders) + doOnTextChanged { newQuery, _, _, _ -> - toggleFolderListsVisibility(!newQuery.isNullOrBlank()) + toggleFolderListsVisibility(newQuery?.toString(), allFolders) if (newQuery?.isNotBlank() == true) { moveViewModel.filterFolders(newQuery.toString(), allFolders, shouldDebounce = true) } - if (!hasAlreadyTrackedFolderSearch) { + if (!hasAlreadyTrackedSearch) { trackMoveSearchEvent("executeSearch") - hasAlreadyTrackedFolderSearch = true + hasAlreadyTrackedSearch = true } } @@ -165,14 +157,41 @@ class MoveFragment : MenuFoldersFragment() { } } - private fun toggleFolderListsVisibility(isSearching: Boolean) = with(binding) { - searchResultsList.isVisible = isSearching - moveFoldersLists.isGone = isSearching + private fun toggleFolderListsVisibility(query: String?, allFolders: List) { + isSearching = !query.isNullOrBlank() + if (!isSearching) folderAdapter.setFolders(allFolders, moveViewModel.currentFolderId) } - private fun observeSearchResults() { - moveViewModel.filterResults.observe(viewLifecycleOwner) { folders -> - searchResultsAdapter.setFolders(folders, currentFolderId) + private fun observeSearchResults() = with(moveViewModel) { + filterResults.observe(viewLifecycleOwner) { folders -> + if (isSearching) folderAdapter.setFolders(folders, currentFolderId) } } + + private fun observeNewFolderCreation() = with(mainViewModel) { + newFolderResultTrigger.observe(viewLifecycleOwner) { inputDialog.resetLoadingAndDismiss() } + isMovedToNewFolder.observe(viewLifecycleOwner) { isFolderCreated -> + if (isFolderCreated) findNavController().popBackStack() + } + } + + private fun onFolderSelected(folderId: String): Unit = with(navigationArgs) { + mainViewModel.moveThreadsOrMessageTo(folderId, threadsUids.toList(), messageUid) + findNavController().popBackStack() + } + + /** + * Asynchronously validate folder name locally + * @return error string, otherwise null + */ + private fun checkForFolderCreationErrors(folderName: CharSequence): String? = when { + folderName.length > 255 -> getString(R.string.errorNewFolderNameTooLong) + folderName.contains(Regex(INVALID_CHARACTERS_PATTERN)) -> getString(R.string.errorNewFolderInvalidCharacter) + folderController.getRootFolder(folderName.toString()) != null -> context?.getString(R.string.errorNewFolderAlreadyExists) + else -> null + } + + companion object { + private const val INVALID_CHARACTERS_PATTERN = "[/'\"]" + } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MoveViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/move/MoveViewModel.kt similarity index 88% rename from app/src/main/java/com/infomaniak/mail/ui/main/menu/MoveViewModel.kt rename to app/src/main/java/com/infomaniak/mail/ui/main/move/MoveViewModel.kt index 7fd9a56a16..944b8c34ea 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/menu/MoveViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/move/MoveViewModel.kt @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.infomaniak.mail.ui.main.menu +package com.infomaniak.mail.ui.main.move import android.app.Application import androidx.lifecycle.* @@ -26,7 +26,7 @@ import com.infomaniak.mail.data.models.Folder import com.infomaniak.mail.di.IoDispatcher import com.infomaniak.mail.utils.coroutineContext import com.infomaniak.mail.utils.extensions.appContext -import com.infomaniak.mail.utils.extensions.getCustomMenuFolders +import com.infomaniak.mail.utils.extensions.flattenFolderChildren import com.infomaniak.mail.utils.extensions.standardize import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.* @@ -49,20 +49,21 @@ class MoveViewModel @Inject constructor( private val messageUid inline get() = savedStateHandle.get(MoveFragmentArgs::messageUid.name) private val threadsUids inline get() = savedStateHandle.get>(MoveFragmentArgs::threadsUids.name)!! + var currentFolderId: String? = null var filterResults: MutableLiveData> = MutableLiveData() fun cancelSearch() { filterJob?.cancel() } - fun getFolderIdAndCustomFolders() = liveData(ioCoroutineContext) { + fun getFolders() = liveData(ioCoroutineContext) { - val folderId = messageUid?.let { messageController.getMessage(it)!!.folderId } + currentFolderId = messageUid?.let(messageController::getMessage)?.folderId ?: threadController.getThread(threadsUids.first())!!.folderId - val customFolders = folderController.getCustomFolders().getCustomMenuFolders() + val folders = folderController.getMoveFolders().flattenFolderChildren() - emit(folderId to customFolders) + emit(folders) } fun filterFolders(query: String, folders: List, shouldDebounce: Boolean) = viewModelScope.launch(ioCoroutineContext) { diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt index 4ea3ab02a7..803ec0cdc6 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadAdapter.kt @@ -75,7 +75,7 @@ class ThreadAdapter( private var threadAdapterCallbacks: ThreadAdapterCallbacks? = null, ) : ListAdapter(MessageDiffCallback()) { - inline val items: MutableList get() = currentList + inline val items: List get() = currentList //region Auto-scroll at Thread opening private val currentSetOfLoadedExpandedMessagesUids = mutableSetOf() @@ -168,7 +168,7 @@ class ThreadAdapter( val item = items[position] holder.binding.root.tag = if (item is SuperCollapsedBlock || (item is Message && item.shouldHideDivider)) { - IGNORE_DIVIDER_TAG + UiUtils.IGNORE_DIVIDER_TAG } else { null } @@ -784,9 +784,6 @@ class ThreadAdapter( } companion object { - - const val IGNORE_DIVIDER_TAG = "ignoreDividerTag" - private val contextMenuTypeForHitTestResultType = mapOf( HitTestResult.PHONE_TYPE to ContextMenuType.PHONE, HitTestResult.EMAIL_TYPE to ContextMenuType.EMAIL, diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt index d3e5c45e7c..524b731f07 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadFragment.kt @@ -74,6 +74,7 @@ import com.infomaniak.mail.ui.main.thread.actions.ReplyBottomSheetDialogArgs import com.infomaniak.mail.ui.main.thread.actions.ThreadActionsBottomSheetDialogArgs import com.infomaniak.mail.ui.main.thread.calendar.AttendeesBottomSheetDialogArgs import com.infomaniak.mail.utils.PermissionUtils +import com.infomaniak.mail.utils.UiUtils import com.infomaniak.mail.utils.UiUtils.dividerDrawable import com.infomaniak.mail.utils.extensions.* import com.infomaniak.mail.utils.extensions.AttachmentExtensions.openAttachment @@ -317,7 +318,7 @@ class ThreadFragment : Fragment() { binding.messagesList.addItemDecoration( DividerItemDecorator( divider = InsetDrawable(dividerDrawable(requireContext()), 0), - shouldIgnoreView = { view -> view.tag == ThreadAdapter.IGNORE_DIVIDER_TAG }, + shouldIgnoreView = { view -> view.tag == UiUtils.IGNORE_DIVIDER_TAG }, ), ) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt index b231a1904b..f1e637cbac 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MessageActionsBottomSheetDialog.kt @@ -38,7 +38,7 @@ import com.infomaniak.mail.R import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.draft.Draft.DraftMode import com.infomaniak.mail.ui.alertDialogs.DescriptionAlertDialog -import com.infomaniak.mail.ui.main.menu.MoveFragmentArgs +import com.infomaniak.mail.ui.main.move.MoveFragmentArgs import com.infomaniak.mail.ui.main.thread.PrintMailFragmentArgs import com.infomaniak.mail.utils.extensions.animatedNavigation import com.infomaniak.mail.utils.extensions.deleteWithConfirmationPopup diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt index 85da40a04b..13af0190d8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/ThreadActionsBottomSheetDialog.kt @@ -42,7 +42,7 @@ import com.infomaniak.mail.data.models.Folder.FolderRole import com.infomaniak.mail.data.models.draft.Draft.DraftMode import com.infomaniak.mail.data.models.thread.Thread import com.infomaniak.mail.ui.alertDialogs.DescriptionAlertDialog -import com.infomaniak.mail.ui.main.menu.MoveFragmentArgs +import com.infomaniak.mail.ui.main.move.MoveFragmentArgs import com.infomaniak.mail.utils.extensions.animatedNavigation import com.infomaniak.mail.utils.extensions.deleteWithConfirmationPopup import com.infomaniak.mail.utils.extensions.notYetImplemented diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/user/AccountFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/user/AccountFragment.kt index 15a2cd77c6..30644c63a8 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/user/AccountFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/user/AccountFragment.kt @@ -41,7 +41,7 @@ import com.infomaniak.mail.ui.MainActivity import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.alertDialogs.DescriptionAlertDialog import com.infomaniak.mail.ui.main.MailboxListFragment -import com.infomaniak.mail.ui.main.menu.MailboxesAdapter +import com.infomaniak.mail.ui.main.menuDrawer.MailboxesAdapter import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.ConfettiUtils import com.infomaniak.mail.utils.ConfettiUtils.ConfettiType diff --git a/app/src/main/java/com/infomaniak/mail/ui/noValidMailboxes/NoValidMailboxesFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/noValidMailboxes/NoValidMailboxesFragment.kt index 3f9b2e80e3..95d69f9777 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/noValidMailboxes/NoValidMailboxesFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/noValidMailboxes/NoValidMailboxesFragment.kt @@ -32,7 +32,7 @@ import com.infomaniak.mail.MatomoMail.trackNoValidMailboxesEvent import com.infomaniak.mail.R import com.infomaniak.mail.databinding.FragmentNoValidMailboxesBinding import com.infomaniak.mail.ui.main.MailboxListFragment -import com.infomaniak.mail.ui.main.menu.MailboxesAdapter +import com.infomaniak.mail.ui.main.menuDrawer.MailboxesAdapter import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint diff --git a/app/src/main/java/com/infomaniak/mail/utils/UiUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/UiUtils.kt index 18d8f28963..af209bd386 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/UiUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/UiUtils.kt @@ -37,6 +37,7 @@ import com.infomaniak.mail.utils.extensions.updateNavigationBarColor object UiUtils { const val FULLY_SLID = 1.0f + const val IGNORE_DIVIDER_TAG = "ignoreDividerTag" @ColorInt fun pointBetweenColors(@ColorInt from: Int, @ColorInt to: Int, percent: Float): Int { diff --git a/app/src/main/java/com/infomaniak/mail/utils/extensions/Extensions.kt b/app/src/main/java/com/infomaniak/mail/utils/extensions/Extensions.kt index f589840293..f60ed111be 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/extensions/Extensions.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/extensions/Extensions.kt @@ -301,14 +301,6 @@ fun List.getMenuFolders(): Pair, List> { } } -fun List.getDefaultMenuFolders(): List { - return sortedBy { it.role?.order }.flattenFolderChildren() -} - -fun List.getCustomMenuFolders(dismissHiddenChildren: Boolean = false): List { - return flattenFolderChildren(dismissHiddenChildren) -} - fun List.flattenFolderChildren(dismissHiddenChildren: Boolean = false): List { if (isEmpty()) return this diff --git a/app/src/main/java/com/infomaniak/mail/views/BottomSheetScaffoldingView.kt b/app/src/main/java/com/infomaniak/mail/views/BottomSheetScaffoldingView.kt index 46c8ce36a4..7f42518741 100644 --- a/app/src/main/java/com/infomaniak/mail/views/BottomSheetScaffoldingView.kt +++ b/app/src/main/java/com/infomaniak/mail/views/BottomSheetScaffoldingView.kt @@ -31,7 +31,7 @@ import com.infomaniak.lib.core.utils.getAttributes import com.infomaniak.lib.core.utils.setMarginsRelative import com.infomaniak.mail.R import com.infomaniak.mail.databinding.ViewBottomSheetScaffoldingBinding -import com.infomaniak.mail.ui.main.menu.SimpleSettingView +import com.infomaniak.mail.ui.main.menuDrawer.SimpleSettingView import com.infomaniak.lib.core.R as RCore class BottomSheetScaffoldingView @JvmOverloads constructor( diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 25e06ea757..fdb641aef3 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -46,7 +46,7 @@ diff --git a/app/src/main/res/layout/bottom_sheet_restore_emails.xml b/app/src/main/res/layout/bottom_sheet_restore_emails.xml index 70bdd8fb90..1bfe6fea15 100644 --- a/app/src/main/res/layout/bottom_sheet_restore_emails.xml +++ b/app/src/main/res/layout/bottom_sheet_restore_emails.xml @@ -19,7 +19,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.main.menu.RestoreEmailsBottomSheetDialog"> + tools:context=".ui.main.menuDrawer.RestoreEmailsBottomSheetDialog"> . --> - - + diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml index 7d32d1d6a1..021c64cb7c 100644 --- a/app/src/main/res/layout/fragment_account.xml +++ b/app/src/main/res/layout/fragment_account.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_ai_engine_setting.xml b/app/src/main/res/layout/fragment_ai_engine_setting.xml index a98e4e79c1..3f7e25e709 100644 --- a/app/src/main/res/layout/fragment_ai_engine_setting.xml +++ b/app/src/main/res/layout/fragment_ai_engine_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_attach_mailbox.xml b/app/src/main/res/layout/fragment_attach_mailbox.xml index 8145546a67..2a3db7dbee 100644 --- a/app/src/main/res/layout/fragment_attach_mailbox.xml +++ b/app/src/main/res/layout/fragment_attach_mailbox.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_auto_advance_settings.xml b/app/src/main/res/layout/fragment_auto_advance_settings.xml index a4c7cf86f1..0b72fb012b 100644 --- a/app/src/main/res/layout/fragment_auto_advance_settings.xml +++ b/app/src/main/res/layout/fragment_auto_advance_settings.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_cancel_delay_setting.xml b/app/src/main/res/layout/fragment_cancel_delay_setting.xml index 4b5530032c..014cda1d37 100644 --- a/app/src/main/res/layout/fragment_cancel_delay_setting.xml +++ b/app/src/main/res/layout/fragment_cancel_delay_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_data_management_matomo_setting.xml b/app/src/main/res/layout/fragment_data_management_matomo_setting.xml index 6db8f35a4e..2c4a925e9e 100644 --- a/app/src/main/res/layout/fragment_data_management_matomo_setting.xml +++ b/app/src/main/res/layout/fragment_data_management_matomo_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_data_management_sentry_setting.xml b/app/src/main/res/layout/fragment_data_management_sentry_setting.xml index ec062589a4..425f071a0a 100644 --- a/app/src/main/res/layout/fragment_data_management_sentry_setting.xml +++ b/app/src/main/res/layout/fragment_data_management_sentry_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_data_management_settings.xml b/app/src/main/res/layout/fragment_data_management_settings.xml index 2f7b36e44d..bc1d7194ba 100644 --- a/app/src/main/res/layout/fragment_data_management_settings.xml +++ b/app/src/main/res/layout/fragment_data_management_settings.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_external_content_setting.xml b/app/src/main/res/layout/fragment_external_content_setting.xml index 0cbef0438d..e562404f0b 100644 --- a/app/src/main/res/layout/fragment_external_content_setting.xml +++ b/app/src/main/res/layout/fragment_external_content_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_forward_mails_setting.xml b/app/src/main/res/layout/fragment_forward_mails_setting.xml index acac90da3f..d95723dc81 100644 --- a/app/src/main/res/layout/fragment_forward_mails_setting.xml +++ b/app/src/main/res/layout/fragment_forward_mails_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_invalid_password.xml b/app/src/main/res/layout/fragment_invalid_password.xml index 36729497a2..6ff1e9888b 100644 --- a/app/src/main/res/layout/fragment_invalid_password.xml +++ b/app/src/main/res/layout/fragment_invalid_password.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_mailbox_settings.xml b/app/src/main/res/layout/fragment_mailbox_settings.xml index 8ac4faf7bd..fc781c3f40 100644 --- a/app/src/main/res/layout/fragment_mailbox_settings.xml +++ b/app/src/main/res/layout/fragment_mailbox_settings.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_menu_drawer.xml b/app/src/main/res/layout/fragment_menu_drawer.xml index a6e462be32..35f8ec2cc8 100644 --- a/app/src/main/res/layout/fragment_menu_drawer.xml +++ b/app/src/main/res/layout/fragment_menu_drawer.xml @@ -131,7 +131,7 @@ android:visibility="gone" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:itemCount="3" - tools:listitem="@layout/item_mailbox_menu_drawer" + tools:listitem="@layout/item_menu_drawer_mailbox" tools:visibility="visible" /> + tools:listitem="@layout/item_menu_drawer_folder" /> - + tools:listitem="@layout/item_menu_drawer_folder" /> - - - - - - - - - - - - - - - - - + android:layout_height="match_parent" + android:background="@color/backgroundColor" + android:clipToPadding="false" + android:fillViewport="true" + android:paddingBottom="@dimen/marginStandardMedium" + android:scrollbars="none" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:itemCount="6" + tools:listitem="@layout/item_menu_drawer_folder" /> diff --git a/app/src/main/res/layout/fragment_send_settings.xml b/app/src/main/res/layout/fragment_send_settings.xml index 8988e91455..78277cc425 100644 --- a/app/src/main/res/layout/fragment_send_settings.xml +++ b/app/src/main/res/layout/fragment_send_settings.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index b5ce44e272..b0b517f673 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_signature_setting.xml b/app/src/main/res/layout/fragment_signature_setting.xml index 4c46390fc7..ebcd1d3854 100644 --- a/app/src/main/res/layout/fragment_signature_setting.xml +++ b/app/src/main/res/layout/fragment_signature_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_swipe_actions_selection_setting.xml b/app/src/main/res/layout/fragment_swipe_actions_selection_setting.xml index 45a2ac87b4..710fc4117b 100644 --- a/app/src/main/res/layout/fragment_swipe_actions_selection_setting.xml +++ b/app/src/main/res/layout/fragment_swipe_actions_selection_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_swipe_actions_settings.xml b/app/src/main/res/layout/fragment_swipe_actions_settings.xml index 158e9b9ba6..dfeb8875f2 100644 --- a/app/src/main/res/layout/fragment_swipe_actions_settings.xml +++ b/app/src/main/res/layout/fragment_swipe_actions_settings.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_switch_user.xml b/app/src/main/res/layout/fragment_switch_user.xml index 5fffc5a9fe..37a0c50520 100644 --- a/app/src/main/res/layout/fragment_switch_user.xml +++ b/app/src/main/res/layout/fragment_switch_user.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_theme_setting.xml b/app/src/main/res/layout/fragment_theme_setting.xml index 862b566e78..c866e2b96a 100644 --- a/app/src/main/res/layout/fragment_theme_setting.xml +++ b/app/src/main/res/layout/fragment_theme_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_thread_list_density_setting.xml b/app/src/main/res/layout/fragment_thread_list_density_setting.xml index c304c330e6..9309c01476 100644 --- a/app/src/main/res/layout/fragment_thread_list_density_setting.xml +++ b/app/src/main/res/layout/fragment_thread_list_density_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/fragment_thread_mode_setting.xml b/app/src/main/res/layout/fragment_thread_mode_setting.xml index e44cb20678..258a544355 100644 --- a/app/src/main/res/layout/fragment_thread_mode_setting.xml +++ b/app/src/main/res/layout/fragment_thread_mode_setting.xml @@ -15,7 +15,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/app/src/main/res/layout/item_folder_menu_drawer.xml b/app/src/main/res/layout/item_menu_drawer_folder.xml similarity index 100% rename from app/src/main/res/layout/item_folder_menu_drawer.xml rename to app/src/main/res/layout/item_menu_drawer_folder.xml diff --git a/app/src/main/res/layout/item_mailbox_menu_drawer.xml b/app/src/main/res/layout/item_menu_drawer_mailbox.xml similarity index 100% rename from app/src/main/res/layout/item_mailbox_menu_drawer.xml rename to app/src/main/res/layout/item_menu_drawer_mailbox.xml diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml index 9bf2aaae7f..fe3c781b91 100644 --- a/app/src/main/res/navigation/main_navigation.xml +++ b/app/src/main/res/navigation/main_navigation.xml @@ -240,7 +240,7 @@