diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt index 3b6c2b1cef..8a6e6e24c0 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt @@ -326,16 +326,40 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { _binding = null } + private fun isAllowedToSwipe(swipeDirection: DirectionFlag): Boolean { + if (mainViewModel.currentFolderLive.value?.role != FolderRole.DRAFT) return true + + val action = if (swipeDirection == DirectionFlag.LEFT) { + localSettings.swipeLeft + } else { + localSettings.swipeRight + } + + val allowedSwipeActionsForDraft = listOf( + SwipeAction.DELETE, + SwipeAction.QUICKACTIONS_MENU, + SwipeAction.READ_UNREAD, + SwipeAction.FAVORITE + ) + + return action in allowedSwipeActionsForDraft + } + private fun unlockSwipeActionsIfSet() = with(binding.threadsList) { - val isMultiSelectClosed = mainViewModel.isMultiSelectOn.not() + val isMultiSelectClosed = !mainViewModel.isMultiSelectOn + + fun updateSwipeDirection(direction: DirectionFlag, action: SwipeAction) { + val isActionSet = action != SwipeAction.NONE - val isLeftSet = localSettings.swipeLeft != SwipeAction.NONE - val isLeftEnabled = isLeftSet && isMultiSelectClosed - if (isLeftEnabled) enableSwipeDirection(DirectionFlag.LEFT) else disableSwipeDirection(DirectionFlag.LEFT) + if (isMultiSelectClosed && isActionSet && isAllowedToSwipe(direction)) { + enableSwipeDirection(direction) + } else { + disableSwipeDirection(direction) + } + } - val isRightSet = localSettings.swipeRight != SwipeAction.NONE - val isRightEnabled = isRightSet && isMultiSelectClosed - if (isRightEnabled) enableSwipeDirection(DirectionFlag.RIGHT) else disableSwipeDirection(DirectionFlag.RIGHT) + updateSwipeDirection(DirectionFlag.LEFT, localSettings.swipeLeft) + updateSwipeDirection(DirectionFlag.RIGHT, localSettings.swipeRight) } private fun setupDensityDependentUi() = with(binding) { @@ -419,20 +443,46 @@ class ThreadListFragment : TwoPaneFragment(), PickerEmojiObserver { setSwipeActionEnabledUi(DirectionFlag.LEFT, isLeftEnabled) setSwipeActionEnabledUi(DirectionFlag.RIGHT, isRightEnabled) + + } + + private fun setSwipeActionEnabledUi(swipeDirection: DirectionFlag, isEnabled: Boolean) { + val action = if (swipeDirection == DirectionFlag.LEFT) localSettings.swipeLeft else localSettings.swipeRight + updateSwipeEnableState(swipeDirection, action) + updateSwipeVisuals(swipeDirection, isEnabled, action) } - private fun setSwipeActionEnabledUi(swipeDirection: DirectionFlag, isEnabled: Boolean) = with(binding.threadsList) { - fun SwipeAction.getIconRes(): Int? = if (isEnabled) iconRes else R.drawable.ic_close_small - fun SwipeAction.getBackgroundColor(): Int { - return if (isEnabled) getBackgroundColor(context) else SwipeAction.NONE.getBackgroundColor(context) + private fun updateSwipeEnableState(swipeDirection: DirectionFlag, action: SwipeAction) { + val isActionSet = action != SwipeAction.NONE + val isMultiSelectClosed = !mainViewModel.isMultiSelectOn + + val shouldEnableSwipe = isActionSet && isMultiSelectClosed && isAllowedToSwipe(swipeDirection) + + with(binding.threadsList) { + if (shouldEnableSwipe) { + enableSwipeDirection(swipeDirection) + } else { + disableSwipeDirection(swipeDirection) + } } + } - if (swipeDirection == DirectionFlag.LEFT) { - behindSwipedItemIconDrawableId = localSettings.swipeLeft.getIconRes() - behindSwipedItemBackgroundColor = localSettings.swipeLeft.getBackgroundColor() - } else { - behindSwipedItemIconSecondaryDrawableId = localSettings.swipeRight.getIconRes() - behindSwipedItemBackgroundSecondaryColor = localSettings.swipeRight.getBackgroundColor() + private fun updateSwipeVisuals(swipeDirection: DirectionFlag, isEnabled: Boolean, action: SwipeAction) { + with(binding.threadsList) { + val resolvedIconRes = if (isEnabled) action.iconRes else R.drawable.ic_close_small + val resolvedBackgroundColor = if (isEnabled) { + action.getBackgroundColor(context) + } else { + SwipeAction.NONE.getBackgroundColor(context) + } + + if (swipeDirection == DirectionFlag.LEFT) { + behindSwipedItemIconDrawableId = resolvedIconRes + behindSwipedItemBackgroundColor = resolvedBackgroundColor + } else { + behindSwipedItemIconSecondaryDrawableId = resolvedIconRes + behindSwipedItemBackgroundSecondaryColor = resolvedBackgroundColor + } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListMultiSelection.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListMultiSelection.kt index 357e30fc50..58f5a16d4d 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListMultiSelection.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListMultiSelection.kt @@ -201,9 +201,11 @@ class ThreadListMultiSelection { val isSelectionEmpty = selectedThreads.isEmpty() threadListFragment.viewLifecycleOwner.lifecycleScope.launch { - val isFromArchive = threadListFragment.folderRoleUtils.getActionFolderRole(selectedThreads) == FolderRole.ARCHIVE + val actionFolderRole = threadListFragment.folderRoleUtils.getActionFolderRole(selectedThreads) + val isArchiveOrDraft = actionFolderRole == FolderRole.ARCHIVE || actionFolderRole == FolderRole.DRAFT + for (index in 0 until getButtonCount()) { - val shouldDisable = isSelectionEmpty || (isFromArchive && index == ARCHIVE_INDEX) + val shouldDisable = isSelectionEmpty || (isArchiveOrDraft && index == ARCHIVE_INDEX) if (shouldDisable) disable(index) else enable(index) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt index 636eb071a7..478ca23d90 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/ThreadViewModel.kt @@ -94,7 +94,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt index 0dd491d81a..bf80ae1dba 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MailActionsBottomSheetDialog.kt @@ -166,16 +166,39 @@ abstract class MailActionsBottomSheetDialog : ActionsBottomSheetDialog() { setTitle(readTextRes) } - fun setFavoriteUi(isFavorite: Boolean) = with(binding.favorite) { - val (favoriteIconRes, favoriteText) = computeFavoriteStyle(isFavorite) - setIconResource(favoriteIconRes) - setTitle(favoriteText) + fun setFavoriteUi(isFavorite: Boolean, isFromDraft: Boolean) = with(binding.favorite) { + isVisible = !isFromDraft + if (!isFromDraft) { + val (favoriteIconRes, favoriteText) = computeFavoriteStyle(isFavorite) + setIconResource(favoriteIconRes) + setTitle(favoriteText) + } + } + + fun setMainActionUi(isFromDraft: Boolean) { + binding.mainActions.isVisible = !isFromDraft + } + + fun setMoveUi(isFromDraft: Boolean) { + binding.move.isVisible = !isFromDraft } - fun setArchiveUi(isFromArchive: Boolean) = with(binding.archive) { + fun setMarkUnreadUi(isFromDraft: Boolean) { + binding.markAsReadUnread.isVisible = !isFromDraft + } + + fun setReportPhishingUi(isFromDraft: Boolean) { + binding.phishing.isVisible = !isFromDraft + } + + fun setArchiveUi(isFromArchive: Boolean, isFromDraft: Boolean) = with(binding.archive) { + isVisible = !isFromDraft if (isFromArchive) { setIconResource(R.drawable.ic_drawer_inbox) setTitle(R.string.actionMoveToInbox) + } else { + setIconResource(R.drawable.ic_archive_folder) + setTitle(R.string.actionArchive) } } 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 3546d9e357..c3384d709d 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 @@ -70,6 +70,8 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { override val shouldCloseMultiSelection: Boolean = false private var isFromSpam: Boolean = false + private var isFromArchive: Boolean = false + private var isFromDraft: Boolean = false @Inject lateinit var descriptionDialog: DescriptionAlertDialog @@ -94,12 +96,18 @@ class MessageActionsBottomSheetDialog : MailActionsBottomSheetDialog() { junkMessagesViewModel.threadsUids = listOf(threadUid) val folderRole = folderRoleUtils.getActionFolderRole(message) isFromSpam = folderRole == FolderRole.SPAM + isFromDraft = folderRole == FolderRole.DRAFT + isFromArchive = folderRole == FolderRole.ARCHIVE setMarkAsReadUi(message.isSeen) - setArchiveUi(isFromArchive = folderRole == FolderRole.ARCHIVE) - setFavoriteUi(message.isFavorite) + setArchiveUi(isFromArchive, isFromDraft) + setFavoriteUi(message.isFavorite, isFromDraft) setReactionUi(message.isValidReactionTarget) - setSpamUi(binding.spam, isFromSpam) + setSpamUi(binding.spam, isFromSpam, isFromDraft) + setMainActionUi(isFromDraft) + setMoveUi(isFromDraft) + setMarkUnreadUi(isFromDraft) + setReportPhishingUi(isFromDraft) observeReportPhishingResult() observePotentialBlockedSenders() diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt index 83674d7f64..68f24fb8f3 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/MultiSelectBottomSheetDialog.kt @@ -266,28 +266,39 @@ class MultiSelectBottomSheetDialog : ActionsBottomSheetDialog() { junkMessagesViewModel.potentialBlockedUsers.observe(viewLifecycleOwner) { potentialUsersToBlock -> val isFromSpam = mainViewModel.currentFolder.value?.role == FolderRole.SPAM setBlockUserUi(binding.blockSender, potentialUsersToBlock, isFromSpam) + hideFirstActionItemDivider() } } private fun setStateDependentUi(shouldRead: Boolean, shouldFavorite: Boolean, isFromArchive: Boolean, threads: Set) { - val (readIcon, readText) = getReadIconAndShortText(shouldRead) - binding.mainActions.setAction(R.id.actionReadUnread, readIcon, readText) + with(binding) { + val isFromDraft = mainViewModel.currentFolder.value?.role == FolderRole.DRAFT + if (isFromDraft) { + mainActions.isVisible = false + phishing.isVisible = false + favorite.isVisible = false + } else { + val (readIcon, readText) = getReadIconAndShortText(shouldRead) + mainActions.setAction(R.id.actionReadUnread, readIcon, readText) - val (archiveIcon, archiveText) = getArchiveIconAndShortText(isFromArchive) - binding.mainActions.setAction(R.id.actionArchive, archiveIcon, archiveText) + val (archiveIcon, archiveText) = getArchiveIconAndShortText(isFromArchive) + mainActions.setAction(R.id.actionArchive, archiveIcon, archiveText) - val (favoriteIcon, favoriteText) = getFavoriteIconAndShortText(shouldFavorite) - binding.favorite.apply { - setIconResource(favoriteIcon) - setTitle(favoriteText) - } + val (favoriteIcon, favoriteText) = getFavoriteIconAndShortText(shouldFavorite) + favorite.apply { + setIconResource(favoriteIcon) + setTitle(favoriteText) + } + } - setSnoozeUi(threads) - ThreadActionsBottomSheetDialog.setSpamUi( - spam = binding.spam, - isFromSpam = mainViewModel.currentFolder.value?.role == FolderRole.SPAM - ) - hideFirstActionItemDivider() + setSnoozeUi(threads) + ThreadActionsBottomSheetDialog.setSpamUi( + spam = spam, + isFromSpam = mainViewModel.currentFolder.value?.role == FolderRole.SPAM, + isFromDraft = isFromDraft, + ) + hideFirstActionItemDivider() + } } private fun setSnoozeUi(threads: Set) = with(binding) { 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 bcb78fac25..6c48a1efad 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 @@ -78,6 +78,7 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { private var folderRole: FolderRole? = null private var isFromArchive: Boolean = false private var isFromSpam: Boolean = false + private var isFromDraft: Boolean = false @Inject lateinit var descriptionDialog: DescriptionAlertDialog @@ -105,13 +106,18 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { folderRole = folderRoleUtils.getActionFolderRole(thread) isFromArchive = folderRole == FolderRole.ARCHIVE isFromSpam = folderRole == FolderRole.SPAM + isFromDraft = folderRole == FolderRole.DRAFT setMarkAsReadUi(thread.isSeen) - setArchiveUi(isFromArchive) - setFavoriteUi(thread.isFavorite) + setArchiveUi(isFromArchive, isFromDraft) + setFavoriteUi(thread.isFavorite, isFromDraft) setSnoozeUi(thread.isSnoozed()) setReactionUi(canBeReactedTo = messageUidToReactTo != null) - setSpamUi(binding.spam, isFromSpam) + setSpamUi(binding.spam, isFromSpam, isFromDraft) + setMainActionUi(isFromDraft) + setMoveUi(isFromDraft) + setMarkUnreadUi(isFromDraft) + setReportPhishingUi(isFromDraft) initOnClickListener(onActionClick(thread, messageUidToExecuteAction, messageUidToReactTo)) } @@ -333,7 +339,12 @@ class ThreadActionsBottomSheetDialog : MailActionsBottomSheetDialog() { const val TAG = "ThreadActionsBottomSheetDialog" const val OPEN_SNOOZE_BOTTOM_SHEET = "openSnoozeBottomSheet" - fun setSpamUi(spam: ActionItemView, isFromSpam: Boolean) { + fun setSpamUi(spam: ActionItemView, isFromSpam: Boolean, isFromDraft: Boolean) { + if (isFromDraft) { + spam.isVisible = false + return + } + spam.apply { val (text, icon) = if (isFromSpam) { R.string.actionNonSpam to R.drawable.ic_non_spam