mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 04:19:54 +03:00
BIT-1545 Allowing the user to search for verification code items (#836)
This commit is contained in:
parent
8d81b160f9
commit
e19ba9df51
12 changed files with 54 additions and 4 deletions
|
@ -23,6 +23,8 @@ private const val SEARCH_TYPE_VAULT_COLLECTION: String = "search_type_vault_coll
|
|||
private const val SEARCH_TYPE_VAULT_NO_FOLDER: String = "search_type_vault_no_folder"
|
||||
private const val SEARCH_TYPE_VAULT_FOLDER: String = "search_type_vault_folder"
|
||||
private const val SEARCH_TYPE_VAULT_TRASH: String = "search_type_vault_trash"
|
||||
private const val SEARCH_TYPE_VAULT_VERIFICATION_CODES: String =
|
||||
"search_type_vault_verification_codes"
|
||||
private const val SEARCH_TYPE_ID: String = "search_type_id"
|
||||
|
||||
private const val SEARCH_ROUTE_PREFIX: String = "search"
|
||||
|
@ -101,6 +103,7 @@ private fun determineSearchType(
|
|||
SEARCH_TYPE_VAULT_NO_FOLDER -> SearchType.Vault.NoFolder
|
||||
SEARCH_TYPE_VAULT_FOLDER -> SearchType.Vault.Folder(requireNotNull(id))
|
||||
SEARCH_TYPE_VAULT_TRASH -> SearchType.Vault.Trash
|
||||
SEARCH_TYPE_VAULT_VERIFICATION_CODES -> SearchType.Vault.VerificationCodes
|
||||
else -> throw IllegalArgumentException("Invalid Search Type")
|
||||
}
|
||||
|
||||
|
@ -118,6 +121,7 @@ private fun SearchType.toTypeString(): String =
|
|||
SearchType.Vault.NoFolder -> SEARCH_TYPE_VAULT_NO_FOLDER
|
||||
SearchType.Vault.SecureNotes -> SEARCH_TYPE_VAULT_SECURE_NOTES
|
||||
SearchType.Vault.Trash -> SEARCH_TYPE_VAULT_TRASH
|
||||
SearchType.Vault.VerificationCodes -> SEARCH_TYPE_VAULT_VERIFICATION_CODES
|
||||
}
|
||||
|
||||
private fun SearchType.toIdOrNull(): String? =
|
||||
|
@ -134,4 +138,5 @@ private fun SearchType.toIdOrNull(): String? =
|
|||
SearchType.Vault.NoFolder -> null
|
||||
SearchType.Vault.SecureNotes -> null
|
||||
SearchType.Vault.Trash -> null
|
||||
SearchType.Vault.VerificationCodes -> null
|
||||
}
|
||||
|
|
|
@ -545,6 +545,7 @@ data class SearchState(
|
|||
val id: String,
|
||||
val title: String,
|
||||
val subtitle: String?,
|
||||
val totpCode: String?,
|
||||
val iconData: IconData,
|
||||
val extraIconList: List<IconRes>,
|
||||
val overflowOptions: List<ListingItemOverflowAction>,
|
||||
|
@ -684,6 +685,16 @@ sealed class SearchTypeData : Parcelable {
|
|||
.concat(" ".asText())
|
||||
.concat(R.string.trash.asText())
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that we should be searching only for verification code items.
|
||||
*/
|
||||
data object VerificationCodes : Vault() {
|
||||
override val title: Text
|
||||
get() = R.string.search.asText()
|
||||
.concat(" ".asText())
|
||||
.concat(R.string.verification_codes.asText())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,5 +81,10 @@ sealed class SearchType : Parcelable {
|
|||
* Indicates that we should be searching only ciphers in the trash.
|
||||
*/
|
||||
data object Trash : Vault()
|
||||
|
||||
/**
|
||||
* Indicates that we should be searching only for verification code items.
|
||||
*/
|
||||
data object VerificationCodes : Vault()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ fun SearchTypeData.updateWithAdditionalDataIfNecessary(
|
|||
SearchTypeData.Vault.NoFolder -> this
|
||||
SearchTypeData.Vault.SecureNotes -> this
|
||||
SearchTypeData.Vault.Trash -> this
|
||||
SearchTypeData.Vault.VerificationCodes -> this
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,6 +98,7 @@ private fun CipherView.filterBySearchType(
|
|||
is SearchTypeData.Vault.Identities -> type == CipherType.IDENTITY
|
||||
is SearchTypeData.Vault.Logins -> type == CipherType.LOGIN
|
||||
is SearchTypeData.Vault.SecureNotes -> type == CipherType.SECURE_NOTE
|
||||
is SearchTypeData.Vault.VerificationCodes -> login?.totp != null
|
||||
is SearchTypeData.Vault.Trash -> deletedDate != null
|
||||
}
|
||||
|
||||
|
@ -171,6 +173,7 @@ private fun CipherView.toDisplayItem(
|
|||
),
|
||||
extraIconList = emptyList(),
|
||||
overflowOptions = toOverflowActions(),
|
||||
totpCode = login?.totp,
|
||||
)
|
||||
|
||||
private fun CipherView.toIconData(
|
||||
|
@ -302,6 +305,7 @@ private fun SendView.toDisplayItem(
|
|||
),
|
||||
extraIconList = toLabelIcons(clock = clock),
|
||||
overflowOptions = toOverflowActions(baseWebSendUrl = baseWebSendUrl),
|
||||
totpCode = null,
|
||||
)
|
||||
|
||||
private enum class SortPriority {
|
||||
|
|
|
@ -20,4 +20,5 @@ fun SearchType.toSearchTypeData(): SearchTypeData =
|
|||
SearchType.Vault.NoFolder -> SearchTypeData.Vault.NoFolder
|
||||
SearchType.Vault.SecureNotes -> SearchTypeData.Vault.SecureNotes
|
||||
SearchType.Vault.Trash -> SearchTypeData.Vault.Trash
|
||||
SearchType.Vault.VerificationCodes -> SearchTypeData.Vault.VerificationCodes
|
||||
}
|
||||
|
|
|
@ -49,6 +49,9 @@ fun NavGraphBuilder.vaultGraph(
|
|||
|
||||
vaultVerificationCodeDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToSearchVault = {
|
||||
onNavigateToSearchVault(SearchType.Vault.VerificationCodes)
|
||||
},
|
||||
onNavigateToVaultItemScreen = onNavigateToVaultItemScreen,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ private const val VERIFICATION_CODE_ROUTE: String = "verification_code"
|
|||
*/
|
||||
fun NavGraphBuilder.vaultVerificationCodeDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToSearchVault: () -> Unit,
|
||||
onNavigateToVaultItemScreen: (String) -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions(
|
||||
|
@ -19,6 +20,7 @@ fun NavGraphBuilder.vaultVerificationCodeDestination(
|
|||
) {
|
||||
VerificationCodeScreen(
|
||||
onNavigateToVaultItemScreen = onNavigateToVaultItemScreen,
|
||||
onNavigateToSearch = onNavigateToSearchVault,
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,14 +16,12 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.showNotYetImplementedToast
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderTextWithSupportLabel
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingContent
|
||||
|
@ -46,10 +44,10 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
fun VerificationCodeScreen(
|
||||
viewModel: VerificationCodeViewModel = hiltViewModel(),
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToSearch: () -> Unit,
|
||||
onNavigateToVaultItemScreen: (String) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsState()
|
||||
val context = LocalContext.current
|
||||
val verificationCodeHandler = remember(viewModel) {
|
||||
VerificationCodeHandlers.create(viewModel)
|
||||
}
|
||||
|
@ -67,7 +65,7 @@ fun VerificationCodeScreen(
|
|||
is VerificationCodeEvent.NavigateBack -> onNavigateBack()
|
||||
is VerificationCodeEvent.NavigateToVaultItem -> onNavigateToVaultItemScreen(event.id)
|
||||
is VerificationCodeEvent.NavigateToVaultSearchScreen -> {
|
||||
showNotYetImplementedToast(context = context)
|
||||
onNavigateToSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,6 +222,11 @@ class SearchScreenTest : BaseComposeTest() {
|
|||
}
|
||||
composeTestRule.onNodeWithText(text = "Search Trash").assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(searchType = SearchTypeData.Vault.VerificationCodes)
|
||||
}
|
||||
composeTestRule.onNodeWithText(text = "Search Verification codes").assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(
|
||||
searchType = SearchTypeData.Vault.Folder(
|
||||
|
|
|
@ -864,6 +864,7 @@ class SearchViewModelTest : BaseViewModelTest() {
|
|||
SearchTypeData.Vault.Logins -> "search_type_vault_logins"
|
||||
SearchTypeData.Vault.NoFolder -> "search_type_vault_no_folder"
|
||||
SearchTypeData.Vault.SecureNotes -> "search_type_vault_secure_notes"
|
||||
SearchTypeData.Vault.VerificationCodes -> "search_type_vault_verification_codes"
|
||||
SearchTypeData.Vault.Trash -> "search_type_vault_trash"
|
||||
null -> "search_type_vault_all"
|
||||
},
|
||||
|
@ -882,6 +883,7 @@ class SearchViewModelTest : BaseViewModelTest() {
|
|||
SearchTypeData.Vault.Logins -> null
|
||||
SearchTypeData.Vault.NoFolder -> null
|
||||
SearchTypeData.Vault.SecureNotes -> null
|
||||
SearchTypeData.Vault.VerificationCodes -> null
|
||||
SearchTypeData.Vault.Trash -> null
|
||||
null -> null
|
||||
},
|
||||
|
|
|
@ -40,6 +40,7 @@ fun createMockDisplayItemForCipher(
|
|||
url = "www.mockuri$number.com",
|
||||
),
|
||||
),
|
||||
totpCode = "mockTotp-$number",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -57,6 +58,7 @@ fun createMockDisplayItemForCipher(
|
|||
notes = "mockNotes-$number",
|
||||
),
|
||||
),
|
||||
totpCode = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -77,6 +79,7 @@ fun createMockDisplayItemForCipher(
|
|||
securityCode = "mockCode-$number",
|
||||
),
|
||||
),
|
||||
totpCode = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -91,6 +94,7 @@ fun createMockDisplayItemForCipher(
|
|||
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
|
||||
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
|
||||
),
|
||||
totpCode = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +135,7 @@ fun createMockDisplayItemForSend(
|
|||
ListingItemOverflowAction.SendAction.RemovePasswordClick(sendId = "mockId-$number"),
|
||||
ListingItemOverflowAction.SendAction.DeleteClick(sendId = "mockId-$number"),
|
||||
),
|
||||
totpCode = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -161,6 +166,7 @@ fun createMockDisplayItemForSend(
|
|||
ListingItemOverflowAction.SendAction.RemovePasswordClick(sendId = "mockId-$number"),
|
||||
ListingItemOverflowAction.SendAction.DeleteClick(sendId = "mockId-$number"),
|
||||
),
|
||||
totpCode = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
|||
class VerificationCodeScreenTest : BaseComposeTest() {
|
||||
|
||||
private var onNavigateBackCalled = false
|
||||
private var onNavigateToSearchCalled = false
|
||||
private var onNavigateToVaultItemId: String? = null
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<VerificationCodeEvent>()
|
||||
|
@ -51,6 +52,7 @@ class VerificationCodeScreenTest : BaseComposeTest() {
|
|||
viewModel = viewModel,
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
onNavigateToVaultItemScreen = { onNavigateToVaultItemId = it },
|
||||
onNavigateToSearch = { onNavigateToSearchCalled = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +63,12 @@ class VerificationCodeScreenTest : BaseComposeTest() {
|
|||
assertTrue(onNavigateBackCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateToVaultSearchScreen event should invoke onNavigateToSearch`() {
|
||||
mutableEventFlow.tryEmit(VerificationCodeEvent.NavigateToVaultSearchScreen)
|
||||
assertTrue(onNavigateToSearchCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateToVaultItem event should call onNavigateToVaultItemScreen`() {
|
||||
val id = "id4321"
|
||||
|
|
Loading…
Add table
Reference in a new issue