Skip to content

feat: improve error messaging during onboarding flow (create account) #426

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

Merged
merged 1 commit into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.codeinc.gen.common.v1.Model
import com.codeinc.gen.transaction.v2.TransactionService
import com.codeinc.gen.transaction.v2.TransactionService.DeclareFiatOnrampPurchaseAttemptResponse
import com.codeinc.gen.transaction.v2.TransactionService.ExchangeDataWithoutRate
import com.codeinc.gen.transaction.v2.TransactionService.SubmitIntentResponse
import com.codeinc.gen.transaction.v2.TransactionService.SubmitIntentResponse.ResponseCase.ERROR
import com.codeinc.gen.transaction.v2.TransactionService.SubmitIntentResponse.ResponseCase.SERVER_PARAMETERS
import com.codeinc.gen.transaction.v2.TransactionService.SubmitIntentResponse.ResponseCase.SUCCESS
Expand Down Expand Up @@ -366,6 +367,9 @@ class TransactionRepository @Inject constructor(

expected?.diff(produced)
}
TransactionService.ErrorDetails.TypeCase.INTENT_DENIED -> {
errors.add("Denied: ${error.intentDenied.reason.name}")
}
else -> Unit
}

Expand Down Expand Up @@ -699,18 +703,53 @@ class TransactionRepository @Inject constructor(
val messageString: String = ""
) : Exception(cause) {
override val message: String
get() = "${errorSubmitIntent.name} $messageString"
get() = "${errorSubmitIntent.javaClass.simpleName} $messageString"
}

enum class DeniedReason {
Unspecified,
TooManyFreeAccountsForPhoneNumber,
TooManyFreeAccountsForDevice,
UnsupportedCountry,
UnsupportedDevice;

companion object {
fun fromValue(value: Int): DeniedReason {
return entries.firstOrNull { it.ordinal == value } ?: Unspecified
}
}
}

enum class ErrorSubmitIntent(val value: Int) {
Denied(0),
InvalidIntent(1),
SignatureError(2),
Unknown(-1);
sealed class ErrorSubmitIntent(val value: Int) {
data class Denied(val reasons: List<DeniedReason> = emptyList()): ErrorSubmitIntent(0)
data object InvalidIntent: ErrorSubmitIntent(1)
data object SignatureError: ErrorSubmitIntent(2)
data object StaleState: ErrorSubmitIntent(3)
data object Unknown: ErrorSubmitIntent(-1)
data object DeviceTokenUnavailable: ErrorSubmitIntent(-2)

companion object {
operator fun invoke(proto: SubmitIntentResponse.Error): ErrorSubmitIntent? {
return when (proto.code) {
SubmitIntentResponse.Error.Code.DENIED -> {
val reasons = proto.errorDetailsList.mapNotNull {
if (!it.hasIntentDenied()) return null
DeniedReason.fromValue(it.intentDenied.reasonValue)
}

Denied(reasons)
}
SubmitIntentResponse.Error.Code.INVALID_INTENT -> InvalidIntent
SubmitIntentResponse.Error.Code.SIGNATURE_ERROR -> SignatureError
SubmitIntentResponse.Error.Code.STALE_STATE -> StaleState
SubmitIntentResponse.Error.Code.UNRECOGNIZED -> Unknown
else -> return null
}
}

fun fromValue(value: Int): ErrorSubmitIntent {
return values().firstOrNull { it.value == value } ?: Unknown
return listOf(Denied(), InvalidIntent, SignatureError, StaleState)
.firstOrNull { it.value == value } ?: Unknown
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.getcode.model.intents.SwapConfigParameters
import com.getcode.model.intents.SwapIntent
import com.getcode.model.intents.requestToSubmitSignatures
import com.getcode.network.core.BidirectionalStreamReference
import com.getcode.network.repository.TransactionRepository.ErrorSubmitIntent
import com.getcode.solana.SolanaTransaction
import com.getcode.solana.diff
import com.getcode.solana.keys.Signature
Expand Down Expand Up @@ -92,6 +93,9 @@ private suspend fun TransactionRepository.submit(intent: SwapIntent): Result<Swa

expected?.diff(produced)
}
TransactionService.ErrorDetails.TypeCase.INTENT_DENIED -> {
errors.add("Denied: ${error.intentDenied.reason.name}")
}
else -> Unit
}

Expand All @@ -103,8 +107,8 @@ private suspend fun TransactionRepository.submit(intent: SwapIntent): Result<Swa
reference.stream?.onCompleted()
cont.resume(
Result.failure(
TransactionRepository.ErrorSubmitIntentException(
TransactionRepository.ErrorSubmitIntent.fromValue(value.error.codeValue),
ErrorSubmitSwapIntentException(
ErrorSubmitSwapIntent.valueOf(value.error.codeValue),
)
)
)
Expand All @@ -122,9 +126,7 @@ private suspend fun TransactionRepository.submit(intent: SwapIntent): Result<Swa
}
cont.resume(
Result.failure(
TransactionRepository.ErrorSubmitIntentException(
TransactionRepository.ErrorSubmitIntent.Unknown, t
)
ErrorSubmitSwapIntentException(ErrorSubmitSwapIntent.Unknown, t)
)
)
}
Expand All @@ -143,4 +145,28 @@ private suspend fun TransactionRepository.submit(intent: SwapIntent): Result<Swa
).build()

reference.stream?.onNext(initiateSwap)
}
}

class ErrorSubmitSwapIntentException(
val errorSubmitSwapIntent: ErrorSubmitSwapIntent,
cause: Throwable? = null,
val messageString: String = ""
) : Exception(cause) {
override val message: String
get() = "${errorSubmitSwapIntent.name} $messageString"
}

enum class ErrorSubmitSwapIntent(val value: Int) {
Denied(0),
InvalidIntent(1),
SignatureError(2),
StaleState(3),
Unknown(-1),
DeviceTokenUnavailable(-2);

companion object {
fun valueOf(value: Int): ErrorSubmitSwapIntent {
return entries.firstOrNull { it.value == value } ?: Unknown
}
}
}
3 changes: 2 additions & 1 deletion app/src/main/java/com/getcode/util/ErrorUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ fun ErrorUtils.showNetworkError(resources: ResourceHelper) = TopBarManager.TopBa
title = resources.getString(R.string.error_title_noInternet),
message =resources.getString(R.string.error_description_noInternet),
type = TopBarManager.TopBarMessageType.ERROR_NETWORK
).let { TopBarManager.showMessage(it) }
).let { TopBarManager.showMessage(it) }

52 changes: 43 additions & 9 deletions app/src/main/java/com/getcode/view/login/BaseAccessKeyViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import com.getcode.media.MediaScanner
import com.getcode.manager.MnemonicManager
import com.getcode.manager.SessionManager
import com.getcode.manager.TopBarManager
import com.getcode.network.repository.ApiDeniedException
import com.getcode.network.repository.TransactionRepository
import com.getcode.network.repository.decodeBase64
import com.getcode.theme.Brand
import com.getcode.theme.Transparent
Expand Down Expand Up @@ -257,21 +257,55 @@ abstract class BaseAccessKeyViewModel(
resources.getString(R.string.error_description_failedToSave),
)

private fun getDeniedError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_tooManyAccounts),
resources.getString(R.string.error_description_tooManyAccounts)
private fun getSomethingWentWrongError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_failedToCreateAccount),
resources.getString(R.string.error_description_failedToCreateAccount),
)

private fun getGenericError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_failedToVerifyPhone),
resources.getString(R.string.error_description_failedToVerifyPhone),
private fun getTooManyAccountsPerPhoneError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_tooManyAccountsPerPhone),
resources.getString(R.string.error_description_tooManyAccountsPerPhone)
)

private fun getTooManyAccountsPerDeviceError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_tooManyAccountsPerDevice),
resources.getString(R.string.error_description_tooManyAccountsPerDevice)
)

private fun getUnsupportedCountryError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_countryNotSupported),
resources.getString(R.string.error_description_countryNotSupported)
)

private fun getUnsupportedDeviceError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_deviceNotSupported),
resources.getString(R.string.error_description_deviceNotSupported)
)

internal fun onSubmitError(e: Throwable) {
when (e) {
is ApiDeniedException -> getDeniedError()
is TransactionRepository.ErrorSubmitIntentException -> {
when (val intent = e.errorSubmitIntent) {
is TransactionRepository.ErrorSubmitIntent.Denied -> {
if (intent.reasons.isEmpty()) {
getSomethingWentWrongError()
} else {
val reason = intent.reasons.first()
when (reason) {
TransactionRepository.DeniedReason.Unspecified -> getSomethingWentWrongError()
TransactionRepository.DeniedReason.TooManyFreeAccountsForPhoneNumber -> getTooManyAccountsPerPhoneError()
TransactionRepository.DeniedReason.TooManyFreeAccountsForDevice -> getTooManyAccountsPerDeviceError()
TransactionRepository.DeniedReason.UnsupportedCountry -> getUnsupportedCountryError()
TransactionRepository.DeniedReason.UnsupportedDevice -> getUnsupportedDeviceError()
}
}
getSomethingWentWrongError()
}
else -> getSomethingWentWrongError()
}
}
is IllegalStateException -> getAccessKeySaveError()
else -> getGenericError()
else -> getSomethingWentWrongError()
}.let { m -> TopBarManager.showMessage(m) }
}
}
32 changes: 27 additions & 5 deletions app/src/main/java/com/getcode/view/login/PhoneVerifyViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,17 @@ class PhoneVerifyViewModel @Inject constructor(
navigator.push(InviteCodeScreen(phoneNumber.urlEncode()))
null
}

PhoneVerificationService.SendVerificationCodeResponse.Result.INVALID_PHONE_NUMBER,
PhoneVerificationService.SendVerificationCodeResponse.Result.UNSUPPORTED_PHONE_TYPE -> {
getUnsupportedPhoneError()
}
PhoneVerificationService.SendVerificationCodeResponse.Result.UNSUPPORTED_COUNTRY -> {
getUnsupportedCountryError()
}
PhoneVerificationService.SendVerificationCodeResponse.Result.UNRECOGNIZED -> {
getUnsupportedDeviceError()
}
else -> getGenericError()
}?.let { message -> TopBarManager.showMessage(message) }
res == PhoneVerificationService.SendVerificationCodeResponse.Result.OK
Expand Down Expand Up @@ -223,12 +234,23 @@ class PhoneVerifyViewModel @Inject constructor(
)
}

private fun setValue(func: (PhoneVerifyUiModel) -> PhoneVerifyUiModel) {
uiFlow.value = func(uiFlow.value)
}

private fun getGenericError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_failedToSendCode),
resources.getString(R.string.error_description_failedToSendCode)
)
}

private fun getUnsupportedPhoneError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_eSimNotSupported),
resources.getString(R.string.error_description_eSimNotSupported)
)

private fun getUnsupportedDeviceError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_deviceNotSupported),
resources.getString(R.string.error_description_deviceNotSupported)
)

private fun getUnsupportedCountryError() = TopBarManager.TopBarMessage(
resources.getString(R.string.error_title_countryNotSupported),
resources.getString(R.string.error_description_countryNotSupported)
)
}
1 change: 0 additions & 1 deletion app/src/main/res/values-ar/strings-localized.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
<string name="error_description_contactsAccessRequired">يُرجى السماح بالوصول إلى جهات الاتصال في الإعدادات لإرسال الدعوات.</string>
<string name="error_description_failedToCreateAccount">لم نتوقع حدوث ذلك. حدث خطأ ما. يُرجى محاولة إنشاء هذا الحساب مرة أخرى.</string>
<string name="error_description_failedToSave">يُرجى السماح لـ Code بالوصول إلى الصور في الإعدادات من أجل حفظ مفتاح الوصول الخاص بك.</string>
<string name="error_description_failedToSendCode">حدث خطأ ما. يُرجى التأكد من أنك تستخدم رقم هاتف تمت دعوته وأنه تم إدخاله بشكل صحيح.</string>
<string name="error_description_failedToVerifyPhone">حدث خطأ ما. يُرجى المحاولة مرة أخرى.</string>
<string name="error_description_failedWithdrawal">فشلت عملية سحب أموالك. حدث خطأ ما, يُرجى محاولة السحب مرة أخرى.</string>
<string name="error_description_giveAmountTooLarge">تم تصميم Code للمعاملات الصغيرة اليومية في حدود %1$s أو أقل.</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-bg/strings-localized.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
<string name="error_description_contactsAccessRequired">Моля, разрешете достъпа до контактите чрез Настройки, за да изпращате покани.</string>
<string name="error_description_failedToCreateAccount">Неочаквано развитие. Нещо се обърка. Моля, опитайте да създадете профила отново. </string>
<string name="error_description_failedToSave">Моля, разрешете достъп на кода до снимки в настройките, за да може да съхраните вашия ключ за достъп. </string>
<string name="error_description_failedToSendCode">Нещо се обърка. Моля, уверете се, че използвате телефонен номер, който е получил поканата и че е въведен правилно. </string>
<string name="error_description_failedToVerifyPhone">Нещо се обърка. Моля, опитайте отново по - късно. </string>
<string name="error_description_failedWithdrawal">Неуспешно изтегляне на вашите средства. Нещо се обърка, моля, опитайте отново. </string>
<string name="error_description_giveAmountTooLarge">Код е създаден за малки и ежедневни транзакции, които са за %1$s или по-малко.</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-cs/strings-localized.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
<string name="error_description_contactsAccessRequired">Povolte přístup ke kontaktům v Nastavení pro odesílání pozvánek.</string>
<string name="error_description_failedToCreateAccount">Toto jsme nečekali. Nastala chyba. Zkuste tento účet vytvořit znovu.</string>
<string name="error_description_failedToSave">Povolte kódu přístup ke snímkům v nastavení a tím si uložte přístupový klíč.</string>
<string name="error_description_failedToSendCode">Nastala chyba. Ověřte si, že využíváte telefonní číslo, které bylo přizváno, a že jste je správně zadali.</string>
<string name="error_description_failedToVerifyPhone">Nastala chyba. Zkuste to znovu.</string>
<string name="error_description_failedWithdrawal">Vaše peníze se nepodařilo vybrat. Nastala chyba, zkuste provést výběr znovu.</string>
<string name="error_description_giveAmountTooLarge">Code je určen pro malé každodenní transakce, které jsou za %1$s nebo méně.</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-da/strings-localized.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
<string name="error_description_contactsAccessRequired">Tillad venligst adgang til kontakter i Indstillinger for at sende invitationer.</string>
<string name="error_description_failedToCreateAccount">Vi forventede ikke, at det ville ske. Noget gik galt. Prøv at oprette kontoen igen.</string>
<string name="error_description_failedToSave">Tillad Code adgang til billeder i Indstillinger for at gemme din adgangsnøgle.</string>
<string name="error_description_failedToSendCode">Noget gik galt. Sørg for, at du bruger det telefonnummer, der modtog invitationen, og at det er indtastet korrekt.</string>
<string name="error_description_failedToVerifyPhone">Noget gik galt. Prøv igen.</string>
<string name="error_description_failedWithdrawal">Det lykkedes ikke at hæve penge. Noget gik galt, prøv at hæve igen.</string>
<string name="error_description_giveAmountTooLarge">Code er designet til små hverdagstransaktioner på %1$s eller mindre.</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-de/strings-localized.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
<string name="error_description_contactsAccessRequired">Bitte erlauben Sie in den Einstellungen den Zugriff auf die Kontakte, um Einladungen zu versenden.</string>
<string name="error_description_failedToCreateAccount">Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie, dieses Konto erneut zu erstellen.</string>
<string name="error_description_failedToSave">Bitte erlauben Sie Code in den Einstellungen Zugriff auf Fotos, um Ihren Zugangsschlüssel speichern zu können.</string>
<string name="error_description_failedToSendCode">Es ist ein Fehler aufgetreten. Bitte vergewissern Sie sich, dass Sie eine Telefonnummer verwenden, für die Sie eine Enladung erhalten haben, und dass diese korrekt eingegeben wurde.</string>
<string name="error_description_failedToVerifyPhone">Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.</string>
<string name="error_description_failedWithdrawal">Die Abhebung Ihres Guthabens ist fehlgeschlagen. Etwas ist schief gelaufen. Bitte versuchen Sie die Abhebung erneut vorzunehmen.</string>
<string name="error_description_giveAmountTooLarge">Code ist für kleine, alltägliche Transaktionen gedacht, die sich auf %1$s oder weniger belaufen.</string>
Expand Down
Loading
Loading