diff options
author | Iván Ávalos <avalos@disroot.org> | 2023-08-31 22:11:06 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2023-11-11 13:20:09 -0600 |
commit | bba3c0c3e2c89f2a9b37a2d8851b8e0741f25b0e (patch) | |
tree | 1bce7e7b12f3af03cda638b7f7c5e2d384b10005 | |
parent | 5a6a280fef4b91c18b73763420b961e602507052 (diff) | |
download | taler-android-bba3c0c3e2c89f2a9b37a2d8851b8e0741f25b0e.tar.gz taler-android-bba3c0c3e2c89f2a9b37a2d8851b8e0741f25b0e.tar.bz2 taler-android-bba3c0c3e2c89f2a9b37a2d8851b8e0741f25b0e.zip |
Refactor FakeReducerViewModel + refactor provider management
Signed-off-by: Iván Ávalos <avalos@disroot.org>
15 files changed, 573 insertions, 465 deletions
diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/backup/BackupFinishedScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/backup/BackupFinishedScreen.kt index fc89caa..df499be 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/backup/BackupFinishedScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/backup/BackupFinishedScreen.kt @@ -53,10 +53,9 @@ import net.taler.anastasis.models.SuccessDetail import net.taler.anastasis.shared.Utils import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI -import net.taler.common.Timestamp @Composable fun BackupFinishedScreen( @@ -168,29 +167,8 @@ fun ProviderCard( @Composable fun BackupFinishedScreenPreview() { BackupFinishedScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.BackupFinished, - authenticationProviders = mapOf( - "https://localhost:8088/" to AuthenticationProviderStatus.Ok( - httpStatus = 200, - methods = listOf(), - annualFee = "EUR:0.99", - truthUploadFee = "EUR:3.99", - liabilityLimit = "EUR:1", - currency = "EUR", - storageLimitInMegabytes = 1, - businessName = "Anastasis 42", - providerSalt = "BXAPCKSH9D3MYJTS9536RHJHCX", - ) - ), - successDetails = mapOf( - "https://localhost:8088/" to SuccessDetail( - policyVersion = 1, - policyExpiration = Timestamp.now(), - ), - ), - ), - ), + viewModel = FakeBackupViewModel( + backupState = BackupStates.BackupFinished, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/backup/EditSecretScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/backup/EditSecretScreen.kt index 9706ceb..799ebf2 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/backup/EditSecretScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/backup/EditSecretScreen.kt @@ -29,17 +29,14 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import net.taler.anastasis.R import net.taler.anastasis.models.BackupStates -import net.taler.anastasis.models.CoreSecret import net.taler.anastasis.models.ReducerArgs import net.taler.anastasis.models.ReducerState import net.taler.anastasis.ui.forms.EditSecretForm import net.taler.anastasis.ui.reusable.pages.WizardPage -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI -import net.taler.common.Amount import net.taler.common.CryptoUtils -import net.taler.common.Timestamp @Composable fun EditSecretScreen( @@ -90,21 +87,8 @@ fun EditSecretScreen( @Composable fun EditSecretScreenPreview() { EditSecretScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.SecretEditing, - secretName = "_TALERWALLET_MyPinePhone", - coreSecret = CoreSecret( - value = "EDJP6WK5EG50", - mime = "text/plain", - ), - expiration = Timestamp.never(), - uploadFees = listOf( - ReducerState.Backup.UploadFee( - fee = Amount("KUDOS", 42L, 0), - ), - ), - ), - ), + viewModel = FakeBackupViewModel( + backupState = BackupStates.SecretEditing, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/backup/ReviewPoliciesScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/backup/ReviewPoliciesScreen.kt index edd6ddb..0055589 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/backup/ReviewPoliciesScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/backup/ReviewPoliciesScreen.kt @@ -55,14 +55,13 @@ import net.taler.anastasis.R import net.taler.anastasis.models.AuthMethod import net.taler.anastasis.models.AuthenticationProviderStatus import net.taler.anastasis.models.BackupStates -import net.taler.anastasis.models.MethodSpec import net.taler.anastasis.models.Policy import net.taler.anastasis.models.ReducerState import net.taler.anastasis.ui.dialogs.EditPolicyDialog import net.taler.anastasis.ui.forms.EditPolicyForm import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @@ -263,75 +262,8 @@ fun PolicyMethodCard( @Composable fun ReviewPoliciesScreenPreview() { ReviewPoliciesScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.PoliciesReviewing, - authenticationMethods = listOf( - AuthMethod( - type = AuthMethod.Type.Question, - mimeType = "text/plain", - challenge = "E1QPPS8A", - instructions = "What is your favorite GNU package?", - ), - AuthMethod( - type = AuthMethod.Type.Email, - instructions = "E-mail to user@*le.com", - challenge = "ENSPAWJ0CNW62VBGDHJJWRVFDM50", - ) - ), - authenticationProviders = mapOf( - "http://localhost:8088/" to AuthenticationProviderStatus.Ok( - httpStatus = 200, - methods = listOf( - MethodSpec(type = AuthMethod.Type.Question, usageFee = "EUR:0.001"), - MethodSpec(type = AuthMethod.Type.Sms, usageFee = "EUR:0.55"), - ), - annualFee = "EUR:0.99", - truthUploadFee = "EUR:3.99", - liabilityLimit = "EUR:1", - currency = "EUR", - storageLimitInMegabytes = 1, - businessName = "Anastasis 4", - providerSalt = "CXAPCKSH9D3MYJTS9536RHJHCW", - ), - "http://localhost:8089/" to AuthenticationProviderStatus.Ok( - httpStatus = 200, - methods = listOf( - MethodSpec(type = AuthMethod.Type.Question, usageFee = "EUR:0.001"), - MethodSpec(type = AuthMethod.Type.Sms, usageFee = "EUR:0.55"), - ), - annualFee = "EUR:0.99", - truthUploadFee = "EUR:3.99", - liabilityLimit = "EUR:1", - currency = "EUR", - storageLimitInMegabytes = 1, - businessName = "Anastasis 2", - providerSalt = "CXAPCKSH9D3MYJTS9536RHJHCW", - ), - ), - policies = listOf( - Policy( - methods = listOf( - Policy.PolicyMethod( - authenticationMethod = 0, - provider = "http://localhost:8089/", - ), - Policy.PolicyMethod( - authenticationMethod = 1, - provider = "http://localhost:8088/", - ), - ), - ), - Policy( - methods = listOf( - Policy.PolicyMethod( - authenticationMethod = 0, - provider = "http://localhost:8089/", - ), - ), - ), - ), - ), - ), + viewModel = FakeBackupViewModel( + backupState = BackupStates.PoliciesReviewing, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/backup/SelectAuthMethodsScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/backup/SelectAuthMethodsScreen.kt index 0b42534..584d853 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/backup/SelectAuthMethodsScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/backup/SelectAuthMethodsScreen.kt @@ -20,29 +20,20 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.EditOff -import androidx.compose.material.icons.filled.Error -import androidx.compose.material.icons.filled.QuestionMark -import androidx.compose.material.icons.filled.SyncDisabled import androidx.compose.material3.Divider import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -62,14 +53,13 @@ import net.taler.anastasis.R import net.taler.anastasis.models.AuthMethod import net.taler.anastasis.models.AuthenticationProviderStatus import net.taler.anastasis.models.BackupStates -import net.taler.anastasis.models.MethodSpec import net.taler.anastasis.models.ReducerState +import net.taler.anastasis.ui.common.ManageProvidersScreen import net.taler.anastasis.ui.dialogs.EditMethodDialog -import net.taler.anastasis.ui.dialogs.EditProviderDialog import net.taler.anastasis.ui.reusable.components.ActionCard import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI import net.taler.common.CryptoUtils @@ -131,7 +121,7 @@ fun SelectAuthMethodsScreen( isLoading = isLoading, ) { scroll -> if (manageProviders) { - ManageBackupProviders( + ManageProvidersScreen( nestedScrollConnection = scroll, authProviders = authProviders, onAddProvider = { @@ -284,173 +274,13 @@ private fun ChallengeCard( } } -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun ManageBackupProviders( - nestedScrollConnection: NestedScrollConnection, - authProviders: Map<String, AuthenticationProviderStatus>, - onAddProvider: (url: String) -> Unit, - onDeleteProvider: (url: String) -> Unit, -) { - var showEditDialog by remember { mutableStateOf(false) } - - if (showEditDialog) { - EditProviderDialog( - onProviderEdited = onAddProvider, - onCancel = { showEditDialog = false } - ) - } - - Scaffold( - floatingActionButton = { - FloatingActionButton(onClick = { - showEditDialog = true - }) { - Icon( - Icons.Default.Add, - contentDescription = stringResource(R.string.add), - ) - } - }, - ) { padding -> - LazyColumn( - modifier = Modifier - .padding(padding) - .nestedScroll(nestedScrollConnection) - .fillMaxSize(), - verticalArrangement = Arrangement.Top, - ) { - items(items = authProviders.keys.toList()) { url -> - val status = authProviders[url]!! - ProviderCard( - modifier = Modifier - .padding( - end = LocalSpacing.current.medium, - bottom = LocalSpacing.current.small, - start = LocalSpacing.current.medium, - ) - .fillMaxWidth(), - url = url, - status = status, - onDelete = { onDeleteProvider(url) }, - ) - } - } - } -} - -@Composable -fun ProviderCard( - modifier: Modifier = Modifier, - url: String, - status: AuthenticationProviderStatus, - onDelete: () -> Unit, -) { - ElevatedCard( - modifier = modifier, - ) { - Row( - modifier = Modifier - .padding(LocalSpacing.current.medium) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - when (status) { - is AuthenticationProviderStatus.Ok -> Icon( - Icons.Default.CheckCircle, - contentDescription = stringResource(R.string.provider_status_ok), - tint = MaterialTheme.colorScheme.primary, - ) - is AuthenticationProviderStatus.Disabled -> Icon( - Icons.Default.SyncDisabled, - contentDescription = stringResource(R.string.provider_status_disabled), - tint = MaterialTheme.colorScheme.primary, - ) - is AuthenticationProviderStatus.Error -> Icon( - Icons.Default.Error, - contentDescription = stringResource(R.string.provider_status_error), - tint = MaterialTheme.colorScheme.error, - ) - is AuthenticationProviderStatus.NotContacted -> Icon( - Icons.Default.QuestionMark, - contentDescription = stringResource(R.string.provider_status_not_contacted), - tint = MaterialTheme.colorScheme.primary, - ) - } - Spacer(Modifier.width(12.dp)) - Column( - modifier = Modifier.weight(1f), - ) { - Text(url, style = MaterialTheme.typography.labelLarge) - if (status is AuthenticationProviderStatus.Ok) { - Text(status.businessName, style = MaterialTheme.typography.labelMedium) - } - } - Spacer(Modifier.width(12.dp)) - IconButton(onClick = onDelete) { - Icon( - Icons.Default.Delete, - contentDescription = stringResource(R.string.delete), - ) - } - } - } -} - -private val fakeViewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.AuthenticationsEditing, - authenticationMethods = listOf( - AuthMethod( - type = AuthMethod.Type.Question, - mimeType = "text/plain", - challenge = "E1QPPS8A", - instructions = "What is your favorite GNU package?", - ), - AuthMethod( - type = AuthMethod.Type.Email, - instructions = "E-mail to user@*le.com", - challenge = "ENSPAWJ0CNW62VBGDHJJWRVFDM50", - ) - ), - authenticationProviders = mapOf( - "http://localhost:8088/" to AuthenticationProviderStatus.Ok( - httpStatus = 200, - methods = listOf( - MethodSpec(type = AuthMethod.Type.Question, usageFee = "EUR:0.001"), - MethodSpec(type = AuthMethod.Type.Sms, usageFee = "EUR:0.55"), - ), - annualFee = "EUR:0.99", - truthUploadFee = "EUR:3.99", - liabilityLimit = "EUR:1", - currency = "EUR", - storageLimitInMegabytes = 1, - businessName = "Anastasis 4", - providerSalt = "CXAPCKSH9D3MYJTS9536RHJHCW", - ), - "http://localhost:8089/" to AuthenticationProviderStatus.Ok( - httpStatus = 200, - methods = listOf( - MethodSpec(type = AuthMethod.Type.Question, usageFee = "EUR:0.001"), - MethodSpec(type = AuthMethod.Type.Sms, usageFee = "EUR:0.55"), - ), - annualFee = "EUR:0.99", - truthUploadFee = "EUR:3.99", - liabilityLimit = "EUR:1", - currency = "EUR", - storageLimitInMegabytes = 1, - businessName = "Anastasis 2", - providerSalt = "CXAPCKSH9D3MYJTS9536RHJHCW", - ), - ), - ), -) - @Preview @Composable fun SelectAuthMethodsScreenPreview() { SelectAuthMethodsScreen( - viewModel = fakeViewModel, + viewModel = FakeBackupViewModel( + backupState = BackupStates.AuthenticationsEditing, + ), ) } @@ -458,7 +288,9 @@ fun SelectAuthMethodsScreenPreview() { @Composable fun ManageBackupProvidersPreview() { SelectAuthMethodsScreen( - viewModel = fakeViewModel, + viewModel = FakeBackupViewModel( + backupState = BackupStates.AuthenticationsEditing, + ), showManageProviders = true, ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/common/ManageProvidersScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/common/ManageProvidersScreen.kt new file mode 100644 index 0000000..e0d4b7e --- /dev/null +++ b/anastasis/src/main/java/net/taler/anastasis/ui/common/ManageProvidersScreen.kt @@ -0,0 +1,171 @@ +/* + * This file is part of GNU Taler + * (C) 2023 Taler Systems S.A. + * + * GNU Taler 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, or (at your option) any later version. + * + * GNU Taler 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 + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.anastasis.ui.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Error +import androidx.compose.material.icons.filled.QuestionMark +import androidx.compose.material.icons.filled.SyncDisabled +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import net.taler.anastasis.R +import net.taler.anastasis.models.AuthenticationProviderStatus +import net.taler.anastasis.ui.dialogs.EditProviderDialog +import net.taler.anastasis.ui.theme.LocalSpacing + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ManageProvidersScreen( + nestedScrollConnection: NestedScrollConnection, + authProviders: Map<String, AuthenticationProviderStatus>, + onAddProvider: (url: String) -> Unit, + onDeleteProvider: (url: String) -> Unit, +) { + var showEditDialog by remember { mutableStateOf(false) } + + if (showEditDialog) { + EditProviderDialog( + onProviderEdited = onAddProvider, + onCancel = { showEditDialog = false } + ) + } + + Scaffold( + floatingActionButton = { + FloatingActionButton(onClick = { + showEditDialog = true + }) { + Icon( + Icons.Default.Add, + contentDescription = stringResource(R.string.add), + ) + } + }, + ) { padding -> + LazyColumn( + modifier = Modifier + .padding(padding) + .nestedScroll(nestedScrollConnection) + .fillMaxSize(), + verticalArrangement = Arrangement.Top, + ) { + items(items = authProviders.keys.toList()) { url -> + val status = authProviders[url]!! + ProviderCard( + modifier = Modifier + .padding( + end = LocalSpacing.current.medium, + bottom = LocalSpacing.current.small, + start = LocalSpacing.current.medium, + ) + .fillMaxWidth(), + url = url, + status = status, + onDelete = { onDeleteProvider(url) }, + ) + } + } + } +} + +@Composable +fun ProviderCard( + modifier: Modifier = Modifier, + url: String, + status: AuthenticationProviderStatus, + onDelete: () -> Unit, +) { + ElevatedCard( + modifier = modifier, + ) { + Row( + modifier = Modifier + .padding(LocalSpacing.current.medium) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + when (status) { + is AuthenticationProviderStatus.Ok -> Icon( + Icons.Default.CheckCircle, + contentDescription = stringResource(R.string.provider_status_ok), + tint = MaterialTheme.colorScheme.primary, + ) + is AuthenticationProviderStatus.Disabled -> Icon( + Icons.Default.SyncDisabled, + contentDescription = stringResource(R.string.provider_status_disabled), + tint = MaterialTheme.colorScheme.primary, + ) + is AuthenticationProviderStatus.Error -> Icon( + Icons.Default.Error, + contentDescription = stringResource(R.string.provider_status_error), + tint = MaterialTheme.colorScheme.error, + ) + is AuthenticationProviderStatus.NotContacted -> Icon( + Icons.Default.QuestionMark, + contentDescription = stringResource(R.string.provider_status_not_contacted), + tint = MaterialTheme.colorScheme.primary, + ) + } + Spacer(Modifier.width(12.dp)) + Column( + modifier = Modifier.weight(1f), + ) { + Text(url, style = MaterialTheme.typography.labelLarge) + if (status is AuthenticationProviderStatus.Ok) { + Text(status.businessName, style = MaterialTheme.typography.labelMedium) + } + } + Spacer(Modifier.width(12.dp)) + IconButton(onClick = onDelete) { + Icon( + Icons.Default.Delete, + contentDescription = stringResource(R.string.delete), + ) + } + } + } +}
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectContinentScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectContinentScreen.kt index 10a8978..c803ffe 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectContinentScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectContinentScreen.kt @@ -32,12 +32,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import net.taler.anastasis.R import net.taler.anastasis.models.BackupStates -import net.taler.anastasis.models.ContinentInfo import net.taler.anastasis.models.ReducerState import net.taler.anastasis.ui.reusable.components.Picker import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @@ -99,15 +98,8 @@ fun SelectContinentScreen( @Composable fun SelectContinentScreenPreview() { SelectContinentScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.ContinentSelecting, - selectedContinent = "Europe", - continents = listOf( - ContinentInfo(name = "Europe"), - ContinentInfo(name = "North America"), - ) - ) + viewModel = FakeBackupViewModel( + backupState = BackupStates.ContinentSelecting, ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectCountryScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectCountryScreen.kt index f137e5e..ba8136e 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectCountryScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectCountryScreen.kt @@ -32,12 +32,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import net.taler.anastasis.R import net.taler.anastasis.models.BackupStates -import net.taler.anastasis.models.CountryInfo import net.taler.anastasis.models.ReducerState import net.taler.anastasis.ui.reusable.components.Picker import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @@ -99,23 +98,8 @@ fun SelectCountryScreen( @Composable fun SelectCountryScreenPreview() { SelectCountryScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.ContinentSelecting, - selectedCountry = "ch", - countries = listOf( - CountryInfo( - code = "ch", - name = "Switzerland", - continent = "Europe", - ), - CountryInfo( - code = "de", - name = "Germany", - continent = "Europe", - ) - ) - ) + viewModel = FakeBackupViewModel( + backupState = BackupStates.CountrySelecting, ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectUserAttributesScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectUserAttributesScreen.kt index 6c09ae5..fe90eff 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectUserAttributesScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/common/SelectUserAttributesScreen.kt @@ -51,7 +51,7 @@ import net.taler.anastasis.shared.Utils import net.taler.anastasis.ui.reusable.components.DatePickerField import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeBackupViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @@ -166,30 +166,8 @@ private fun fieldStatus( @Composable fun SelectUserAttributesScreenPreview() { SelectUserAttributesScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Backup( - backupState = BackupStates.UserAttributesCollecting, - identityAttributes = mapOf( - "full_name" to "Max Musterman", - "birthdate" to "2000-01-01", - ), - requiredAttributes = listOf( - UserAttributeSpec( - type = "string", - name = "full_name", - label = "Full name", - widget = "anastasis_gtk_ia_full_name", - uuid = "9e8f463f-575f-42cb-85f3-759559997331", - ), - UserAttributeSpec( - type = "date", - name = "birthdate", - label = "Birthdate", - widget = "anastasis_gtk_ia_birthdate", - uuid = "83d655c7-bdb6-484d-904e-80c1058c8854", - ), - ), - ), - ), + viewModel = FakeBackupViewModel( + backupState = BackupStates.UserAttributesCollecting, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/RecoveryFinishedScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/RecoveryFinishedScreen.kt index 9cc58e5..aa43200 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/RecoveryFinishedScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/RecoveryFinishedScreen.kt @@ -41,13 +41,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import net.taler.anastasis.R -import net.taler.anastasis.models.CoreSecret -import net.taler.anastasis.models.RecoveryInternalData import net.taler.anastasis.models.RecoveryStates import net.taler.anastasis.models.ReducerState import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeRecoveryViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI import net.taler.common.CryptoUtils @@ -124,19 +122,8 @@ fun RecoveryFinishedScreen( @Composable fun RecoveryFinishedScreenPreview() { RecoveryFinishedScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Recovery( - recoveryState = RecoveryStates.RecoveryFinished, - recoveryDocument = RecoveryInternalData( - secretName = "Secret", - providerUrl = "http://localhost:8089", - version = 1, - ), - coreSecret = CoreSecret( - mime = "text/plain", - value = CryptoUtils.encodeCrock("Taler".toByteArray(Charsets.UTF_8)), - ) - ), - ), + viewModel = FakeRecoveryViewModel( + recoveryState = RecoveryStates.RecoveryFinished, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectChallengeScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectChallengeScreen.kt index df7afdf..742ad0b 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectChallengeScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectChallengeScreen.kt @@ -45,7 +45,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import net.taler.anastasis.R -import net.taler.anastasis.models.AuthMethod import net.taler.anastasis.models.ChallengeFeedback import net.taler.anastasis.models.ChallengeInfo import net.taler.anastasis.models.RecoveryInformation @@ -53,7 +52,7 @@ import net.taler.anastasis.models.RecoveryStates import net.taler.anastasis.models.ReducerState import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeRecoveryViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @@ -225,34 +224,8 @@ fun FeedbackSolvedButton() { @Composable fun SelectChallengeScreenPreview() { SelectChallengeScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Recovery( - recoveryState = RecoveryStates.ChallengeSelecting, - recoveryInformation = RecoveryInformation( - challenges = listOf( - ChallengeInfo( - instructions = "What is your favorite GNU package?", - type = AuthMethod.Type.Question, - uuid = "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0", - ), - ChallengeInfo( - instructions = "E-mail to user@*le.com", - type = AuthMethod.Type.Email, - uuid = "ZA6T35B8XAR0DNKS5H100GK8PDPTA7Q8ST2FPQSYAZ4QRAA9XKK0", - ), - ), - policies = listOf( - listOf( - RecoveryInformation.Policy(uuid = "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0"), - RecoveryInformation.Policy(uuid = "ZA6T35B8XAR0DNKS5H100GK8PDPTA7Q8ST2FPQSYAZ4QRAA9XKK0") - ), - ), - ), - challengeFeedback = mapOf( - "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0" to ChallengeFeedback.IncorrectAnswer, - "ZA6T35B8XAR0DNKS5H100GK8PDPTA7Q8ST2FPQSYAZ4QRAA9XKK0" to ChallengeFeedback.Solved, - ), - ), - ), + viewModel = FakeRecoveryViewModel( + recoveryState = RecoveryStates.ChallengeSelecting, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectSecretScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectSecretScreen.kt index 71e1d5e..872886a 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectSecretScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SelectSecretScreen.kt @@ -24,8 +24,13 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.EditOff import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Text @@ -37,6 +42,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -45,16 +52,17 @@ import net.taler.anastasis.R import net.taler.anastasis.models.AggregatedPolicyMetaInfo import net.taler.anastasis.models.RecoveryStates import net.taler.anastasis.models.ReducerState -import net.taler.anastasis.models.SelectedVersionInfo +import net.taler.anastasis.ui.common.ManageProvidersScreen import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing -import net.taler.anastasis.viewmodels.FakeReducerViewModel +import net.taler.anastasis.viewmodels.FakeRecoveryViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @Composable fun SelectSecretScreen( viewModel: ReducerViewModelI = hiltViewModel<ReducerViewModel>(), + showManageProviders: Boolean = false, ) { val state by viewModel.reducerState.collectAsState() val reducerState = state as? ReducerState.Recovery @@ -63,12 +71,17 @@ fun SelectSecretScreen( val tasks by viewModel.tasks.collectAsState() val isLoading = tasks.isBackgroundLoading + val authProviders = reducerState.authenticationProviders ?: emptyMap() val versions = reducerState.discoveryState?.aggregatedPolicies ?: emptyList() var selectedIndex by remember { mutableStateOf<Int?>(null) } + var manageProviders by remember { mutableStateOf(showManageProviders) } + WizardPage( - title = stringResource(R.string.select_secret_title), + title = if (manageProviders) + stringResource(R.string.recovery_providers) + else stringResource(R.string.select_auth_methods_title), enableNext = selectedIndex != null, onBackClicked = { viewModel.goHome() }, onPrevClicked = { viewModel.goBack() }, @@ -77,22 +90,70 @@ fun SelectSecretScreen( viewModel.reducerManager?.selectVersion(versions[it]) } }, + actions = { + IconButton(onClick = { + manageProviders = !manageProviders + }) { + if (manageProviders) { + Icon( + Icons.Default.EditOff, + contentDescription = stringResource(R.string.select_auth_methods_title) + ) + } else { + Icon( + Icons.Default.Edit, + contentDescription = stringResource(R.string.manage_backup_providers), + ) + } + + } + }, isLoading = isLoading, + ) { scroll -> + if (manageProviders) { + ManageProvidersScreen( + nestedScrollConnection = scroll, + authProviders = authProviders, + onAddProvider = { + viewModel.reducerManager?.addProvider(it) + }, + onDeleteProvider = { + viewModel.reducerManager?.deleteProvider(it) + }, + ) + } else { + SecretList( + scrollConnection = scroll, + versions = versions, + selectedIndex = selectedIndex, + onSelectIndex = { selectedIndex = it } + ) + } + } +} + +@Composable +fun SecretList( + scrollConnection: NestedScrollConnection, + versions: List<AggregatedPolicyMetaInfo>, + selectedIndex: Int?, + onSelectIndex: (Int) -> Unit, +) { + LazyColumn( + modifier = Modifier.nestedScroll(scrollConnection), ) { - LazyColumn { - items(count = versions.size) { index -> - SecretCard( - modifier = Modifier - .padding( - start = LocalSpacing.current.medium, - end = LocalSpacing.current.medium, - bottom = LocalSpacing.current.small, - ) - .fillMaxWidth(), - policy = versions[index], - isSelected = selectedIndex == index, - ) { selectedIndex = index } - } + items(count = versions.size) { index -> + SecretCard( + modifier = Modifier + .padding( + start = LocalSpacing.current.medium, + end = LocalSpacing.current.medium, + bottom = LocalSpacing.current.small, + ) + .fillMaxWidth(), + policy = versions[index], + isSelected = selectedIndex == index, + ) { onSelectIndex(index) } } } } @@ -146,30 +207,8 @@ fun SecretCard( @Composable fun SelectSecretScreenPreview() { SelectSecretScreen( - viewModel = FakeReducerViewModel( - state = ReducerState.Recovery( - recoveryState = RecoveryStates.SecretSelecting, - discoveryState = ReducerState.Recovery.DiscoveryState( - state = "finished", - aggregatedPolicies = listOf( - AggregatedPolicyMetaInfo( - attributeMask = 0, - policyHash = "000000000000000000000000000000000000000000000000000B28GR6691Y51HR2SAFJZFF0DCMRDZD1YQMS03A55P9NCWHQGEKW8", - providers = listOf( - SelectedVersionInfo.Provider( - url = "https://v1.anastasis.taler.net/", - version = 1, - ), - SelectedVersionInfo.Provider( - url = "https://v1.anastasis.codeblau.de/", - version = 1, - ), - ), - secretName = "Secret", - ), - ), - ), - ), - ), + viewModel = FakeRecoveryViewModel( + recoveryState = RecoveryStates.SecretSelecting, + ) ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SolveChallengeScreen.kt b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SolveChallengeScreen.kt index d644385..cc0b4c6 100644 --- a/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SolveChallengeScreen.kt +++ b/anastasis/src/main/java/net/taler/anastasis/ui/recovery/SolveChallengeScreen.kt @@ -37,11 +37,13 @@ import androidx.hilt.navigation.compose.hiltViewModel import net.taler.anastasis.R import net.taler.anastasis.models.AuthMethod import net.taler.anastasis.models.ChallengeFeedback +import net.taler.anastasis.models.RecoveryStates import net.taler.anastasis.models.ReducerState import net.taler.anastasis.shared.Utils import net.taler.anastasis.ui.forms.EditAnswerForm import net.taler.anastasis.ui.reusable.pages.WizardPage import net.taler.anastasis.ui.theme.LocalSpacing +import net.taler.anastasis.viewmodels.FakeRecoveryViewModel import net.taler.anastasis.viewmodels.ReducerViewModel import net.taler.anastasis.viewmodels.ReducerViewModelI @@ -145,5 +147,9 @@ fun SolveChallengeScreen( @Preview @Composable fun SolveChallengeScreenPreview() { - SolveChallengeScreen() + SolveChallengeScreen( + viewModel = FakeRecoveryViewModel( + recoveryState = RecoveryStates.ChallengeSolving, + ) + ) }
\ No newline at end of file diff --git a/anastasis/src/main/java/net/taler/anastasis/viewmodels/FakeReducerViewModel.kt b/anastasis/src/main/java/net/taler/anastasis/viewmodels/FakeReducerViewModel.kt new file mode 100644 index 0000000..3f482e4 --- /dev/null +++ b/anastasis/src/main/java/net/taler/anastasis/viewmodels/FakeReducerViewModel.kt @@ -0,0 +1,274 @@ +/* + * This file is part of GNU Taler + * (C) 2023 Taler Systems S.A. + * + * GNU Taler 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, or (at your option) any later version. + * + * GNU Taler 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 + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.anastasis.viewmodels + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import net.taler.anastasis.backend.TalerErrorInfo +import net.taler.anastasis.backend.Tasks +import net.taler.anastasis.models.AggregatedPolicyMetaInfo +import net.taler.anastasis.models.AuthMethod +import net.taler.anastasis.models.AuthenticationProviderStatus +import net.taler.anastasis.models.BackupStates +import net.taler.anastasis.models.ChallengeFeedback +import net.taler.anastasis.models.ChallengeInfo +import net.taler.anastasis.models.ContinentInfo +import net.taler.anastasis.models.CoreSecret +import net.taler.anastasis.models.CountryInfo +import net.taler.anastasis.models.MethodSpec +import net.taler.anastasis.models.Policy +import net.taler.anastasis.models.RecoveryInformation +import net.taler.anastasis.models.RecoveryInternalData +import net.taler.anastasis.models.RecoveryStates +import net.taler.anastasis.models.ReducerState +import net.taler.anastasis.models.SelectedVersionInfo +import net.taler.anastasis.models.SuccessDetail +import net.taler.anastasis.models.UserAttributeSpec +import net.taler.common.Amount +import net.taler.common.Timestamp + +open class FakeReducerViewModel( + state: ReducerState, + error: TalerErrorInfo? = null, +): ReducerViewModelI { + override val reducerManager = null + private val _reducerState = MutableStateFlow<ReducerState?>(state) + override val reducerState: StateFlow<ReducerState?> = _reducerState.asStateFlow() + private val _reducerError = MutableStateFlow(error) + override val reducerError: StateFlow<TalerErrorInfo?> = _reducerError.asStateFlow() + private val _loading = MutableStateFlow(Tasks()) + override val tasks = _loading.asStateFlow() + + override fun goBack(): Boolean = false + + override fun goHome() { + _reducerState.value = null + } + + override fun cleanError() { + _reducerError.value = null + } +} + +internal val continents = listOf( + ContinentInfo(name = "Europe"), + ContinentInfo(name = "North America"), +) + +internal val countries = listOf( + CountryInfo( + code = "ch", + name = "Switzerland", + continent = "Europe", + ), + CountryInfo( + code = "de", + name = "Germany", + continent = "Europe", + ) +) + +internal val identityAttributes = mapOf( + "full_name" to "Max Musterman", + "birthdate" to "2000-01-01", +) + +internal val authenticationProviders = mapOf( + "http://localhost:8088/" to AuthenticationProviderStatus.Ok( + httpStatus = 200, + methods = listOf( + MethodSpec(type = AuthMethod.Type.Question, usageFee = "EUR:0.001"), + MethodSpec(type = AuthMethod.Type.Sms, usageFee = "EUR:0.55"), + ), + annualFee = "EUR:0.99", + truthUploadFee = "EUR:3.99", + liabilityLimit = "EUR:1", + currency = "EUR", + storageLimitInMegabytes = 1, + businessName = "Anastasis 4", + providerSalt = "CXAPCKSH9D3MYJTS9536RHJHCW", + ), + "http://localhost:8089/" to AuthenticationProviderStatus.Ok( + httpStatus = 200, + methods = listOf( + MethodSpec(type = AuthMethod.Type.Question, usageFee = "EUR:0.001"), + MethodSpec(type = AuthMethod.Type.Sms, usageFee = "EUR:0.55"), + ), + annualFee = "EUR:0.99", + truthUploadFee = "EUR:3.99", + liabilityLimit = "EUR:1", + currency = "EUR", + storageLimitInMegabytes = 1, + businessName = "Anastasis 2", + providerSalt = "CXAPCKSH9D3MYJTS9536RHJHCW", + ) +) + +internal val requiredAttributes = listOf( + UserAttributeSpec( + type = "string", + name = "full_name", + label = "Full name", + widget = "anastasis_gtk_ia_full_name", + uuid = "9e8f463f-575f-42cb-85f3-759559997331", + ), + UserAttributeSpec( + type = "date", + name = "birthdate", + label = "Birthdate", + widget = "anastasis_gtk_ia_birthdate", + uuid = "83d655c7-bdb6-484d-904e-80c1058c8854", + ), +) + +internal const val selectedContinent = "Europe" +internal const val selectedCountry = "ch" + +internal val coreSecret = CoreSecret( + value = "EDJP6WK5EG50", + mime = "text/plain", +) + +class FakeBackupViewModel( + backupState: BackupStates, +): FakeReducerViewModel( + state = ReducerState.Backup( + backupState = backupState, + continents = continents, + countries = countries, + identityAttributes = identityAttributes, + authenticationProviders = authenticationProviders, + authenticationMethods = listOf( + AuthMethod( + type = AuthMethod.Type.Question, + mimeType = "text/plain", + challenge = "E1QPPS8A", + instructions = "What is your favorite GNU package?", + ), + AuthMethod( + type = AuthMethod.Type.Email, + instructions = "E-mail to user@*le.com", + challenge = "ENSPAWJ0CNW62VBGDHJJWRVFDM50", + ) + ), + requiredAttributes = requiredAttributes, + selectedContinent = selectedContinent, + selectedCountry = selectedCountry, + secretName = "_TALERWALLET_MyPinePhone", + policies = listOf( + Policy( + methods = listOf( + Policy.PolicyMethod( + authenticationMethod = 0, + provider = "http://localhost:8089/", + ), + Policy.PolicyMethod( + authenticationMethod = 1, + provider = "http://localhost:8088/", + ), + ), + ), + Policy( + methods = listOf( + Policy.PolicyMethod( + authenticationMethod = 0, + provider = "http://localhost:8089/", + ), + ), + ), + ), + successDetails = mapOf( + "http://localhost:8088/" to SuccessDetail( + policyVersion = 1, + policyExpiration = Timestamp.now(), + ), + ), + coreSecret = coreSecret, + uploadFees = listOf( + ReducerState.Backup.UploadFee( + fee = Amount("KUDOS", 42L, 0), + ), + ), + ), +) + +class FakeRecoveryViewModel( + recoveryState: RecoveryStates, +): FakeReducerViewModel( + state = ReducerState.Recovery( + recoveryState = recoveryState, + continents = continents, + countries = countries, + identityAttributes = identityAttributes, + selectedContinent = selectedContinent, + selectedCountry = selectedCountry, + requiredAttributes = requiredAttributes, + recoveryInformation = RecoveryInformation( + challenges = listOf( + ChallengeInfo( + instructions = "What is your favorite GNU package?", + type = AuthMethod.Type.Question, + uuid = "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0", + ), + ChallengeInfo( + instructions = "E-mail to user@*le.com", + type = AuthMethod.Type.Email, + uuid = "ZA6T35B8XAR0DNKS5H100GK8PDPTA7Q8ST2FPQSYAZ4QRAA9XKK0", + ), + ), + policies = listOf( + listOf( + RecoveryInformation.Policy(uuid = "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0"), + RecoveryInformation.Policy(uuid = "ZA6T35B8XAR0DNKS5H100GK8PDPTA7Q8ST2FPQSYAZ4QRAA9XKK0") + ), + ), + ), + recoveryDocument = RecoveryInternalData( + secretName = "Secret", + providerUrl = "http://localhost:8089", + version = 1, + ), + selectedChallengeUuid = "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0", + challengeFeedback = mapOf( + "RNB84NQZPCM3MZWF9D5FFMSYYN07J2NAT5N8Q0DBHHT7R3GJ4AA0" to ChallengeFeedback.IncorrectAnswer, + "ZA6T35B8XAR0DNKS5H100GK8PDPTA7Q8ST2FPQSYAZ4QRAA9XKK0" to ChallengeFeedback.Solved, + ), + coreSecret = coreSecret, + authenticationProviders = authenticationProviders, + discoveryState = ReducerState.Recovery.DiscoveryState( + state = "finished", + aggregatedPolicies = listOf( + AggregatedPolicyMetaInfo( + attributeMask = 0, + policyHash = "000000000000000000000000000000000000000000000000000B28GR6691Y51HR2SAFJZFF0DCMRDZD1YQMS03A55P9NCWHQGEKW8", + providers = listOf( + SelectedVersionInfo.Provider( + url = "https://v1.anastasis.taler.net/", + version = 1, + ), + SelectedVersionInfo.Provider( + url = "https://v1.anastasis.codeblau.de/", + version = 1, + ), + ), + secretName = "Secret", + ), + ), + ), + ) +) diff --git a/anastasis/src/main/java/net/taler/anastasis/viewmodels/ReducerViewModel.kt b/anastasis/src/main/java/net/taler/anastasis/viewmodels/ReducerViewModel.kt index faba94a..a1fb08c 100644 --- a/anastasis/src/main/java/net/taler/anastasis/viewmodels/ReducerViewModel.kt +++ b/anastasis/src/main/java/net/taler/anastasis/viewmodels/ReducerViewModel.kt @@ -137,26 +137,3 @@ class ReducerViewModel @Inject constructor(): ViewModel(), ReducerViewModelI { _reducerError.value = null } } - -class FakeReducerViewModel( - state: ReducerState, - error: TalerErrorInfo? = null, -): ReducerViewModelI { - override val reducerManager = null - private val _reducerState = MutableStateFlow<ReducerState?>(state) - override val reducerState: StateFlow<ReducerState?> = _reducerState.asStateFlow() - private val _reducerError = MutableStateFlow(error) - override val reducerError: StateFlow<TalerErrorInfo?> = _reducerError.asStateFlow() - private val _loading = MutableStateFlow(Tasks()) - override val tasks = _loading.asStateFlow() - - override fun goBack(): Boolean = false - - override fun goHome() { - _reducerState.value = null - } - - override fun cleanError() { - _reducerError.value = null - } -}
\ No newline at end of file diff --git a/anastasis/src/main/res/values/strings.xml b/anastasis/src/main/res/values/strings.xml index 426ee26..d53db88 100644 --- a/anastasis/src/main/res/values/strings.xml +++ b/anastasis/src/main/res/values/strings.xml @@ -80,6 +80,7 @@ <string name="select_challenge_title">Select challenge</string> <string name="solve_challenge_title">Solve challenge</string> <string name="recovery_finished_title">Your secret was recovered</string> + <string name="recovery_providers">Manage recovery providers</string> <string name="challenge_solve">Solve</string> <string name="challenge_feedback_solved">Solved</string> <string name="challenge_feedback_incorrect_answer">Incorrect answer</string> |