mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
Sends file tab is blocked by premium membership (#527)
This commit is contained in:
parent
1cfd85d9f8
commit
e57dc0393c
3 changed files with 93 additions and 5 deletions
|
@ -4,6 +4,8 @@ import android.os.Parcelable
|
|||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
|
@ -12,6 +14,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.asText
|
|||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.util.toSendView
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -27,6 +30,7 @@ private const val KEY_STATE = "state"
|
|||
@HiltViewModel
|
||||
class AddSendViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
authRepo: AuthRepository,
|
||||
private val vaultRepo: VaultRepository,
|
||||
) : BaseViewModel<AddSendState, AddSendEvent, AddSendAction>(
|
||||
initialState = savedStateHandle[KEY_STATE] ?: AddSendState(
|
||||
|
@ -45,6 +49,7 @@ class AddSendViewModel @Inject constructor(
|
|||
),
|
||||
),
|
||||
dialogState = null,
|
||||
isPremiumUser = authRepo.userStateFlow.value?.activeAccount?.isPremium == true,
|
||||
),
|
||||
) {
|
||||
|
||||
|
@ -52,6 +57,12 @@ class AddSendViewModel @Inject constructor(
|
|||
stateFlow
|
||||
.onEach { savedStateHandle[KEY_STATE] = it }
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
authRepo
|
||||
.userStateFlow
|
||||
.map { AddSendAction.Internal.UserStateReceive(it) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
override fun handleAction(action: AddSendAction): Unit = when (action) {
|
||||
|
@ -74,6 +85,7 @@ class AddSendViewModel @Inject constructor(
|
|||
|
||||
private fun handleInternalAction(action: AddSendAction.Internal): Unit = when (action) {
|
||||
is AddSendAction.Internal.CreateSendResultReceive -> handleCreateSendResultReceive(action)
|
||||
is AddSendAction.Internal.UserStateReceive -> handleUserStateReceive(action)
|
||||
}
|
||||
|
||||
private fun handleCreateSendResultReceive(
|
||||
|
@ -98,6 +110,12 @@ class AddSendViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleUserStateReceive(action: AddSendAction.Internal.UserStateReceive) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isPremiumUser = action.userState?.activeAccount?.isPremium == true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePasswordChange(action: AddSendAction.PasswordChange) {
|
||||
updateCommonContent {
|
||||
it.copy(passwordInput = action.input)
|
||||
|
@ -164,6 +182,17 @@ class AddSendViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleFileTypeClick() {
|
||||
if (!state.isPremiumUser) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.send.asText(),
|
||||
message = R.string.send_file_premium_required.asText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
updateContent {
|
||||
it.copy(selectedType = AddSendState.ViewState.Content.SendType.File)
|
||||
}
|
||||
|
@ -257,6 +286,7 @@ class AddSendViewModel @Inject constructor(
|
|||
data class AddSendState(
|
||||
val dialogState: DialogState?,
|
||||
val viewState: ViewState,
|
||||
val isPremiumUser: Boolean,
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
|
@ -438,6 +468,11 @@ sealed class AddSendAction {
|
|||
* Models actions that the [AddSendViewModel] itself might send.
|
||||
*/
|
||||
sealed class Internal : AddSendAction() {
|
||||
/**
|
||||
* Indicates what the current [userState] is.
|
||||
*/
|
||||
data class UserStateReceive(val userState: UserState?) : Internal()
|
||||
|
||||
/**
|
||||
* Indicates a result for creating a send has been received.
|
||||
*/
|
||||
|
|
|
@ -601,6 +601,7 @@ class AddSendScreenTest : BaseComposeTest() {
|
|||
private val DEFAULT_STATE = AddSendState(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialogState = null,
|
||||
isPremiumUser = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import androidx.lifecycle.SavedStateHandle
|
|||
import app.cash.turbine.test
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
|
@ -15,6 +18,7 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
@ -23,6 +27,10 @@ import org.junit.jupiter.api.Test
|
|||
|
||||
class AddSendViewModelTest : BaseViewModelTest() {
|
||||
|
||||
private val mutableUserStateFlow = MutableStateFlow<UserState?>(DEFAULT_USER_STATE)
|
||||
private val authRepository: AuthRepository = mockk {
|
||||
every { userStateFlow } returns mutableUserStateFlow
|
||||
}
|
||||
private val vaultRepository: VaultRepository = mockk()
|
||||
|
||||
@BeforeEach
|
||||
|
@ -43,7 +51,9 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
|
||||
@Test
|
||||
fun `initial state should read from saved state when present`() {
|
||||
val savedState = mockk<AddSendState>()
|
||||
val savedState = DEFAULT_STATE.copy(
|
||||
dialogState = AddSendState.DialogState.Loading("Loading".asText()),
|
||||
)
|
||||
val viewModel = createViewModel(savedState)
|
||||
assertEquals(savedState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
@ -158,21 +168,45 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `FileTypeClick and TextTypeClick should toggle sendType`() = runTest {
|
||||
fun `FileTypeClick and TextTypeClick should toggle sendType when user is premium`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
val premiumUserState = DEFAULT_STATE.copy(isPremiumUser = true)
|
||||
val expectedViewState = DEFAULT_VIEW_STATE.copy(
|
||||
selectedType = AddSendState.ViewState.Content.SendType.File,
|
||||
)
|
||||
// Make sure we are a premium user
|
||||
mutableUserStateFlow.tryEmit(
|
||||
DEFAULT_USER_STATE.copy(
|
||||
accounts = listOf(DEFAULT_USER_ACCOUNT_STATE.copy(isPremium = true)),
|
||||
),
|
||||
)
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(DEFAULT_STATE, awaitItem())
|
||||
assertEquals(premiumUserState, awaitItem())
|
||||
viewModel.trySendAction(AddSendAction.FileTypeClick)
|
||||
assertEquals(DEFAULT_STATE.copy(viewState = expectedViewState), awaitItem())
|
||||
assertEquals(premiumUserState.copy(viewState = expectedViewState), awaitItem())
|
||||
viewModel.trySendAction(AddSendAction.TextTypeClick)
|
||||
assertEquals(DEFAULT_STATE, awaitItem())
|
||||
assertEquals(premiumUserState, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FileTypeClick should display error dialog when account is not premium`() {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
viewModel.trySendAction(AddSendAction.FileTypeClick)
|
||||
|
||||
assertEquals(
|
||||
DEFAULT_STATE.copy(
|
||||
dialogState = AddSendState.DialogState.Error(
|
||||
title = R.string.send.asText(),
|
||||
message = R.string.send_file_premium_required.asText(),
|
||||
),
|
||||
),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NameChange should update name input`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
|
@ -278,6 +312,7 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
state: AddSendState? = null,
|
||||
): AddSendViewModel = AddSendViewModel(
|
||||
savedStateHandle = SavedStateHandle().apply { set("state", state) },
|
||||
authRepo = authRepository,
|
||||
vaultRepo = vaultRepository,
|
||||
)
|
||||
|
||||
|
@ -307,6 +342,23 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
|||
private val DEFAULT_STATE = AddSendState(
|
||||
viewState = DEFAULT_VIEW_STATE,
|
||||
dialogState = null,
|
||||
isPremiumUser = false,
|
||||
)
|
||||
|
||||
private val DEFAULT_USER_ACCOUNT_STATE = UserState.Account(
|
||||
userId = "user_id_1",
|
||||
name = "Bit",
|
||||
email = "bitwarden@gmail.com",
|
||||
avatarColorHex = "#ff00ff",
|
||||
environment = Environment.Us,
|
||||
isPremium = false,
|
||||
isVaultUnlocked = true,
|
||||
organizations = emptyList(),
|
||||
)
|
||||
|
||||
private val DEFAULT_USER_STATE = UserState(
|
||||
activeUserId = "user_id_1",
|
||||
accounts = listOf(DEFAULT_USER_ACCOUNT_STATE),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue