Drop swipe as FF resolution in ConversationRouter

ET-5623
This commit is contained in:
Niccolò Forlini
2025-12-19 11:09:56 +01:00
committed by MargeBot
parent 55ca8563bc
commit 20b31a052d
10 changed files with 18 additions and 1439 deletions
@@ -103,25 +103,6 @@ sealed class Destination(val route: String) {
.replace(ViewModeIsConversation.wrap(), viewModeIsConversation.toString())
}
object ConversationNonSwipable : Destination(
"mailbox/conversationlegacy/${ConversationIdKey.wrap()}/" +
"${ScrollToMessageIdKey.wrap()}/${OpenedFromLocationKey.wrap()}/${IsSingleMessageMode.wrap()}" +
"/${ConversationDetailEntryPointNameKey.wrap()}"
) {
operator fun invoke(
conversationId: ConversationId,
scrollToMessageId: MessageId? = null,
openedFromLocation: LabelId,
isSingleMessageMode: Boolean,
entryPoint: ConversationDetailEntryPoint
) = route.replace(ConversationIdKey.wrap(), conversationId.id)
.replace(ScrollToMessageIdKey.wrap(), scrollToMessageId?.id ?: "null")
.replace(OpenedFromLocationKey.wrap(), openedFromLocation.id)
.replace(IsSingleMessageMode.wrap(), isSingleMessageMode.toString())
.replace(ConversationDetailEntryPointNameKey.wrap(), entryPoint.name)
}
data object EntireMessageBody : Destination(
"mailbox/message/${MESSAGE_ID_KEY.wrap()}/body/${INPUT_PARAMS_KEY.wrap()}"
) {
@@ -45,17 +45,14 @@ import ch.protonmail.android.mailcontact.presentation.contactgroupdetails.Contac
import ch.protonmail.android.mailcontact.presentation.contactlist.ui.ContactListScreen
import ch.protonmail.android.mailcontact.presentation.contactsearch.ContactSearchScreen
import ch.protonmail.android.mailconversation.domain.entity.ConversationDetailEntryPoint
import ch.protonmail.android.maildetail.presentation.model.MessageIdUiModel
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetail
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetailScreen.ConversationDetailEntryPointNameKey
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetailScreen.ConversationIdKey
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetailScreen.IsSingleMessageMode
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetailScreen.OpenedFromLocationKey
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetailScreen.ScrollToMessageIdKey
import ch.protonmail.android.maildetail.presentation.ui.EntireMessageBodyScreen
import ch.protonmail.android.maildetail.presentation.ui.PagedConversationDetailScreen.ViewModeIsConversation
import ch.protonmail.android.maildetail.presentation.ui.ConversationDetailScreenLegacy
import ch.protonmail.android.maildetail.presentation.ui.PagedConversationDetailScreen
import ch.protonmail.android.maildetail.presentation.ui.PagedConversationDetailScreen.ViewModeIsConversation
import ch.protonmail.android.maildetail.presentation.viewmodel.ConversationRouterViewModel
import ch.protonmail.android.maillabel.domain.model.LabelId
import ch.protonmail.android.mailmailbox.presentation.mailbox.MailboxScreen
@@ -85,37 +82,21 @@ internal fun NavGraphBuilder.addConversationDetail(
LaunchedEffect(conversationSettings) {
val conversationSettings = conversationSettings ?: return@LaunchedEffect
val singleMessageMode = conversationSettings.singleMessageModeEnabled
val swipeAutoAdvanceEnabled = conversationSettings.swipeAutoAdvanceEnabled
val conversationDestination = if (!swipeAutoAdvanceEnabled) {
Destination.Screen.ConversationNonSwipable(
conversationId = ConversationId(backStackEntry.arguments?.getString(ConversationIdKey)!!),
scrollToMessageId = backStackEntry.arguments?.getString(ScrollToMessageIdKey)
?.takeIf { it != "null" }
?.let(::MessageId),
openedFromLocation = LabelId(backStackEntry.arguments?.getString(OpenedFromLocationKey)!!),
// for the conversation view
isSingleMessageMode = singleMessageMode,
entryPoint = ConversationDetailEntryPoint.valueOf(
backStackEntry.arguments?.getString(ConversationDetailEntryPointNameKey)!!
)
val conversationDestination = Destination.Screen.Conversation(
conversationId = ConversationId(backStackEntry.arguments?.getString(ConversationIdKey)!!),
scrollToMessageId = backStackEntry.arguments?.getString(ScrollToMessageIdKey)
?.takeIf { it != "null" }
?.let(::MessageId),
openedFromLocation = LabelId(backStackEntry.arguments?.getString(OpenedFromLocationKey)!!),
// for the conversation view
isSingleMessageMode = singleMessageMode,
// for the message list grouping
viewModeIsConversation = backStackEntry.arguments?.getString(ViewModeIsConversation)!!.toBoolean(),
entryPoint = ConversationDetailEntryPoint.valueOf(
backStackEntry.arguments?.getString(ConversationDetailEntryPointNameKey)!!
)
} else {
Destination.Screen.Conversation(
conversationId = ConversationId(backStackEntry.arguments?.getString(ConversationIdKey)!!),
scrollToMessageId = backStackEntry.arguments?.getString(ScrollToMessageIdKey)
?.takeIf { it != "null" }
?.let(::MessageId),
openedFromLocation = LabelId(backStackEntry.arguments?.getString(OpenedFromLocationKey)!!),
// for the conversation view
isSingleMessageMode = singleMessageMode,
// for the message list grouping
viewModeIsConversation = backStackEntry.arguments?.getString(ViewModeIsConversation)!!.toBoolean(),
entryPoint = ConversationDetailEntryPoint.valueOf(
backStackEntry.arguments?.getString(ConversationDetailEntryPointNameKey)!!
)
)
}
)
Timber.d("Conversation Router resolved conversation settings to $conversationSettings")
navController.navigate(conversationDestination) {
@@ -135,28 +116,6 @@ internal fun NavGraphBuilder.addConversationDetail(
) {
PagedConversationDetailScreen(actions = actions)
}
composable(
route = Destination.Screen.ConversationNonSwipable.route,
enterTransition = { RouteTransitions.enterTransientLeftToRight },
popEnterTransition = { RouteTransitions.enterTransientLeftToRight },
popExitTransition = { RouteTransitions.exitTransitionRightToLeft },
exitTransition = { RouteTransitions.exitTransitionRightToLeft }
) { backStackEntry ->
ConversationDetailScreenLegacy(
conversationId = ConversationId(backStackEntry.arguments?.getString(ConversationIdKey)!!),
initialScrollToMessageId = backStackEntry.arguments?.getString(ScrollToMessageIdKey)
?.takeIf { it != "null" }
?.let(::MessageIdUiModel),
openedFromLocation = LabelId(backStackEntry.arguments?.getString(OpenedFromLocationKey)!!),
// for the conversation view
singleMessageMode = backStackEntry.arguments?.getString(IsSingleMessageMode)!!.toBoolean(),
conversationEntryPoint = ConversationDetailEntryPoint.valueOf(
backStackEntry.arguments?.getString(ConversationDetailEntryPointNameKey)!!
),
actions = actions
)
}
}
@Suppress("LongMethod", "LongParameterList")
@@ -280,9 +280,7 @@ private fun Pager(
// Disable user scrolling while the page is being updated through ViewModel methods.
userScrollEnabled = canScroll
) { page ->
val item = pages[page]
when (item) {
when (val item = pages[page]) {
is Page.Conversation -> {
val topBarHostState = remember { ConversationTopBarState() }
if (page == pagerState.targetPage) {
@@ -1,30 +0,0 @@
/*
* Copyright (c) 2025 Proton Technologies AG
* This file is part of Proton Technologies AG and Proton Mail.
*
* Proton Mail 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.
*
* Proton Mail 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 Proton Mail. If not, see <https://www.gnu.org/licenses/>.
*/
package ch.protonmail.android.maildetail.presentation.usecase
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsSwipeAutoAdvanceEnabled
import ch.protonmail.android.mailfeatureflags.domain.model.FeatureFlag
import javax.inject.Inject
class GetIsSwipeAutoEnabled @Inject constructor(
@IsSwipeAutoAdvanceEnabled private val isSwipeAutoEnabled: FeatureFlag<Boolean>
) {
suspend operator fun invoke(): Boolean = isSwipeAutoEnabled.get()
}
@@ -20,7 +20,6 @@ package ch.protonmail.android.maildetail.presentation.viewmodel
import androidx.lifecycle.ViewModel
import ch.protonmail.android.maildetail.domain.usecase.IsShowSingleMessageMode
import ch.protonmail.android.maildetail.presentation.usecase.GetIsSwipeAutoEnabled
import ch.protonmail.android.mailsession.domain.usecase.ObservePrimaryUserId
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
@@ -31,20 +30,15 @@ import javax.inject.Inject
@HiltViewModel
class ConversationRouterViewModel @Inject constructor(
observePrimaryUserId: ObservePrimaryUserId,
private val isShowSingleMessageMode: IsShowSingleMessageMode,
private val isSwipeAutoAdvanceEnabled: GetIsSwipeAutoEnabled
private val isShowSingleMessageMode: IsShowSingleMessageMode
) : ViewModel() {
val conversationSettings: Flow<ConversationSettings> = observePrimaryUserId()
.filterNotNull()
.mapLatest { userId ->
ConversationSettings(
singleMessageModeEnabled = isShowSingleMessageMode(userId),
swipeAutoAdvanceEnabled = isSwipeAutoAdvanceEnabled()
)
ConversationSettings(singleMessageModeEnabled = isShowSingleMessageMode(userId))
}
data class ConversationSettings(val singleMessageModeEnabled: Boolean, val swipeAutoAdvanceEnabled: Boolean)
data class ConversationSettings(val singleMessageModeEnabled: Boolean)
}
@@ -1,67 +0,0 @@
/*
* Copyright (c) 2025 Proton Technologies AG
* This file is part of Proton Technologies AG and Proton Mail.
*
* Proton Mail 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.
*
* Proton Mail 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 Proton Mail. If not, see <https://www.gnu.org/licenses/>.
*/
package ch.protonmail.android.maildetail.presentation.usecase
import ch.protonmail.android.mailfeatureflags.domain.model.FeatureFlag
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.Before
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class GetIsSwipeAutoEnabledTest {
private val mockIsSwipeAutoEnabledFlag = mockk<FeatureFlag<Boolean>>()
private lateinit var sut: GetIsSwipeAutoEnabled
@Before
fun setup() {
sut = GetIsSwipeAutoEnabled(mockIsSwipeAutoEnabledFlag)
}
@Test
fun `given enabled then invoke returns true`() = runTest {
// Given
coEvery { mockIsSwipeAutoEnabledFlag.get() } returns true
// When
val result = sut()
assertTrue(result)
coVerify { mockIsSwipeAutoEnabledFlag.get() }
}
@Test
fun `given not enabled then invoke returns false`() = runTest {
// Given
coEvery { mockIsSwipeAutoEnabledFlag.get() } returns false
// When
val result = sut()
// Then
assertFalse(result)
coVerify { mockIsSwipeAutoEnabledFlag.get() }
}
}
@@ -34,7 +34,6 @@ import ch.protonmail.android.mailfeatureflags.domain.annotation.IsOnboardingUpse
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsRestrictMessageWebViewHeightEnabled
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsShowBlockedTrackersEnabled
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsShowRatingBoosterEnabled
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsSwipeAutoAdvanceEnabled
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsUpsellEnabled
import ch.protonmail.android.mailfeatureflags.domain.model.ComposerAutoCollapseQuotedText
import ch.protonmail.android.mailfeatureflags.domain.model.ConversationDetailAutoExpandLastMessageEnabled
@@ -47,7 +46,6 @@ import ch.protonmail.android.mailfeatureflags.domain.model.OnboardingUpsellingEn
import ch.protonmail.android.mailfeatureflags.domain.model.RestrictMessageWebViewHeight
import ch.protonmail.android.mailfeatureflags.domain.model.ShowBlockedTrackers
import ch.protonmail.android.mailfeatureflags.domain.model.ShowRatingBoosterEnabled
import ch.protonmail.android.mailfeatureflags.domain.model.SwipeAutoAdvanceEnabled
import ch.protonmail.android.mailfeatureflags.domain.model.UpsellingEnabled
import dagger.Module
import dagger.Provides
@@ -75,17 +73,6 @@ object FeatureFlagsModule {
fun provideAutoExpandLastMessageConvoEnabled(factory: BooleanFeatureFlagFactory) =
factory.create(ConversationDetailAutoExpandLastMessageEnabled.key, false)
@Provides
@IntoSet
@Singleton
fun provideIsSwipeAutoEnabledDefinitions(): FeatureFlagDefinition = SwipeAutoAdvanceEnabled
@Provides
@Singleton
@IsSwipeAutoAdvanceEnabled
fun provideIsSwipeAutoEnabled(factory: BooleanFeatureFlagFactory) =
factory.create(SwipeAutoAdvanceEnabled.key, false)
@Provides
@IntoSet
@Singleton
@@ -48,10 +48,6 @@ annotation class IsDebugInspectDbEnabled
@Retention(AnnotationRetention.BINARY)
annotation class IsLastMessageAutoExpandEnabled
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IsSwipeAutoAdvanceEnabled
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class IsInjectCssOverrideEnabled
@@ -74,14 +74,6 @@ data object ConversationDetailAutoExpandLastMessageEnabled : FeatureFlagDefiniti
defaultValue = false
)
data object SwipeAutoAdvanceEnabled : FeatureFlagDefinition(
key = "MailAndroidV7SwipeAutoAdvance",
name = "Swipe and AutoAdvance View",
category = FeatureFlagCategory.Details,
description = "Show New Conversation detail Screen (that swipes) or Legacy",
defaultValue = false
)
data object InjectDetailCssOverrideEnabled : FeatureFlagDefinition(
key = "MailAndroidV7CssHeightOverride",
name = "Inject CSS override in HTML messages",