Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Android add seed handle and show proper errors #2302

Merged
merged 17 commits into from
Jan 29, 2024
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3f73286
work in progress - catching errors when storage created to pass it to UI
Dmitry-Borodin Jan 17, 2024
930462a
seed storage error handling implemented in seed storage
Dmitry-Borodin Jan 18, 2024
24778b5
passing seed storage errors to the UI and show error
Dmitry-Borodin Jan 18, 2024
72b84aa
fixed feature toggle, migrated logs to timber
Dmitry-Borodin Jan 18, 2024
b0677b0
Merge branch 'master' into android-fix-unlock-crash
Dmitry-Borodin Jan 19, 2024
aafec6b
Merge branch 'master' of github.com:paritytech/parity-signer into and…
Dmitry-Borodin Jan 20, 2024
099aab3
Merge branch 'android-fix-unlock-crash' of github.com:paritytech/pari…
Dmitry-Borodin Jan 20, 2024
49d9b70
added error state for global nav state
Dmitry-Borodin Jan 20, 2024
6418c93
removed outdated log
Dmitry-Borodin Jan 20, 2024
1857516
passing errors for add seed as well - work in progress
Dmitry-Borodin Jan 20, 2024
baa5ec0
passing errors while adding seed work in progress
Dmitry-Borodin Jan 20, 2024
77c145b
error state handling process 2
Dmitry-Borodin Jan 20, 2024
f09d5ff
Merge branch 'master' of github.com:paritytech/parity-signer into and…
Dmitry-Borodin Jan 22, 2024
f039460
implemented showing error as end of add seed operation
Dmitry-Borodin Jan 24, 2024
d014f54
Merge branch 'master' of github.com:paritytech/parity-signer into and…
Dmitry-Borodin Jan 24, 2024
da8da7e
Merge branch 'master' into android-add-seed-errors
prybalko Jan 24, 2024
92ea132
Merge branch 'master' into android-add-seed-errors
Dmitry-Borodin Jan 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.parity.signer.domain.backend

import android.content.Context
import io.parity.signer.domain.AuthResult
import io.parity.signer.domain.KeySetDetailsModel
import io.parity.signer.domain.KeySetsListModel
import io.parity.signer.domain.NetworkModel
@@ -354,6 +355,13 @@ sealed class OperationResult<out T, out E> {
data class Err<out E>(val error: E) : OperationResult<Nothing, E>()
}

sealed interface AuthOperationResult{
data object Success: AuthOperationResult
data class Error(val exception: Exception): AuthOperationResult
data class AuthFailed(val result: AuthResult) : AuthOperationResult

}

sealed class CompletableResult<out T, out E> {
data class Ok<out T>(val result: T) : CompletableResult<T, Nothing>()
data class Err<out E>(val error: E) : CompletableResult<Nothing, E>()
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import io.parity.signer.domain.AuthResult
import io.parity.signer.domain.Authentication
import io.parity.signer.domain.backend.AuthOperationResult
import io.parity.signer.domain.backend.OperationResult
import io.parity.signer.domain.backend.UniffiInteractor
import io.parity.signer.domain.backend.UniffiResult
@@ -121,33 +122,31 @@ class SeedRepository(
seedName: String,
seedPhrase: String,
networksKeys: List<String>
): Boolean {
//todo return operation result here and show error in UI
// Check if seed name already exists
): AuthOperationResult {
if (isSeedPhraseCollision(seedPhrase)) {
return false
return AuthOperationResult.Error(Exception("Seed Phrase Collision - can't proceed"))
}

try {
addSeedDangerous(seedName, seedPhrase, networksKeys)
return true
return AuthOperationResult.Success
} catch (e: UserNotAuthenticatedException) {
return when (val authResult = authentication.authenticate(activity)) {
AuthResult.AuthSuccess -> {
addSeedDangerous(seedName, seedPhrase, networksKeys)
true
AuthOperationResult.Success
}

AuthResult.AuthError,
AuthResult.AuthFailed,
AuthResult.AuthUnavailable -> {
Timber.e(TAG, "auth error - $authResult")
false
Timber.w(TAG, "auth error - $authResult")
AuthOperationResult.AuthFailed(authResult)
}
}
} catch (e: java.lang.Exception) {
Timber.e(TAG, e.toString())
return false
return AuthOperationResult.Error(e)
}
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.parity.signer.domain.usecases

import io.parity.signer.dependencygraph.ServiceLocator
import io.parity.signer.domain.backend.AuthOperationResult

/**
* Creates key set
@@ -11,7 +12,7 @@ class CreateKeySetUseCase() {
seedName: String,
seedPhrase: String,
networksKeys: List<String>,
): Boolean {
): AuthOperationResult {
val repository = ServiceLocator.activityScope!!.seedRepository
return repository.addSeed(
seedName = seedName,
Original file line number Diff line number Diff line change
@@ -8,15 +8,11 @@ import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import io.parity.signer.domain.NavigationError
import io.parity.signer.domain.backend.OperationResult
import io.parity.signer.domain.backend.UniffiResult
import io.parity.signer.domain.backend.toOperationResult
import io.parity.signer.domain.getDebugDetailedDescriptionString
import io.parity.signer.screens.error.wrongversion.errorWrongVersionSubgraph
import io.parity.signer.screens.initial.eachstartchecks.airgap.AirgapScreen
import io.parity.signer.ui.mainnavigation.CoreUnlockedNavSubgraph
import io.parity.signer.uniffi.ErrorDisplayed


fun NavGraphBuilder.errorStateDestination(
@@ -67,63 +63,3 @@ inline fun <reified T> UniffiResult<T>.handleErrorAppState(coreNavController: Na
return this.toOperationResult().handleErrorAppState(coreNavController)
}

data class ErrorStateDestinationState(
val argHeader: String,
val argDescription: String,
val argVerbose: String,
)

inline fun <reified T, E> OperationResult<T, E>.handleErrorAppState(
coreNavController: NavController
): T? {
return when (this) {
is OperationResult.Err -> {
coreNavController.navigate(
when (error) {
is ErrorStateDestinationState -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = error.argHeader,
argDescription = error.argDescription,
argVerbose = error.argVerbose,
)
}
is NavigationError -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation navigation error trying to get ${T::class.java}",
argDescription = error.message,
argVerbose = "",
)
}

is ErrorDisplayed ->
when (error) {
is ErrorDisplayed.DbSchemaMismatch -> {
CoreUnlockedNavSubgraph.errorWrongDbVersionUpdate
}

else -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation error to get ${T::class.java}",
argDescription = error.toString(),
argVerbose = error.getDebugDetailedDescriptionString(),
)
}
}

else -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation unknown error trying to get ${T::class.java}",
argDescription = "",
argVerbose = error.toString(),
)
}
}
)
null
}

is OperationResult.Ok -> {
result
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package io.parity.signer.screens.error

import android.content.Context
import android.widget.Toast
import androidx.navigation.NavController
import io.parity.signer.R
import io.parity.signer.domain.NavigationError
import io.parity.signer.domain.backend.AuthOperationResult
import io.parity.signer.domain.backend.OperationResult
import io.parity.signer.domain.getDebugDetailedDescriptionString
import io.parity.signer.ui.mainnavigation.CoreUnlockedNavSubgraph
import io.parity.signer.uniffi.ErrorDisplayed


data class ErrorStateDestinationState(
val argHeader: String,
val argDescription: String,
val argVerbose: String,
)

inline fun <reified T, E> OperationResult<T, E>.handleErrorAppState(
coreNavController: NavController
): T? {
return when (this) {
is OperationResult.Err -> {
coreNavController.navigate(
when (error) {
is ErrorStateDestinationState -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = error.argHeader,
argDescription = error.argDescription,
argVerbose = error.argVerbose,
)
}

is NavigationError -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation navigation error trying to get ${T::class.java}",
argDescription = error.message,
argVerbose = "",
)
}

is ErrorDisplayed ->
when (error) {
is ErrorDisplayed.DbSchemaMismatch -> {
CoreUnlockedNavSubgraph.errorWrongDbVersionUpdate
}

else -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation error to get ${T::class.java}",
argDescription = error.toString(),
argVerbose = error.getDebugDetailedDescriptionString(),
)
}
}

else -> {
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation unknown error trying to get ${T::class.java}",
argDescription = "",
argVerbose = error.toString(),
)
}
}
)
null
}

is OperationResult.Ok -> {
result
}
}
}


fun AuthOperationResult.handleErrorAppState(
coreNavController: NavController,
context: Context,
): Unit? {
return when (this) {
is AuthOperationResult.AuthFailed -> {
Toast.makeText(context, R.string.auth_failed_message, Toast.LENGTH_SHORT)
.show()
null
}

is AuthOperationResult.Error -> {
coreNavController.navigate(
CoreUnlockedNavSubgraph.ErrorScreenGeneral.destination(
argHeader = "Operation error",
argDescription = exception.toString(),
argVerbose = exception.stackTraceToString(),
)
)
null
}

AuthOperationResult.Success -> {
Unit
}
}
}
Original file line number Diff line number Diff line change
@@ -74,11 +74,6 @@ internal fun ErrorStateScreen(
)
}
}
// PrimaryButtonWide(
// label = stringResource(R.string.button_next),
// modifier = Modifier.padding(24.dp),
// onClicked = {},
// )
}
}

Original file line number Diff line number Diff line change
@@ -6,12 +6,11 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
Original file line number Diff line number Diff line change
@@ -12,11 +12,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import io.parity.signer.domain.backend.AuthOperationResult
import io.parity.signer.domain.backend.toOperationResult
import io.parity.signer.domain.popUpToTop
import io.parity.signer.screens.error.handleErrorAppState
@@ -96,6 +98,7 @@ fun NewKeysetSubgraph(
}
}
composable(NewKeySetBackupStepSubgraph.NewKeySetSelectNetworks) {
val context = LocalContext.current
NewKeySetSelectNetworkScreen(
seedName = seedName,
seedPhrase = seedPhrase,
@@ -108,6 +111,9 @@ fun NewKeysetSubgraph(
popUpToTop(coreNavController)
}
},
showError = { error: AuthOperationResult ->
error.handleErrorAppState(coreNavController, context)
},
onBack = subgraphNavController::popBackStack,
)
}
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import io.parity.signer.dependencygraph.ServiceLocator
import io.parity.signer.domain.Callback
import io.parity.signer.domain.NetworkModel
import io.parity.signer.domain.backend.AuthOperationResult
import io.parity.signer.domain.usecases.AllNetworksUseCase
import io.parity.signer.domain.usecases.CreateKeySetUseCase
import kotlinx.coroutines.launch
@@ -23,7 +24,7 @@ class NewKeySetNetworksViewModel : ViewModel() {
fun createKeySetWithNetworks(
seedName: String, seedPhrase: String,
networkForKeys: Set<NetworkModel>,
onAfterCreate: (Boolean) -> Unit = {},
onAfterCreate: (AuthOperationResult) -> Unit = {},
): Unit {
viewModelScope.launch {
val success = createKeySetUseCase.createKeySetWithNetworks(
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ import io.parity.signer.components.base.SignerDivider
import io.parity.signer.components.items.NetworkItemMultiselect
import io.parity.signer.domain.Callback
import io.parity.signer.domain.NetworkModel
import io.parity.signer.domain.backend.AuthOperationResult
import io.parity.signer.ui.BottomSheetWrapperContent
import io.parity.signer.ui.theme.SignerNewTheme
import io.parity.signer.ui.theme.SignerTypeface
@@ -54,6 +55,7 @@ fun NewKeySetSelectNetworkScreen(
seedName: String,
seedPhrase: String,
onBack: Callback,
showError: (AuthOperationResult) -> Unit,
onSuccess: Callback,
) {
val networksViewModel: NewKeySetNetworksViewModel = viewModel()
@@ -82,14 +84,24 @@ fun NewKeySetSelectNetworkScreen(
seedName = seedName, seedPhrase = seedPhrase,
networkForKeys = selected.value.mapNotNull { selected -> networks.find { it.key == selected } }
.toSet(),
onAfterCreate = { isSuccess ->
if (isSuccess) {
Toast.makeText(
context,
context.getString(R.string.key_set_has_been_created_toast, seedName),
Toast.LENGTH_LONG
).show()
onSuccess()
onAfterCreate = { success ->
when (success) {
is AuthOperationResult.AuthFailed,
is AuthOperationResult.Error -> {
showError(success)
}

AuthOperationResult.Success -> {
Toast.makeText(
context,
context.getString(
R.string.key_set_has_been_created_toast,
seedName
),
Toast.LENGTH_LONG
).show()
onSuccess()
}
}
}
)
Loading
Loading