diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 62760b043a..8d7f327c61 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -2229,13 +2229,19 @@ func apiCloseBadgeInvoice(_ userId: Int64, _ invoiceId: String) async throws { } // localized where the user can act on it; otherwise the error itself, so a screenshot says what happened -func redeemErrorText(_ error: Error) -> String { +func redeemErrorText(_ error: Error, purchase: Bool) -> String { if case let .error(.badgeRedeemError(e)) = error as? ChatError { switch e { case .invalidCode: return NSLocalizedString("This code is not valid.", comment: "alert message") case .invalidReceipt: break - case .serviceNotConfigured: return NSLocalizedString("This app version cannot redeem badge codes.", comment: "alert message") - case .badgeActive: return NSLocalizedString("This profile already has a badge. Redeem the code on another profile, or once this badge ends.", comment: "alert message") + case .serviceNotConfigured: + return purchase + ? NSLocalizedString("This app version cannot buy badges.", comment: "alert message") + : NSLocalizedString("This app version cannot redeem badge codes.", comment: "alert message") + case .badgeActive: + return purchase + ? NSLocalizedString("This profile already has a badge. The purchase will be added when this badge ends.", comment: "alert message") + : NSLocalizedString("This profile already has a badge. Redeem the code on another profile, or once this badge ends.", comment: "alert message") case let .serviceError(code): if let text = badgeServiceErrorText(code) { return text } case let .invalidResponse(message): return String.localizedStringWithFormat(NSLocalizedString("The badge service sent an unexpected response: %@", comment: "alert message"), message) diff --git a/apps/ios/Shared/Views/Badges/BadgeStore.swift b/apps/ios/Shared/Views/Badges/BadgeStore.swift index ae48c9548f..b25117cdda 100644 --- a/apps/ios/Shared/Views/Badges/BadgeStore.swift +++ b/apps/ios/Shared/Views/Badges/BadgeStore.swift @@ -258,7 +258,7 @@ final class BadgeStore: ObservableObject { logger.error("BadgeStore.presentPurchase: \(responseError(error))") let refused = badgeReceiptRefused(error) if refused { await finish(receipt) } - let text = redeemErrorText(error) + let text = redeemErrorText(error, purchase: true) alertText = refused ? text : text + "\n\n" + NSLocalizedString("The purchase will be retried, and the badge will arrive.", comment: "alert message") } await loadCurrentBadgeState() diff --git a/apps/ios/Shared/Views/Badges/BadgesCheckOrderView.swift b/apps/ios/Shared/Views/Badges/BadgesCheckOrderView.swift index 113665c620..a5214ffb3e 100644 --- a/apps/ios/Shared/Views/Badges/BadgesCheckOrderView.swift +++ b/apps/ios/Shared/Views/Badges/BadgesCheckOrderView.swift @@ -114,7 +114,7 @@ struct BadgesCheckOrderView: View { alert = SomeAlert( alert: Alert( title: Text("Purchase error"), - message: Text(verbatim: String(describing: error)) + message: Text(verbatim: redeemErrorText(error, purchase: true)) ), id: "badgePurchaseError" ) diff --git a/apps/ios/Shared/Views/Badges/BadgesRedeemCodeView.swift b/apps/ios/Shared/Views/Badges/BadgesRedeemCodeView.swift index ed53b21173..82b225e5b2 100644 --- a/apps/ios/Shared/Views/Badges/BadgesRedeemCodeView.swift +++ b/apps/ios/Shared/Views/Badges/BadgesRedeemCodeView.swift @@ -59,7 +59,7 @@ func redeemBadgeCode(_ user: User, _ code: String) async -> BadgeRedeemOutcome { } } catch let error { logger.error("apiRedeemBadgeCode: \(responseError(error))") - return .refused(message: redeemErrorText(error)) + return .refused(message: redeemErrorText(error, purchase: false)) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 596df2ca0b..2607fdb35b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -607,6 +607,7 @@ object ChatController { suspend fun apiCreateBadgeInvoice(rh: Long?, userId: Long): String { val r = sendCmd(rh, CC.ApiCreateBadgeInvoice(userId)) if (r is API.Result && r.res is CR.BadgeInvoice) return r.res.invoiceId + if (r is API.Error) throw BadgeStoreError.InvoiceRefused(r.err) throw Exception("apiCreateBadgeInvoice: unexpected ${r.responseType}") } @@ -617,13 +618,15 @@ object ChatController { } // localized where the user can act on it; otherwise the error itself, so a screenshot says what happened - fun redeemErrorText(err: ChatError?): String { + fun redeemErrorText(err: ChatError?, purchase: Boolean): String { if (err is ChatError.ChatErrorChat && err.errorType is ChatErrorType.CEBadgeRedeemError) { when (val e = err.errorType.badgeRedeemError) { is BadgeRedeemError.InvalidCode -> return generalGetString(MR.strings.badges_error_invalid_code) is BadgeRedeemError.InvalidReceipt -> {} - is BadgeRedeemError.ServiceNotConfigured -> return generalGetString(MR.strings.badges_error_service_not_configured) - is BadgeRedeemError.BadgeActive -> return generalGetString(MR.strings.badges_error_already_active) + is BadgeRedeemError.ServiceNotConfigured -> + return generalGetString(if (purchase) MR.strings.badges_error_service_not_configured_purchase else MR.strings.badges_error_service_not_configured) + is BadgeRedeemError.BadgeActive -> + return generalGetString(if (purchase) MR.strings.badges_error_already_active_purchase else MR.strings.badges_error_already_active) is BadgeRedeemError.ServiceError -> badgeServiceErrorText(e.serviceError)?.let { return it } is BadgeRedeemError.InvalidResponse -> return String.format(generalGetString(MR.strings.badges_error_bad_service_response), e.message) is BadgeRedeemError.UnknownKeyIndex, is BadgeRedeemError.CredentialNotVerified -> return generalGetString(MR.strings.badges_error_credential_not_verified) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesCheckOrderView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesCheckOrderView.kt index d02d854200..fd60c363fe 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesCheckOrderView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesCheckOrderView.kt @@ -100,7 +100,8 @@ private fun purchase(level: BadgeLevel, period: BadgePeriod, purchasing: Mutable purchasing.value = false AlertManager.shared.showAlertMsg( title = generalGetString(MR.strings.badges_purchase_error), - text = e.toString() + text = if (e is BadgeStoreError.InvoiceRefused) chatModel.controller.redeemErrorText(e.err, purchase = true) + else "${generalGetString(MR.strings.error_prefix)}: ${e.message ?: e}" ) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesRedeemCodeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesRedeemCodeView.kt index 1254671f59..3cc18f911d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesRedeemCodeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/badges/BadgesRedeemCodeView.kt @@ -85,7 +85,7 @@ suspend fun redeemBadgeCode(rhId: Long?, user: User, code: String): BadgeRedeemO } is BadgeRedeemResult.Failed -> { Log.e(TAG, "apiRedeemBadgeCode: ${result.err?.string}") - BadgeRedeemOutcome.Refused(chatModel.controller.redeemErrorText(result.err)) + BadgeRedeemOutcome.Refused(chatModel.controller.redeemErrorText(result.err, purchase = false)) } } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index c602416945..67710ca214 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -3211,7 +3211,9 @@ Cannot redeem code This code is not valid. This app version cannot redeem badge codes. + This app version cannot buy badges. This profile already has a badge. Redeem the code on another profile, or once this badge ends. + This profile already has a badge. The purchase will be added when this badge ends. This code was not recognized. This code has already been used. This code has expired.