mirror of
https://github.com/ProtonMail/android-mail.git
synced 2026-06-14 09:54:47 +00:00
Add separate feature spotlight FF
ET-5738
This commit is contained in:
committed by
MargeBot
parent
e2aef7419d
commit
e75df7fe60
+19
-6
@@ -20,25 +20,38 @@ package ch.protonmail.android.feature.spotlight
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsFeatureSpotlightEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.FeatureFlag
|
||||
import ch.protonmail.android.mailspotlight.domain.usecase.ObserveFeatureSpotlightDisplay
|
||||
import ch.protonmail.android.mailspotlight.presentation.model.FeatureSpotlightState
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class HomeFeatureSpotlightViewModel @Inject constructor(
|
||||
observeFeatureSpotlightDisplay: ObserveFeatureSpotlightDisplay
|
||||
observeFeatureSpotlightDisplay: ObserveFeatureSpotlightDisplay,
|
||||
@IsFeatureSpotlightEnabled private val isEnabled: FeatureFlag<Boolean>
|
||||
) : ViewModel() {
|
||||
|
||||
val state: StateFlow<FeatureSpotlightState> = observeFeatureSpotlightDisplay().map { preferenceEither ->
|
||||
preferenceEither.fold(
|
||||
ifLeft = { FeatureSpotlightState.Hide },
|
||||
ifRight = { if (it.show) FeatureSpotlightState.Show else FeatureSpotlightState.Hide }
|
||||
)
|
||||
val state: StateFlow<FeatureSpotlightState> = flow {
|
||||
if (!isEnabled.get()) {
|
||||
emit(FeatureSpotlightState.Hide)
|
||||
} else {
|
||||
emitAll(
|
||||
observeFeatureSpotlightDisplay().map { preferenceEither ->
|
||||
preferenceEither.fold(
|
||||
ifLeft = { FeatureSpotlightState.Hide },
|
||||
ifRight = { if (it.show) FeatureSpotlightState.Show else FeatureSpotlightState.Hide }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Lazily,
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.feature.spotlight
|
||||
|
||||
import app.cash.turbine.test
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import ch.protonmail.android.mailcommon.domain.model.PreferencesError
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.FeatureFlag
|
||||
import ch.protonmail.android.mailspotlight.domain.model.FeatureSpotlightDisplay
|
||||
import ch.protonmail.android.mailspotlight.domain.usecase.ObserveFeatureSpotlightDisplay
|
||||
import ch.protonmail.android.mailspotlight.presentation.model.FeatureSpotlightState
|
||||
import ch.protonmail.android.test.utils.rule.MainDispatcherRule
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class HomeFeatureSpotlightViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private val mockFeatureFlag = mockk<FeatureFlag<Boolean>>()
|
||||
private val mockObserveFeatureSpotlightDisplay = mockk<ObserveFeatureSpotlightDisplay>()
|
||||
|
||||
@Test
|
||||
fun `should emit Hide when feature flag is disabled`() = runTest {
|
||||
// Given
|
||||
coEvery { mockFeatureFlag.get() } returns false
|
||||
|
||||
val viewModel = buildViewModel()
|
||||
|
||||
// When/Then
|
||||
viewModel.state.test {
|
||||
assertEquals(FeatureSpotlightState.Hide, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit Show when feature flag is enabled and preference is show`() = runTest {
|
||||
// Given
|
||||
coEvery { mockFeatureFlag.get() } returns true
|
||||
every { mockObserveFeatureSpotlightDisplay() } returns flowOf(FeatureSpotlightDisplay(show = true).right())
|
||||
|
||||
val viewModel = buildViewModel()
|
||||
|
||||
// When/Then
|
||||
viewModel.state.test {
|
||||
assertEquals(FeatureSpotlightState.Show, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit Hide when feature flag is enabled and preference is hide`() = runTest {
|
||||
// Given
|
||||
coEvery { mockFeatureFlag.get() } returns true
|
||||
every { mockObserveFeatureSpotlightDisplay() } returns flowOf(FeatureSpotlightDisplay(show = false).right())
|
||||
|
||||
val viewModel = buildViewModel()
|
||||
|
||||
// When/Then
|
||||
viewModel.state.test {
|
||||
assertEquals(FeatureSpotlightState.Hide, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit Hide when feature flag is enabled and preference returns error`() = runTest {
|
||||
// Given
|
||||
coEvery { mockFeatureFlag.get() } returns true
|
||||
every { mockObserveFeatureSpotlightDisplay() } returns flowOf(PreferencesError.left())
|
||||
|
||||
val viewModel = buildViewModel()
|
||||
|
||||
// When/Then
|
||||
viewModel.state.test {
|
||||
assertEquals(FeatureSpotlightState.Hide, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildViewModel() = HomeFeatureSpotlightViewModel(
|
||||
observeFeatureSpotlightDisplay = mockObserveFeatureSpotlightDisplay,
|
||||
isEnabled = mockFeatureFlag
|
||||
)
|
||||
}
|
||||
+13
@@ -28,6 +28,7 @@ import ch.protonmail.android.mailfeatureflags.domain.annotation.FeatureFlagsCoro
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsBlackFridayWave1Enabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsBlackFridayWave2Enabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsDebugInspectDbEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsFeatureSpotlightEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsInjectCssOverrideEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsLastMessageAutoExpandEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.annotation.IsOnboardingUpsellEnabled
|
||||
@@ -39,6 +40,7 @@ import ch.protonmail.android.mailfeatureflags.domain.model.ComposerAutoCollapseQ
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.ConversationDetailAutoExpandLastMessageEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.DebugInspectDbEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.FeatureFlagDefinition
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.FeatureSpotlight
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.InjectDetailCssOverrideEnabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.MailBlackFriday2025Enabled
|
||||
import ch.protonmail.android.mailfeatureflags.domain.model.MailBlackFriday2025Wave2Enabled
|
||||
@@ -186,4 +188,15 @@ object FeatureFlagsModule {
|
||||
@IntoSet
|
||||
@Singleton
|
||||
fun providePrivacyBundle2601EnabledDef(): FeatureFlagDefinition = PrivacyBundle2601
|
||||
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@IsFeatureSpotlightEnabled
|
||||
fun provideFeatureSpotlightEnabled(factory: BooleanFeatureFlagFactory) = factory.create(FeatureSpotlight.key, false)
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
@Singleton
|
||||
fun provideFeatureSpotlightEnabledDef(): FeatureFlagDefinition = FeatureSpotlight
|
||||
}
|
||||
|
||||
+4
@@ -63,3 +63,7 @@ annotation class IsShowRatingBoosterEnabled
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class IsPrivacyBundle2601Enabled
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class IsFeatureSpotlightEnabled
|
||||
|
||||
+8
@@ -105,3 +105,11 @@ data object PrivacyBundle2601 : FeatureFlagDefinition(
|
||||
description = "Enable padlocks + blocked trackers in details/composer.",
|
||||
defaultValue = false
|
||||
)
|
||||
|
||||
data object FeatureSpotlight : FeatureFlagDefinition(
|
||||
key = "MailAndroidV7FeatureSpotlight",
|
||||
name = "Enable the feature spotlight",
|
||||
category = FeatureFlagCategory.Details,
|
||||
description = "Show a what's new screen on startup when available.",
|
||||
defaultValue = false
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user