mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-1524, BIT-898: Update generated text (#964)
This commit is contained in:
parent
6294e656ce
commit
dc2e07c130
5 changed files with 213 additions and 57 deletions
|
@ -18,6 +18,7 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
|
import androidx.compose.material3.SliderDefaults
|
||||||
import androidx.compose.material3.SnackbarDuration
|
import androidx.compose.material3.SnackbarDuration
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
@ -33,6 +34,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
@ -470,7 +472,7 @@ private fun PasscodeOptionsItem(
|
||||||
currentSubState: GeneratorState.MainType.Passcode,
|
currentSubState: GeneratorState.MainType.Passcode,
|
||||||
onSubStateOptionClicked: (GeneratorState.MainType.Passcode.PasscodeTypeOption) -> Unit,
|
onSubStateOptionClicked: (GeneratorState.MainType.Passcode.PasscodeTypeOption) -> Unit,
|
||||||
) {
|
) {
|
||||||
val possibleSubStates = GeneratorState.MainType.Passcode.PasscodeTypeOption.values().toList()
|
val possibleSubStates = GeneratorState.MainType.Passcode.PasscodeTypeOption.entries
|
||||||
val optionsWithStrings = possibleSubStates.associateWith { stringResource(id = it.labelRes) }
|
val optionsWithStrings = possibleSubStates.associateWith { stringResource(id = it.labelRes) }
|
||||||
|
|
||||||
BitwardenMultiSelectButton(
|
BitwardenMultiSelectButton(
|
||||||
|
@ -622,6 +624,12 @@ private fun PasswordLengthSliderItem(
|
||||||
},
|
},
|
||||||
valueRange = sliderRange,
|
valueRange = sliderRange,
|
||||||
steps = maxValue - 1,
|
steps = maxValue - 1,
|
||||||
|
colors = SliderDefaults.colors(
|
||||||
|
activeTickColor = Color.Transparent,
|
||||||
|
inactiveTickColor = Color.Transparent,
|
||||||
|
disabledActiveTickColor = Color.Transparent,
|
||||||
|
disabledInactiveTickColor = Color.Transparent,
|
||||||
|
),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.semantics { testTag = "PasswordLengthSlider" }
|
.semantics { testTag = "PasswordLengthSlider" }
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
|
@ -782,8 +790,7 @@ private fun ColumnScope.PassphraseTypeContent(
|
||||||
|
|
||||||
PassphraseWordSeparatorInputItem(
|
PassphraseWordSeparatorInputItem(
|
||||||
wordSeparator = passphraseTypeState.wordSeparator,
|
wordSeparator = passphraseTypeState.wordSeparator,
|
||||||
onPassphraseWordSeparatorChange =
|
onPassphraseWordSeparatorChange = passphraseHandlers.onPassphraseWordSeparatorChange,
|
||||||
passphraseHandlers.onPassphraseWordSeparatorChange,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
@ -835,13 +842,19 @@ private fun PassphraseWordSeparatorInputItem(
|
||||||
) {
|
) {
|
||||||
BitwardenTextField(
|
BitwardenTextField(
|
||||||
label = stringResource(id = R.string.word_separator),
|
label = stringResource(id = R.string.word_separator),
|
||||||
value = wordSeparator?.toString() ?: "",
|
value = wordSeparator?.toString().orEmpty(),
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
onPassphraseWordSeparatorChange(it.toCharArray().firstOrNull())
|
// When onValueChange triggers and we don't update the value for whatever reason,
|
||||||
|
// onValueChange triggers again with the previous value.
|
||||||
|
// To avoid passphrase regeneration, we filter out those re-emissions.
|
||||||
|
val char = it.firstOrNull()
|
||||||
|
if (char != wordSeparator) {
|
||||||
|
onPassphraseWordSeparatorChange(char)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.semantics { testTag = "WordSeparatorEntry" }
|
.semantics { testTag = "WordSeparatorEntry" }
|
||||||
.width(267.dp)
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ import javax.inject.Inject
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
private const val KEY_STATE = "state"
|
private const val KEY_STATE = "state"
|
||||||
|
private const val NO_GENERATED_TEXT: String = "-"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewModel responsible for handling user interactions in the generator screen.
|
* ViewModel responsible for handling user interactions in the generator screen.
|
||||||
|
@ -77,7 +78,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
private val policyManager: PolicyManager,
|
private val policyManager: PolicyManager,
|
||||||
) : BaseViewModel<GeneratorState, GeneratorEvent, GeneratorAction>(
|
) : BaseViewModel<GeneratorState, GeneratorEvent, GeneratorAction>(
|
||||||
initialState = savedStateHandle[KEY_STATE] ?: GeneratorState(
|
initialState = savedStateHandle[KEY_STATE] ?: GeneratorState(
|
||||||
generatedText = "",
|
generatedText = NO_GENERATED_TEXT,
|
||||||
selectedType = when (GeneratorArgs(savedStateHandle).type) {
|
selectedType = when (GeneratorArgs(savedStateHandle).type) {
|
||||||
GeneratorMode.Modal.Username -> Username()
|
GeneratorMode.Modal.Username -> Username()
|
||||||
GeneratorMode.Modal.Password -> Passcode()
|
GeneratorMode.Modal.Password -> Passcode()
|
||||||
|
@ -309,7 +310,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadUsernameOptions(selectedType: Username) {
|
private fun loadUsernameOptions(selectedType: Username, forceRegeneration: Boolean = false) {
|
||||||
val options = generatorRepository.getUsernameGenerationOptions()
|
val options = generatorRepository.getUsernameGenerationOptions()
|
||||||
val updatedSelectedType = when (val type = selectedType.selectedType) {
|
val updatedSelectedType = when (val type = selectedType.selectedType) {
|
||||||
is PlusAddressedEmail -> {
|
is PlusAddressedEmail -> {
|
||||||
|
@ -351,7 +352,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutableStateFlow.update { it.copy(selectedType = updatedSelectedType) }
|
updateGeneratorMainType(forceRegeneration = forceRegeneration) { updatedSelectedType }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun savePasswordOptionsToDisk(password: Password) {
|
private fun savePasswordOptionsToDisk(password: Password) {
|
||||||
|
@ -520,7 +521,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
private suspend fun generatePassphrase(passphrase: Passphrase) {
|
private suspend fun generatePassphrase(passphrase: Passphrase) {
|
||||||
val request = PassphraseGeneratorRequest(
|
val request = PassphraseGeneratorRequest(
|
||||||
numWords = passphrase.numWords.toUByte(),
|
numWords = passphrase.numWords.toUByte(),
|
||||||
wordSeparator = passphrase.wordSeparator.toString(),
|
wordSeparator = passphrase.wordSeparator?.toString() ?: " ",
|
||||||
capitalize = passphrase.capitalize,
|
capitalize = passphrase.capitalize,
|
||||||
includeNumber = passphrase.includeNumber,
|
includeNumber = passphrase.includeNumber,
|
||||||
)
|
)
|
||||||
|
@ -536,7 +537,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
private fun handleRegenerationClick() {
|
private fun handleRegenerationClick() {
|
||||||
// Go through the update process with the current state to trigger a
|
// Go through the update process with the current state to trigger a
|
||||||
// regeneration of the generated text for the same state.
|
// regeneration of the generated text for the same state.
|
||||||
updateGeneratorMainType(isManualRegeneration = true) { mutableStateFlow.value.selectedType }
|
updateGeneratorMainType(forceRegeneration = true) { mutableStateFlow.value.selectedType }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCopyClick() {
|
private fun handleCopyClick() {
|
||||||
|
@ -650,10 +651,12 @@ class GeneratorViewModel @Inject constructor(
|
||||||
private fun handleMainTypeOptionSelect(action: GeneratorAction.MainTypeOptionSelect) {
|
private fun handleMainTypeOptionSelect(action: GeneratorAction.MainTypeOptionSelect) {
|
||||||
when (action.mainTypeOption) {
|
when (action.mainTypeOption) {
|
||||||
GeneratorState.MainTypeOption.PASSWORD -> {
|
GeneratorState.MainTypeOption.PASSWORD -> {
|
||||||
loadPasscodeOptions(Passcode(), usePolicyDefault = true)
|
loadPasscodeOptions(selectedType = Passcode(), usePolicyDefault = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneratorState.MainTypeOption.USERNAME -> loadUsernameOptions(Username())
|
GeneratorState.MainTypeOption.USERNAME -> {
|
||||||
|
loadUsernameOptions(selectedType = Username(), forceRegeneration = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,18 +886,24 @@ class GeneratorViewModel @Inject constructor(
|
||||||
when (action.usernameTypeOption) {
|
when (action.usernameTypeOption) {
|
||||||
Username.UsernameTypeOption.PLUS_ADDRESSED_EMAIL -> loadUsernameOptions(
|
Username.UsernameTypeOption.PLUS_ADDRESSED_EMAIL -> loadUsernameOptions(
|
||||||
selectedType = Username(selectedType = PlusAddressedEmail()),
|
selectedType = Username(selectedType = PlusAddressedEmail()),
|
||||||
|
forceRegeneration = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
Username.UsernameTypeOption.CATCH_ALL_EMAIL -> loadUsernameOptions(
|
Username.UsernameTypeOption.CATCH_ALL_EMAIL -> loadUsernameOptions(
|
||||||
selectedType = Username(selectedType = CatchAllEmail()),
|
selectedType = Username(selectedType = CatchAllEmail()),
|
||||||
|
forceRegeneration = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// We do not force regeneration here since the API can fail if the data is entered
|
||||||
|
// incorrectly. This will only be generated when the user clicks the regenerate button.
|
||||||
Username.UsernameTypeOption.FORWARDED_EMAIL_ALIAS -> loadUsernameOptions(
|
Username.UsernameTypeOption.FORWARDED_EMAIL_ALIAS -> loadUsernameOptions(
|
||||||
selectedType = Username(selectedType = ForwardedEmailAlias()),
|
selectedType = Username(selectedType = ForwardedEmailAlias()),
|
||||||
|
forceRegeneration = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
Username.UsernameTypeOption.RANDOM_WORD -> loadUsernameOptions(
|
Username.UsernameTypeOption.RANDOM_WORD -> loadUsernameOptions(
|
||||||
selectedType = Username(selectedType = RandomWord()),
|
selectedType = Username(selectedType = RandomWord()),
|
||||||
|
forceRegeneration = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1170,7 +1179,7 @@ class GeneratorViewModel @Inject constructor(
|
||||||
//region Utility Functions
|
//region Utility Functions
|
||||||
|
|
||||||
private inline fun updateGeneratorMainType(
|
private inline fun updateGeneratorMainType(
|
||||||
isManualRegeneration: Boolean = false,
|
forceRegeneration: Boolean = false,
|
||||||
crossinline block: (GeneratorState.MainType) -> GeneratorState.MainType?,
|
crossinline block: (GeneratorState.MainType) -> GeneratorState.MainType?,
|
||||||
) {
|
) {
|
||||||
val currentSelectedType = mutableStateFlow.value.selectedType
|
val currentSelectedType = mutableStateFlow.value.selectedType
|
||||||
|
@ -1195,30 +1204,34 @@ class GeneratorViewModel @Inject constructor(
|
||||||
is Username -> when (val selectedType = updatedMainType.selectedType) {
|
is Username -> when (val selectedType = updatedMainType.selectedType) {
|
||||||
is ForwardedEmailAlias -> {
|
is ForwardedEmailAlias -> {
|
||||||
saveForwardedEmailAliasServiceTypeToDisk(selectedType)
|
saveForwardedEmailAliasServiceTypeToDisk(selectedType)
|
||||||
if (isManualRegeneration) {
|
if (forceRegeneration) {
|
||||||
generateForwardedEmailAlias(selectedType)
|
generateForwardedEmailAlias(selectedType)
|
||||||
|
} else {
|
||||||
|
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is CatchAllEmail -> {
|
is CatchAllEmail -> {
|
||||||
saveCatchAllEmailOptionsToDisk(selectedType)
|
saveCatchAllEmailOptionsToDisk(selectedType)
|
||||||
if (isManualRegeneration) {
|
if (forceRegeneration) {
|
||||||
generateCatchAllEmail(selectedType)
|
generateCatchAllEmail(selectedType)
|
||||||
|
} else {
|
||||||
|
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is PlusAddressedEmail -> {
|
is PlusAddressedEmail -> {
|
||||||
savePlusAddressedEmailOptionsToDisk(selectedType)
|
savePlusAddressedEmailOptionsToDisk(selectedType)
|
||||||
if (isManualRegeneration) {
|
if (forceRegeneration) {
|
||||||
generatePlusAddressedEmail(selectedType)
|
generatePlusAddressedEmail(selectedType)
|
||||||
|
} else {
|
||||||
|
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is RandomWord -> {
|
is RandomWord -> {
|
||||||
saveRandomWordOptionsToDisk(selectedType)
|
saveRandomWordOptionsToDisk(selectedType)
|
||||||
if (isManualRegeneration) {
|
generateRandomWordUsername(selectedType)
|
||||||
generateRandomWordUsername(selectedType)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1226,7 +1239,10 @@ class GeneratorViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun generateForwardedEmailAlias(alias: ForwardedEmailAlias) {
|
private suspend fun generateForwardedEmailAlias(alias: ForwardedEmailAlias) {
|
||||||
val request = alias.selectedServiceType?.toUsernameGeneratorRequest() ?: return
|
val request = alias.selectedServiceType?.toUsernameGeneratorRequest() ?: run {
|
||||||
|
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||||
|
return
|
||||||
|
}
|
||||||
val result = generatorRepository.generateForwardedServiceUsername(request)
|
val result = generatorRepository.generateForwardedServiceUsername(request)
|
||||||
sendAction(GeneratorAction.Internal.UpdateGeneratedForwardedServiceUsernameResult(result))
|
sendAction(GeneratorAction.Internal.UpdateGeneratedForwardedServiceUsernameResult(result))
|
||||||
}
|
}
|
||||||
|
@ -1242,10 +1258,14 @@ class GeneratorViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun generateCatchAllEmail(catchAllEmail: CatchAllEmail) {
|
private suspend fun generateCatchAllEmail(catchAllEmail: CatchAllEmail) {
|
||||||
|
val domainName = catchAllEmail.domainName.orNullIfBlank() ?: run {
|
||||||
|
mutableStateFlow.update { it.copy(generatedText = NO_GENERATED_TEXT) }
|
||||||
|
return
|
||||||
|
}
|
||||||
val result = generatorRepository.generateCatchAllEmail(
|
val result = generatorRepository.generateCatchAllEmail(
|
||||||
UsernameGeneratorRequest.Catchall(
|
UsernameGeneratorRequest.Catchall(
|
||||||
type = AppendType.Random,
|
type = AppendType.Random,
|
||||||
domain = catchAllEmail.domainName,
|
domain = domainName,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
sendAction(GeneratorAction.Internal.UpdateGeneratedCatchAllUsernameResult(result))
|
sendAction(GeneratorAction.Internal.UpdateGeneratedCatchAllUsernameResult(result))
|
||||||
|
@ -1498,7 +1518,7 @@ data class GeneratorState(
|
||||||
* Provides a list of available main types for the generator.
|
* Provides a list of available main types for the generator.
|
||||||
*/
|
*/
|
||||||
val typeOptions: List<MainTypeOption>
|
val typeOptions: List<MainTypeOption>
|
||||||
get() = MainTypeOption.values().toList()
|
get() = MainTypeOption.entries.toList()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum representing the main type options for the generator, such as PASSWORD and USERNAME.
|
* Enum representing the main type options for the generator, such as PASSWORD and USERNAME.
|
||||||
|
|
|
@ -2,18 +2,22 @@ package com.x8bit.bitwarden.ui.tools.feature.generator.util
|
||||||
|
|
||||||
import com.bitwarden.generators.ForwarderServiceType
|
import com.bitwarden.generators.ForwarderServiceType
|
||||||
import com.bitwarden.generators.UsernameGeneratorRequest
|
import com.bitwarden.generators.UsernameGeneratorRequest
|
||||||
|
import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a [ServiceType] to a [UsernameGeneratorRequest.Forwarded].
|
* Converts a [ServiceType] to a [UsernameGeneratorRequest.Forwarded].
|
||||||
*/
|
*/
|
||||||
fun ServiceType.toUsernameGeneratorRequest(): UsernameGeneratorRequest.Forwarded {
|
@Suppress("ReturnCount", "LongMethod")
|
||||||
|
fun ServiceType.toUsernameGeneratorRequest(): UsernameGeneratorRequest.Forwarded? {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is ServiceType.AddyIo -> {
|
is ServiceType.AddyIo -> {
|
||||||
|
val accessToken = this.apiAccessToken.orNullIfBlank() ?: return null
|
||||||
|
val domain = this.domainName.orNullIfBlank() ?: return null
|
||||||
UsernameGeneratorRequest.Forwarded(
|
UsernameGeneratorRequest.Forwarded(
|
||||||
service = ForwarderServiceType.AddyIo(
|
service = ForwarderServiceType.AddyIo(
|
||||||
apiToken = this.apiAccessToken,
|
apiToken = accessToken,
|
||||||
domain = this.domainName,
|
domain = domain,
|
||||||
baseUrl = this.baseUrl,
|
baseUrl = this.baseUrl,
|
||||||
),
|
),
|
||||||
website = null,
|
website = null,
|
||||||
|
@ -21,31 +25,51 @@ fun ServiceType.toUsernameGeneratorRequest(): UsernameGeneratorRequest.Forwarded
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServiceType.DuckDuckGo -> {
|
is ServiceType.DuckDuckGo -> {
|
||||||
UsernameGeneratorRequest.Forwarded(
|
this
|
||||||
service = ForwarderServiceType.DuckDuckGo(token = this.apiKey),
|
.apiKey
|
||||||
website = null,
|
.orNullIfBlank()
|
||||||
)
|
?.let {
|
||||||
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.DuckDuckGo(token = it),
|
||||||
|
website = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServiceType.FirefoxRelay -> {
|
is ServiceType.FirefoxRelay -> {
|
||||||
UsernameGeneratorRequest.Forwarded(
|
this
|
||||||
service = ForwarderServiceType.Firefox(apiToken = this.apiAccessToken),
|
.apiAccessToken
|
||||||
website = null,
|
.orNullIfBlank()
|
||||||
)
|
?.let {
|
||||||
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.Firefox(apiToken = it),
|
||||||
|
website = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServiceType.FastMail -> {
|
is ServiceType.FastMail -> {
|
||||||
UsernameGeneratorRequest.Forwarded(
|
this
|
||||||
service = ForwarderServiceType.Fastmail(apiToken = this.apiKey),
|
.apiKey
|
||||||
website = null,
|
.orNullIfBlank()
|
||||||
)
|
?.let {
|
||||||
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.Fastmail(apiToken = it),
|
||||||
|
website = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServiceType.SimpleLogin -> {
|
is ServiceType.SimpleLogin -> {
|
||||||
UsernameGeneratorRequest.Forwarded(
|
this
|
||||||
service = ForwarderServiceType.SimpleLogin(apiKey = this.apiKey),
|
.apiKey
|
||||||
website = null,
|
.orNullIfBlank()
|
||||||
)
|
?.let {
|
||||||
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.SimpleLogin(apiKey = it),
|
||||||
|
website = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,10 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(GeneratorAction.SelectClick)
|
viewModel.actionChannel.trySend(GeneratorAction.SelectClick)
|
||||||
|
|
||||||
assertEquals(GeneratorEvent.NavigateBack, eventTurbine.awaitItem())
|
assertEquals(GeneratorEvent.NavigateBack, eventTurbine.awaitItem())
|
||||||
assertEquals(GeneratorResult.Username("username"), generatorResultTurbine.awaitItem())
|
assertEquals(
|
||||||
|
GeneratorResult.Username(username = "-"),
|
||||||
|
generatorResultTurbine.awaitItem(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,6 +487,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
val expectedState =
|
val expectedState =
|
||||||
initialPasscodeState.copy(
|
initialPasscodeState.copy(
|
||||||
|
generatedText = "email+abcd1234@address.com",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail(
|
GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail(
|
||||||
email = "currentEmail",
|
email = "currentEmail",
|
||||||
|
@ -558,6 +562,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = initialUsernameState.copy(
|
val expectedState = initialUsernameState.copy(
|
||||||
|
generatedText = "email+abcd1234@address.com",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState
|
selectedType = GeneratorState
|
||||||
.MainType
|
.MainType
|
||||||
|
@ -587,6 +592,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = initialUsernameState.copy(
|
val expectedState = initialUsernameState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState.MainType.Username.UsernameType.CatchAllEmail(),
|
selectedType = GeneratorState.MainType.Username.UsernameType.CatchAllEmail(),
|
||||||
),
|
),
|
||||||
|
@ -612,6 +618,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = initialUsernameState.copy(
|
val expectedState = initialUsernameState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState
|
selectedType = GeneratorState
|
||||||
.MainType
|
.MainType
|
||||||
|
@ -639,6 +646,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = initialUsernameState.copy(
|
val expectedState = initialUsernameState.copy(
|
||||||
|
generatedText = "randomWord",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState.MainType.Username.UsernameType.RandomWord(),
|
selectedType = GeneratorState.MainType.Username.UsernameType.RandomWord(),
|
||||||
),
|
),
|
||||||
|
@ -1209,6 +1217,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultForwardedEmailAliasState.copy(
|
val expectedState = defaultForwardedEmailAliasState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState
|
selectedType = GeneratorState
|
||||||
.MainType
|
.MainType
|
||||||
|
@ -1262,6 +1271,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultAddyIoState.copy(
|
val expectedState = defaultAddyIoState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
||||||
selectedServiceType = GeneratorState
|
selectedServiceType = GeneratorState
|
||||||
|
@ -1301,6 +1311,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultAddyIoState.copy(
|
val expectedState = defaultAddyIoState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
||||||
selectedServiceType = GeneratorState
|
selectedServiceType = GeneratorState
|
||||||
|
@ -1351,6 +1362,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultDuckDuckGoState.copy(
|
val expectedState = defaultDuckDuckGoState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
||||||
selectedServiceType = GeneratorState
|
selectedServiceType = GeneratorState
|
||||||
|
@ -1401,6 +1413,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultFastMailState.copy(
|
val expectedState = defaultFastMailState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
||||||
selectedServiceType = GeneratorState
|
selectedServiceType = GeneratorState
|
||||||
|
@ -1452,6 +1465,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultFirefoxRelayState.copy(
|
val expectedState = defaultFirefoxRelayState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
||||||
selectedServiceType = GeneratorState
|
selectedServiceType = GeneratorState
|
||||||
|
@ -1503,6 +1517,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
viewModel.actionChannel.trySend(action)
|
viewModel.actionChannel.trySend(action)
|
||||||
|
|
||||||
val expectedState = defaultSimpleLoginState.copy(
|
val expectedState = defaultSimpleLoginState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias(
|
||||||
selectedServiceType = GeneratorState
|
selectedServiceType = GeneratorState
|
||||||
|
@ -1547,6 +1562,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = defaultPlusAddressedEmailState.copy(
|
val expectedState = defaultPlusAddressedEmailState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState
|
selectedType = GeneratorState
|
||||||
.MainType
|
.MainType
|
||||||
|
@ -1589,6 +1605,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = defaultCatchAllEmailState.copy(
|
val expectedState = defaultCatchAllEmailState.copy(
|
||||||
|
generatedText = "-",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
selectedType = GeneratorState
|
selectedType = GeneratorState
|
||||||
.MainType
|
.MainType
|
||||||
|
@ -1629,6 +1646,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = defaultRandomWordState.copy(
|
val expectedState = defaultRandomWordState.copy(
|
||||||
|
generatedText = "randomWord",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.RandomWord(
|
GeneratorState.MainType.Username.UsernameType.RandomWord(
|
||||||
capitalize = true,
|
capitalize = true,
|
||||||
|
@ -1654,6 +1672,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectedState = defaultRandomWordState.copy(
|
val expectedState = defaultRandomWordState.copy(
|
||||||
|
generatedText = "randomWord",
|
||||||
selectedType = GeneratorState.MainType.Username(
|
selectedType = GeneratorState.MainType.Username(
|
||||||
GeneratorState.MainType.Username.UsernameType.RandomWord(
|
GeneratorState.MainType.Username.UsernameType.RandomWord(
|
||||||
includeNumber = true,
|
includeNumber = true,
|
||||||
|
|
|
@ -1,12 +1,38 @@
|
||||||
package com.x8bit.bitwarden.ui.tools.feature.generator.util
|
package com.x8bit.bitwarden.ui.tools.feature.generator.util
|
||||||
|
|
||||||
import com.bitwarden.generators.ForwarderServiceType
|
import com.bitwarden.generators.ForwarderServiceType
|
||||||
|
import com.bitwarden.generators.UsernameGeneratorRequest
|
||||||
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType
|
import com.x8bit.bitwarden.ui.tools.feature.generator.GeneratorState.MainType.Username.UsernameType.ForwardedEmailAlias.ServiceType
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertNull
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
internal class ServiceTypeExtensionsTest {
|
internal class ServiceTypeExtensionsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toUsernameGeneratorRequest for AddyIo returns null when apiAccessToken is blank`() {
|
||||||
|
val addyIoServiceType = ServiceType.AddyIo(
|
||||||
|
apiAccessToken = "",
|
||||||
|
domainName = "test.com",
|
||||||
|
baseUrl = "http://test.com",
|
||||||
|
)
|
||||||
|
val request = addyIoServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
|
assertNull(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toUsernameGeneratorRequest for AddyIo returns null when domainName is blank`() {
|
||||||
|
val addyIoServiceType = ServiceType.AddyIo(
|
||||||
|
apiAccessToken = "testToken",
|
||||||
|
domainName = "",
|
||||||
|
baseUrl = "http://test.com",
|
||||||
|
)
|
||||||
|
val request = addyIoServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
|
assertNull(request)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `toUsernameGeneratorRequest for AddyIo returns correct request`() {
|
fun `toUsernameGeneratorRequest for AddyIo returns correct request`() {
|
||||||
val addyIoServiceType = ServiceType.AddyIo(
|
val addyIoServiceType = ServiceType.AddyIo(
|
||||||
|
@ -17,14 +43,24 @@ internal class ServiceTypeExtensionsTest {
|
||||||
val request = addyIoServiceType.toUsernameGeneratorRequest()
|
val request = addyIoServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
ForwarderServiceType.AddyIo(
|
UsernameGeneratorRequest.Forwarded(
|
||||||
apiToken = "testToken",
|
service = ForwarderServiceType.AddyIo(
|
||||||
domain = "test.com",
|
apiToken = "testToken",
|
||||||
baseUrl = "http://test.com",
|
domain = "test.com",
|
||||||
|
baseUrl = "http://test.com",
|
||||||
|
),
|
||||||
|
website = null,
|
||||||
),
|
),
|
||||||
request.service,
|
request,
|
||||||
)
|
)
|
||||||
assertEquals(null, request.website)
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toUsernameGeneratorRequest for DuckDuckGo returns null when apiKey is blank`() {
|
||||||
|
val duckDuckGoServiceType = ServiceType.DuckDuckGo(apiKey = "")
|
||||||
|
val request = duckDuckGoServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
|
assertNull(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -32,8 +68,21 @@ internal class ServiceTypeExtensionsTest {
|
||||||
val duckDuckGoServiceType = ServiceType.DuckDuckGo(apiKey = "testKey")
|
val duckDuckGoServiceType = ServiceType.DuckDuckGo(apiKey = "testKey")
|
||||||
val request = duckDuckGoServiceType.toUsernameGeneratorRequest()
|
val request = duckDuckGoServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
assertEquals(ForwarderServiceType.DuckDuckGo("testKey"), request.service)
|
assertEquals(
|
||||||
assertEquals(null, request.website)
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.DuckDuckGo(token = "testKey"),
|
||||||
|
website = null,
|
||||||
|
),
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toUsernameGeneratorRequest for FirefoxRelay returns null when apiAccessToken is blank`() {
|
||||||
|
val firefoxRelayServiceType = ServiceType.FirefoxRelay(apiAccessToken = "")
|
||||||
|
val request = firefoxRelayServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
|
assertNull(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -41,8 +90,21 @@ internal class ServiceTypeExtensionsTest {
|
||||||
val firefoxRelayServiceType = ServiceType.FirefoxRelay(apiAccessToken = "testToken")
|
val firefoxRelayServiceType = ServiceType.FirefoxRelay(apiAccessToken = "testToken")
|
||||||
val request = firefoxRelayServiceType.toUsernameGeneratorRequest()
|
val request = firefoxRelayServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
assertEquals(ForwarderServiceType.Firefox(apiToken = "testToken"), request.service)
|
assertEquals(
|
||||||
assertEquals(null, request.website)
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.Firefox(apiToken = "testToken"),
|
||||||
|
website = null,
|
||||||
|
),
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toUsernameGeneratorRequest for FastMail returns null when apiKey is blank`() {
|
||||||
|
val fastMailServiceType = ServiceType.FastMail(apiKey = "")
|
||||||
|
val request = fastMailServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
|
assertNull(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -50,8 +112,21 @@ internal class ServiceTypeExtensionsTest {
|
||||||
val fastMailServiceType = ServiceType.FastMail(apiKey = "testKey")
|
val fastMailServiceType = ServiceType.FastMail(apiKey = "testKey")
|
||||||
val request = fastMailServiceType.toUsernameGeneratorRequest()
|
val request = fastMailServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
assertEquals(ForwarderServiceType.Fastmail(apiToken = "testKey"), request.service)
|
assertEquals(
|
||||||
assertEquals(null, request.website)
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.Fastmail(apiToken = "testKey"),
|
||||||
|
website = null,
|
||||||
|
),
|
||||||
|
request,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toUsernameGeneratorRequest for SimpleLogin returns null when apiKey is blank`() {
|
||||||
|
val simpleLoginServiceType = ServiceType.SimpleLogin(apiKey = "")
|
||||||
|
val request = simpleLoginServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
|
assertNull(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -59,7 +134,12 @@ internal class ServiceTypeExtensionsTest {
|
||||||
val simpleLoginServiceType = ServiceType.SimpleLogin(apiKey = "testKey")
|
val simpleLoginServiceType = ServiceType.SimpleLogin(apiKey = "testKey")
|
||||||
val request = simpleLoginServiceType.toUsernameGeneratorRequest()
|
val request = simpleLoginServiceType.toUsernameGeneratorRequest()
|
||||||
|
|
||||||
assertEquals(ForwarderServiceType.SimpleLogin(apiKey = "testKey"), request.service)
|
assertEquals(
|
||||||
assertEquals(null, request.website)
|
UsernameGeneratorRequest.Forwarded(
|
||||||
|
service = ForwarderServiceType.SimpleLogin(apiKey = "testKey"),
|
||||||
|
website = null,
|
||||||
|
),
|
||||||
|
request,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue