mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-972 Add the import url (#903)
This commit is contained in:
parent
7b32e46d37
commit
77913805ab
6 changed files with 114 additions and 12 deletions
|
@ -50,6 +50,15 @@ val EnvironmentUrlDataJson.baseWebSendUrl: String
|
||||||
?.let { "$it/#/send/" }
|
?.let { "$it/#/send/" }
|
||||||
?: DEFAULT_WEB_SEND_URL
|
?: DEFAULT_WEB_SEND_URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the base web vault import URL or the default value if one is not present.
|
||||||
|
*/
|
||||||
|
val EnvironmentUrlDataJson.toBaseWebVaultImportUrl: String
|
||||||
|
get() =
|
||||||
|
this
|
||||||
|
.baseWebVaultUrlOrDefault
|
||||||
|
.let { "$it/#/tools/import" }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a base icon url based on the environment or the default value if values are missing.
|
* Returns a base icon url based on the environment or the default value if values are missing.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,13 +17,17 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenExternalLinkRow
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenExternalLinkRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextRow
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||||
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
|
import com.x8bit.bitwarden.ui.platform.theme.LocalIntentManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the vault settings screen.
|
* Displays the vault settings screen.
|
||||||
|
@ -36,7 +40,10 @@ fun VaultSettingsScreen(
|
||||||
onNavigateToExportVault: () -> Unit,
|
onNavigateToExportVault: () -> Unit,
|
||||||
onNavigateToFolders: () -> Unit,
|
onNavigateToFolders: () -> Unit,
|
||||||
viewModel: VaultSettingsViewModel = hiltViewModel(),
|
viewModel: VaultSettingsViewModel = hiltViewModel(),
|
||||||
|
intentManager: IntentManager = LocalIntentManager.current,
|
||||||
) {
|
) {
|
||||||
|
val state = viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
EventsEffect(viewModel = viewModel) { event ->
|
EventsEffect(viewModel = viewModel) { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
|
@ -46,6 +53,10 @@ fun VaultSettingsScreen(
|
||||||
is VaultSettingsEvent.ShowToast -> {
|
is VaultSettingsEvent.ShowToast -> {
|
||||||
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is VaultSettingsEvent.NavigateToImportVault -> {
|
||||||
|
intentManager.launchUri(event.url.toUri())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +107,12 @@ fun VaultSettingsScreen(
|
||||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||||
},
|
},
|
||||||
withDivider = true,
|
withDivider = true,
|
||||||
dialogTitle = stringResource(id = R.string.import_items_confirmation),
|
dialogTitle = stringResource(id = R.string.continue_to_web_app),
|
||||||
dialogMessage = stringResource(id = R.string.import_items_description),
|
dialogMessage =
|
||||||
|
stringResource(
|
||||||
|
id = R.string.you_can_import_data_to_your_vault_on_x,
|
||||||
|
state.value.baseUrl,
|
||||||
|
),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||||
|
|
||||||
|
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
|
||||||
|
import com.x8bit.bitwarden.data.platform.repository.util.baseWebVaultUrlOrDefault
|
||||||
|
import com.x8bit.bitwarden.data.platform.repository.util.toBaseWebVaultImportUrl
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -8,10 +11,23 @@ import javax.inject.Inject
|
||||||
* View model for the vault screen.
|
* View model for the vault screen.
|
||||||
*/
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class VaultSettingsViewModel @Inject constructor() :
|
class VaultSettingsViewModel @Inject constructor(
|
||||||
BaseViewModel<Unit, VaultSettingsEvent, VaultSettingsAction>(
|
val environmentRepository: EnvironmentRepository,
|
||||||
initialState = Unit,
|
) : BaseViewModel<VaultSettingsState, VaultSettingsEvent, VaultSettingsAction>(
|
||||||
) {
|
initialState = run {
|
||||||
|
VaultSettingsState(
|
||||||
|
baseUrl = environmentRepository
|
||||||
|
.environment
|
||||||
|
.environmentUrlData
|
||||||
|
.baseWebVaultUrlOrDefault,
|
||||||
|
importUrl = environmentRepository
|
||||||
|
.environment
|
||||||
|
.environmentUrlData
|
||||||
|
.toBaseWebVaultImportUrl,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
|
||||||
override fun handleAction(action: VaultSettingsAction): Unit = when (action) {
|
override fun handleAction(action: VaultSettingsAction): Unit = when (action) {
|
||||||
VaultSettingsAction.BackClick -> handleBackClicked()
|
VaultSettingsAction.BackClick -> handleBackClicked()
|
||||||
VaultSettingsAction.ExportVaultClick -> handleExportVaultClicked()
|
VaultSettingsAction.ExportVaultClick -> handleExportVaultClicked()
|
||||||
|
@ -32,11 +48,20 @@ class VaultSettingsViewModel @Inject constructor() :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleImportItemsClicked() {
|
private fun handleImportItemsClicked() {
|
||||||
// TODO BIT-972 implement import items functionality
|
sendEvent(
|
||||||
sendEvent(VaultSettingsEvent.ShowToast("Not yet implemented."))
|
VaultSettingsEvent.NavigateToImportVault(state.importUrl),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models the state for the VaultSettingScreen.
|
||||||
|
*/
|
||||||
|
data class VaultSettingsState(
|
||||||
|
val baseUrl: String,
|
||||||
|
val importUrl: String,
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Models events for the vault screen.
|
* Models events for the vault screen.
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +71,11 @@ sealed class VaultSettingsEvent {
|
||||||
*/
|
*/
|
||||||
data object NavigateBack : VaultSettingsEvent()
|
data object NavigateBack : VaultSettingsEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the import vault URL.
|
||||||
|
*/
|
||||||
|
data class NavigateToImportVault(val url: String) : VaultSettingsEvent()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Navigate to the Export Vault screen.
|
* Navigate to the Export Vault screen.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -236,6 +236,29 @@ class EnvironmentUrlsDataJsonExtensionsTest {
|
||||||
.baseIconUrl,
|
.baseIconUrl,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toBaseWebVaultImportUrl should return correct url if webVault is empty`() {
|
||||||
|
val expectedUrl = "base/#/tools/import"
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
expectedUrl,
|
||||||
|
DEFAULT_CUSTOM_ENVIRONMENT_URL_DATA.copy(
|
||||||
|
webVault = null,
|
||||||
|
)
|
||||||
|
.toBaseWebVaultImportUrl,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toBaseWebVaultImportUrl should correctly convert to the import url`() {
|
||||||
|
val expectedUrl = "webVault/#/tools/import"
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
expectedUrl,
|
||||||
|
DEFAULT_CUSTOM_ENVIRONMENT_URL_DATA.toBaseWebVaultImportUrl,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val DEFAULT_CUSTOM_ENVIRONMENT_URL_DATA = EnvironmentUrlDataJson(
|
private val DEFAULT_CUSTOM_ENVIRONMENT_URL_DATA = EnvironmentUrlDataJson(
|
||||||
|
|
|
@ -7,8 +7,10 @@ import androidx.compose.ui.test.isDialog
|
||||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.core.net.toUri
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
|
@ -23,7 +25,15 @@ class VaultSettingsScreenTest : BaseComposeTest() {
|
||||||
private var onNavigateToExportVaultCalled = false
|
private var onNavigateToExportVaultCalled = false
|
||||||
private var onNavigateToFoldersCalled = false
|
private var onNavigateToFoldersCalled = false
|
||||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultSettingsEvent>()
|
private val mutableEventFlow = bufferedMutableSharedFlow<VaultSettingsEvent>()
|
||||||
private val mutableStateFlow = MutableStateFlow(Unit)
|
private val mutableStateFlow = MutableStateFlow(
|
||||||
|
VaultSettingsState(
|
||||||
|
baseUrl = "testUrl", importUrl = "testUrl/#/tools/import",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
private val intentManager: IntentManager = mockk(relaxed = true) {
|
||||||
|
every { launchUri(any()) } returns Unit
|
||||||
|
}
|
||||||
|
|
||||||
val viewModel = mockk<VaultSettingsViewModel>(relaxed = true) {
|
val viewModel = mockk<VaultSettingsViewModel>(relaxed = true) {
|
||||||
every { eventFlow } returns mutableEventFlow
|
every { eventFlow } returns mutableEventFlow
|
||||||
every { stateFlow } returns mutableStateFlow
|
every { stateFlow } returns mutableStateFlow
|
||||||
|
@ -37,6 +47,7 @@ class VaultSettingsScreenTest : BaseComposeTest() {
|
||||||
onNavigateBack = { onNavigateBackCalled = true },
|
onNavigateBack = { onNavigateBackCalled = true },
|
||||||
onNavigateToExportVault = { onNavigateToExportVaultCalled = true },
|
onNavigateToExportVault = { onNavigateToExportVaultCalled = true },
|
||||||
onNavigateToFolders = { onNavigateToFoldersCalled = true },
|
onNavigateToFolders = { onNavigateToFoldersCalled = true },
|
||||||
|
intentManager = intentManager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,4 +112,13 @@ class VaultSettingsScreenTest : BaseComposeTest() {
|
||||||
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToFolders)
|
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToFolders)
|
||||||
assertTrue(onNavigateToFoldersCalled)
|
assertTrue(onNavigateToFoldersCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on NavigateToImportVault should invoke IntentManager`() {
|
||||||
|
val testUrl = "testUrl"
|
||||||
|
mutableEventFlow.tryEmit(VaultSettingsEvent.NavigateToImportVault(testUrl))
|
||||||
|
verify {
|
||||||
|
intentManager.launchUri(testUrl.toUri())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||||
|
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
|
import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository
|
||||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class VaultSettingsViewModelTest : BaseViewModelTest() {
|
class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
|
private val environmentRepository = FakeEnvironmentRepository()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `BackClick should emit NavigateBack`() = runTest {
|
fun `BackClick should emit NavigateBack`() = runTest {
|
||||||
|
@ -30,16 +32,19 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ImportItemsClick should emit ShowToast`() = runTest {
|
fun `ImportItemsClick should emit send NavigateToImportVault with correct url`() = runTest {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
val expected = "https://vault.bitwarden.com/#/tools/import"
|
||||||
viewModel.eventFlow.test {
|
viewModel.eventFlow.test {
|
||||||
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
viewModel.trySendAction(VaultSettingsAction.ImportItemsClick)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
VaultSettingsEvent.ShowToast("Not yet implemented."),
|
VaultSettingsEvent.NavigateToImportVault(expected),
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel()
|
private fun createViewModel(): VaultSettingsViewModel = VaultSettingsViewModel(
|
||||||
|
environmentRepository = environmentRepository,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue