From b04db06ed9b4fbe9c3fa5fc5d30deb0ed8e37201 Mon Sep 17 00:00:00 2001 From: "Gleb Levinkov (p2p)" Date: Tue, 20 Jun 2023 11:47:01 +0800 Subject: [PATCH 1/4] NO-TICKET - add metadata change logs --- .../java/org/p2p/wallet/auth/AuthModule.kt | 2 + .../gateway/parser/CountryCodeXmlParser.kt | 14 +++-- .../repository/mapper/BorshSerializable.kt | 6 +- .../GatewayServiceCreateWalletMapper.kt | 15 ++--- .../GatewayServiceUpdateMetadataMapper.kt | 5 +- .../auth/interactor/MetadataChangesLogger.kt | 40 ++++++++++++ .../auth/interactor/MetadataInteractor.kt | 9 +++ .../web3authsdk/Web3AuthDurationTracker.kt | 18 +++--- .../debug/pushservice/DebugWeb3Fragment.kt | 63 ++++++++++++++++++- .../infrastructure/network/NetworkModule.kt | 10 +++ .../wallet/sell/interactor/SellInteractor.kt | 6 +- .../main/res/layout/fragment_debug_web3.xml | 42 +++++++++++-- .../fragment_striga_sign_up_first_step.xml | 1 - .../external/api/EthereumNetworkModule.kt | 6 +- 14 files changed, 199 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt diff --git a/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt b/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt index a216641eb2..f9b3d29067 100644 --- a/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt +++ b/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt @@ -17,6 +17,7 @@ import org.p2p.wallet.auth.interactor.AuthLogoutInteractor import org.p2p.wallet.auth.interactor.CreateWalletInteractor import org.p2p.wallet.auth.interactor.FileInteractor import org.p2p.wallet.auth.interactor.GatewayMetadataMerger +import org.p2p.wallet.auth.interactor.MetadataChangesLogger import org.p2p.wallet.auth.interactor.MetadataInteractor import org.p2p.wallet.auth.interactor.OnboardingInteractor import org.p2p.wallet.auth.interactor.UserSignUpInteractor @@ -191,6 +192,7 @@ object AuthModule { factoryOf(::UserRestoreInteractor) factoryOf(::GatewayMetadataMerger) factoryOf(::MetadataInteractor) + factoryOf(::MetadataChangesLogger) singleOf(::RestoreStateMachine) } } diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt index 883c4c1b2d..d6f243f575 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt @@ -62,11 +62,15 @@ class CountryCodeXmlParser( private fun getMaskForCountryCode(countryCode: String, phoneCode: String): String { return try { val exampleNumber: Phonenumber.PhoneNumber? = phoneNumberUtil.getExampleNumber(countryCode) - val internationalFormat = phoneNumberUtil.format( - exampleNumber, - PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL - ) - internationalFormat.replace("+$phoneCode", emptyString()) + if (exampleNumber != null) { + val internationalFormat = phoneNumberUtil.format( + exampleNumber, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL + ) + internationalFormat.replace("+$phoneCode", emptyString()) + } else { + emptyString() + } } catch (e: Throwable) { Timber.i(e, "Get mask for country code failed") emptyString() diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt index 189fe2a9aa..a4c9b65c0f 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt @@ -34,8 +34,12 @@ interface BorshSerializable { fun BorshBuffer.write(vararg objects: Any): BorshBuffer { val validatedObjects: List = objects.map { if (it is JsonObject) it.toString() else it } validatedObjects.forEach { + if (it is ByteArray) { + this.writeFixedArray(it) + } else { + this.write(it) + } Timber.tag("BorshBuffer").d("Written to borsh: $it") - this.write(it) } return this } diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt index deee791a31..4566d589bb 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt @@ -3,6 +3,12 @@ package org.p2p.wallet.auth.gateway.repository.mapper import com.google.gson.Gson import com.google.gson.JsonObject import org.near.borshj.Borsh +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import kotlin.time.DurationUnit +import kotlin.time.toDuration +import org.p2p.core.utils.Constants import org.p2p.solanaj.utils.crypto.toBase64Instance import org.p2p.wallet.auth.gateway.api.request.ConfirmRegisterWalletRequest import org.p2p.wallet.auth.gateway.api.request.GatewayOnboardingMetadataCiphered @@ -15,16 +21,10 @@ import org.p2p.wallet.auth.gateway.repository.model.GatewayOnboardingMetadata import org.p2p.wallet.auth.gateway.repository.model.PushServiceError import org.p2p.wallet.auth.model.PhoneNumber import org.p2p.wallet.auth.web3authsdk.response.Web3AuthSignUpResponse +import org.p2p.wallet.settings.DeviceInfoHelper import org.p2p.wallet.utils.Base58String -import org.p2p.core.utils.Constants import org.p2p.wallet.utils.toByteArray import org.p2p.wallet.utils.toJsonObject -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import kotlin.time.DurationUnit -import kotlin.time.toDuration -import org.p2p.wallet.settings.DeviceInfoHelper const val TIMESTAMP_PATTERN_GATEWAY_SERVICE = "yyyy-MM-dd HH:mm:ssXXX" @@ -132,6 +132,7 @@ class GatewayServiceCreateWalletMapper( phoneNumberTimestampSec = epochUnixTimeSeconds, socialShareOwnerEmail = socialShareOwnerId, emailTimestampSec = epochUnixTimeSeconds, + authProviderTimestampSec = epochUnixTimeSeconds ) ) diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt index b1dafb721b..d46171d741 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt @@ -4,8 +4,7 @@ import com.google.gson.Gson import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.milliseconds import org.p2p.core.rpc.JsonRpc import org.p2p.solanaj.utils.crypto.toBase64Instance import org.p2p.wallet.auth.gateway.api.request.GatewayOnboardingMetadataCiphered @@ -42,7 +41,7 @@ class GatewayServiceUpdateMetadataMapper( userSeedPhrase: List, metadata: GatewayOnboardingMetadata, ): JsonRpc, UpdateMetadataResponse> { - val epochUnixTime = System.currentTimeMillis().toDuration(DurationUnit.MILLISECONDS) + val epochUnixTime = System.currentTimeMillis().milliseconds val encryptedMetadata: GatewayOnboardingMetadataCiphered = onboardingMetadataCipher.encryptMetadata( mnemonicPhrase = userSeedPhrase, onboardingMetadata = metadata diff --git a/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt new file mode 100644 index 0000000000..b91c3b0eaa --- /dev/null +++ b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt @@ -0,0 +1,40 @@ +package org.p2p.wallet.auth.interactor + +import androidx.core.content.edit +import android.content.SharedPreferences +import com.google.gson.Gson +import org.p2p.wallet.BuildConfig +import org.p2p.wallet.auth.gateway.repository.model.GatewayOnboardingMetadata +import org.p2p.wallet.utils.DateTimeUtils + +private const val KEY_CHANGES_LOG = "KEY_CHANGES_LOG" + +class MetadataChangesLogger( + private val sharedPreferences: SharedPreferences, + private val gson: Gson +) { + val logs: Set + get() = sharedPreferences.getStringSet(KEY_CHANGES_LOG, emptySet()).orEmpty() + + fun logChange( + metadataOld: GatewayOnboardingMetadata?, + metadataNew: GatewayOnboardingMetadata + ) { + if (BuildConfig.DEBUG) { + val metadataLogs = + sharedPreferences.getStringSet(KEY_CHANGES_LOG, emptySet()) ?: mutableSetOf() + + val logEntry = buildString { + append(DateTimeUtils.getFormattedDateAndTime(System.currentTimeMillis())) + append(":") + appendLine() + append("before=${gson.toJson(metadataOld)}") + appendLine() + append("after=${gson.toJson(metadataNew)}") + appendLine() + } + val newMetadataLogs = HashSet(metadataLogs).plus(logEntry) + sharedPreferences.edit { putStringSet(KEY_CHANGES_LOG, newMetadataLogs) } + } + } +} diff --git a/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt index 74ec2fd492..e7567fecb1 100644 --- a/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt @@ -26,6 +26,7 @@ class MetadataInteractor( private val gatewayMetadataMerger: GatewayMetadataMerger, private val ethereumInteractor: EthereumInteractor, private val bridgeFeatureToggle: EthAddressEnabledFeatureToggle, + private val metadataChangesLogger: MetadataChangesLogger ) { var currentMetadata: GatewayOnboardingMetadata? = null @@ -108,6 +109,10 @@ class MetadataInteractor( } suspend fun updateMetadata(metadata: GatewayOnboardingMetadata) { + metadataChangesLogger.logChange( + metadataOld = currentMetadata, + metadataNew = metadata + ) currentMetadata = metadata tryToUploadMetadata(metadata) } @@ -198,6 +203,10 @@ class MetadataInteractor( } updatedMetadata } ?: serverMetadata + metadataChangesLogger.logChange( + metadataOld = currentMetadata, + metadataNew = finalMetadata + ) currentMetadata = finalMetadata } } diff --git a/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt b/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt index 57c2467730..8169a30f21 100644 --- a/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt +++ b/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt @@ -1,15 +1,15 @@ package org.p2p.wallet.auth.web3authsdk -import org.p2p.wallet.auth.analytics.OnboardingAnalytics -import org.p2p.wallet.infrastructure.network.environment.TorusEnvironment -import org.p2p.wallet.utils.DateTimeUtils.PATTERN_HH_MM_SS_SS -import org.p2p.wallet.utils.DateTimeUtils.getFormattedDate -import org.p2p.wallet.utils.emptyString import timber.log.Timber import java.util.Date import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.toDuration +import org.p2p.wallet.auth.analytics.OnboardingAnalytics +import org.p2p.wallet.infrastructure.network.environment.TorusEnvironment +import org.p2p.wallet.utils.DateTimeUtils.PATTERN_HH_MM_SS_SS +import org.p2p.wallet.utils.DateTimeUtils.getFormattedDate +import org.p2p.wallet.utils.emptyString private const val EXPECTED_REQUEST_TIME_SEC = 15 private const val TAG = "Web3AuthDuration" @@ -35,10 +35,10 @@ class Web3AuthDurationTracker( Timber.tag(TAG).i( buildString { append("--> Web3Auth request: ") - append("$methodName;") - append("${torusNetwork.verifier};") - append("${torusNetwork.subVerifier};") - append("${torusNetwork.baseUrl};") + append("$methodName; ") + append("${torusNetwork.verifier}; ") + append("${torusNetwork.subVerifier}; ") + append("${torusNetwork.baseUrl}; ") append("date=${getFormattedDate(Date().time, PATTERN_HH_MM_SS_SS)}") } ) diff --git a/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt b/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt index 567332e033..f8eb6680a9 100644 --- a/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt +++ b/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt @@ -5,16 +5,25 @@ import androidx.lifecycle.lifecycleScope import android.annotation.SuppressLint import android.os.Bundle import android.view.View +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.gson.Gson import org.json.JSONObject import org.koin.android.ext.android.inject +import java.nio.charset.Charset +import org.p2p.solanaj.utils.crypto.toBase64Instance +import org.p2p.uikit.utils.toast import org.p2p.wallet.R +import org.p2p.wallet.auth.gateway.api.request.GatewayOnboardingMetadataCiphered +import org.p2p.wallet.auth.gateway.repository.mapper.GatewayServiceOnboardingMetadataCipher +import org.p2p.wallet.auth.interactor.MetadataChangesLogger import org.p2p.wallet.auth.interactor.MetadataInteractor import org.p2p.wallet.auth.model.MetadataLoadStatus import org.p2p.wallet.auth.repository.UserSignUpDetailsStorage import org.p2p.wallet.common.mvp.BaseFragment import org.p2p.wallet.databinding.FragmentDebugWeb3Binding +import org.p2p.wallet.utils.fromJsonReified import org.p2p.wallet.utils.popBackStack +import org.p2p.wallet.utils.shareText import org.p2p.wallet.utils.viewbinding.viewBinding @SuppressLint("SetTextI18n") @@ -27,6 +36,8 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { private val binding: FragmentDebugWeb3Binding by viewBinding() private val metadataInteractor: MetadataInteractor by inject() private val signUpDetailsStorage: UserSignUpDetailsStorage by inject() + private val metadataDecipher: GatewayServiceOnboardingMetadataCipher by inject() + private val metadataChangesLogger: MetadataChangesLogger by inject() private val gson: Gson by inject() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -38,6 +49,10 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { buttonLoadMetadata.setOnClickListener { lifecycleScope.launchWhenResumed { loadMetadata() } } + + buttonDecipherMetadata.setOnClickListener { + decipherMetadata() + } } } @@ -46,15 +61,19 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { val web3AuthData = signUpDetailsStorage.getLastSignUpUserDetails() val web3DataJson = web3AuthData - ?.let { JSONObject(gson.toJson(it)).toString(2) } + ?.toUiJson() ?: "None" val metadataJson = metadataInteractor.currentMetadata - ?.let { JSONObject(gson.toJson(it)).toString(2) } + ?.toUiJson() ?: if (web3AuthData != null) "Not loaded" else "None" textViewWeb3Value.text = web3DataJson textViewMetadataValue.text = metadataJson + + buttonMetadataLogs.setOnClickListener { + requireContext().shareText(metadataChangesLogger.logs.joinToString("\n")) + } } private suspend fun loadMetadata() { @@ -83,4 +102,44 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { } binding.buttonLoadMetadata.setLoading(false) } + + private fun decipherMetadata() { + val cipheredMetadata = binding.editTextDecipherMetadata.text?.toString() + ?.trim() + .orEmpty() + if (cipheredMetadata.isNotBlank()) { + // get json like GatewayOnboardingMetadataCiphered + val metadataAsJson = kotlin.runCatching { + gson.fromJsonReified( + cipheredMetadata.toBase64Instance() + .decodeToBytes() + .toString(Charset.defaultCharset()) + ) + } + .onFailure { toast(it.toString()) } + .getOrNull() + val seedPhrase = signUpDetailsStorage.getLastSignUpUserDetails() + ?.signUpDetails + ?.mnemonicPhraseWords + + if (seedPhrase != null && metadataAsJson != null) { + kotlin.runCatching { metadataDecipher.decryptMetadata(seedPhrase, metadataAsJson) } + .onSuccess { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(it.toUiJson()) + .setPositiveButton(R.string.common_ok) { d, _ -> + d.dismiss() + } + .setNegativeButton(R.string.common_share) { _, _ -> + requireContext().shareText(it.toUiJson()) + } + .show() + } + } + } + } + + private fun Any.toUiJson(): String { + return JSONObject(gson.toJson(this)).toString(2) + } } diff --git a/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt b/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt index c28db0d429..8ee9794342 100644 --- a/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt +++ b/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt @@ -18,6 +18,7 @@ import retrofit2.converter.scalars.ScalarsConverterFactory import java.math.BigDecimal import java.util.concurrent.TimeUnit import org.p2p.core.rpc.RPC_RETROFIT_QUALIFIER +import org.p2p.core.rpc.RpcApi import org.p2p.solanaj.utils.crypto.Base64String import org.p2p.wallet.BuildConfig import org.p2p.wallet.R @@ -92,6 +93,15 @@ object NetworkModule : InjectionModule { interceptor = RpcInterceptor(get(), get()) ) } + single { + getRetrofit( + // no need for baseUrl here, we pass URL inside RpcApi + baseUrl = "http://localhost/", + tag = "RpcApi", + interceptor = null + ) + .create(RpcApi::class.java) + } single(named(REN_POOL_RETROFIT_QUALIFIER)) { val environment = get().loadRpcEnvironment() val rpcApiUrl = environment.endpoint diff --git a/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt b/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt index c4118acfbb..b1bbbe9969 100644 --- a/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt @@ -56,9 +56,9 @@ class SellInteractor( sellRepository.isSellAllowedForUser() val debugInfo = buildString { - append("isFeatureEnabled=${sellEnabledFeatureToggle.isFeatureEnabled}") - append("isUserBalancePositive=${isUserBalancePositive()}") - append("isSellAllowedForUser=${sellRepository.isSellAllowedForUser()}") + append("isFeatureEnabled=${sellEnabledFeatureToggle.isFeatureEnabled} ") + append("isUserBalancePositive=${isUserBalancePositive()} ") + append("isSellAllowedForUser=${sellRepository.isSellAllowedForUser()} ") } Timber.i("Checking if sell is available: $debugInfo") return isSellAvailable diff --git a/app/src/main/res/layout/fragment_debug_web3.xml b/app/src/main/res/layout/fragment_debug_web3.xml index 3d6f3f9bb5..52017f2e7a 100644 --- a/app/src/main/res/layout/fragment_debug_web3.xml +++ b/app/src/main/res/layout/fragment_debug_web3.xml @@ -18,6 +18,40 @@ app:navigationIcon="@drawable/ic_back" app:title="Web3 User data" /> + + + + + + - (named(QUALIFIER_ETH_RETROFIT)).create(RpcApi::class.java) } single { get(named(QUALIFIER_ETH_RETROFIT)).create(CoinGeckoService::class.java) } } @@ -38,6 +37,7 @@ internal object EthereumNetworkModule { val requestBuilder = chain.request().newBuilder() chain.proceed(requestBuilder.build()) } + val httpClient = OkHttpClient.Builder() .addInterceptor(EthereumApiLoggingInterceptor()) .addInterceptor(headersInterceptor) From ad9c3e64f00f52f3cf68b0a707a8ad0e3391bea0 Mon Sep 17 00:00:00 2001 From: "Gleb Levinkov (p2p)" Date: Tue, 20 Jun 2023 19:22:57 +0800 Subject: [PATCH 2/4] NO-TICKET - review fixes --- .../debug/pushservice/DebugWeb3Fragment.kt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt b/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt index f8eb6680a9..be67ed429f 100644 --- a/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt +++ b/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt @@ -109,15 +109,16 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { .orEmpty() if (cipheredMetadata.isNotBlank()) { // get json like GatewayOnboardingMetadataCiphered - val metadataAsJson = kotlin.runCatching { - gson.fromJsonReified( - cipheredMetadata.toBase64Instance() - .decodeToBytes() - .toString(Charset.defaultCharset()) - ) - } - .onFailure { toast(it.toString()) } - .getOrNull() + val metadataAsJson = + kotlin.runCatching { + gson.fromJsonReified( + cipheredMetadata.toBase64Instance() + .decodeToBytes() + .toString(Charset.defaultCharset()) + ) + } + .onFailure { toast(it.toString()) } + .getOrNull() val seedPhrase = signUpDetailsStorage.getLastSignUpUserDetails() ?.signUpDetails ?.mnemonicPhraseWords From 555fa3b0baa4795d932e1a9ee483baa95458a548 Mon Sep 17 00:00:00 2001 From: "Gleb Levinkov (p2p)" Date: Tue, 20 Jun 2023 11:47:01 +0800 Subject: [PATCH 3/4] PWN-8982 - fix metadata and user status load on topup --- .../java/org/p2p/wallet/auth/AuthModule.kt | 2 + .../gateway/parser/CountryCodeXmlParser.kt | 14 ++-- .../repository/mapper/BorshSerializable.kt | 6 +- .../GatewayServiceCreateWalletMapper.kt | 15 ++-- .../GatewayServiceUpdateMetadataMapper.kt | 5 +- .../auth/interactor/MetadataChangesLogger.kt | 40 ++++++++++ .../auth/interactor/MetadataInteractor.kt | 9 +++ .../wallet/auth/model/MetadataLoadStatus.kt | 6 ++ .../wallet/auth/model/PhoneNumberWithCode.kt | 6 ++ .../web3authsdk/Web3AuthDurationTracker.kt | 18 ++--- .../debug/pushservice/DebugWeb3Fragment.kt | 63 +++++++++++++++- .../infrastructure/network/NetworkModule.kt | 10 +++ .../wallet/sell/interactor/SellInteractor.kt | 6 +- .../org/p2p/wallet/settings/SettingsModule.kt | 7 +- .../striga/StrigaSmsInputInteractor.kt | 24 +++--- .../striga/StrigaSmsInputPresenter.kt | 10 +-- .../interactor/StrigaSignupInteractor.kt | 19 ++--- .../wallet/striga/ui/TopUpWalletPresenter.kt | 73 +++++++++---------- .../p2p/wallet/striga/user/StrigaStorage.kt | 4 +- .../striga/user/StrigaStorageContract.kt | 4 +- .../user/interactor/StrigaUserInteractor.kt | 22 +++--- .../striga/user/model/StrigaUserDetails.kt | 2 +- .../repository/StrigaUserRemoteRepository.kt | 4 +- .../user/repository/StrigaUserRepository.kt | 4 +- .../repository/StrigaUserRepositoryMapper.kt | 6 +- .../StrigaUserStatusDestinationMapper.kt | 45 +++++------- .../repository/StrigaUserStatusRepository.kt | 40 ++++------ .../main/res/layout/fragment_debug_web3.xml | 42 ++++++++++- .../fragment_striga_sign_up_first_step.xml | 1 - .../external/api/EthereumNetworkModule.kt | 6 +- 30 files changed, 335 insertions(+), 178 deletions(-) create mode 100644 app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt diff --git a/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt b/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt index a216641eb2..f9b3d29067 100644 --- a/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt +++ b/app/src/main/java/org/p2p/wallet/auth/AuthModule.kt @@ -17,6 +17,7 @@ import org.p2p.wallet.auth.interactor.AuthLogoutInteractor import org.p2p.wallet.auth.interactor.CreateWalletInteractor import org.p2p.wallet.auth.interactor.FileInteractor import org.p2p.wallet.auth.interactor.GatewayMetadataMerger +import org.p2p.wallet.auth.interactor.MetadataChangesLogger import org.p2p.wallet.auth.interactor.MetadataInteractor import org.p2p.wallet.auth.interactor.OnboardingInteractor import org.p2p.wallet.auth.interactor.UserSignUpInteractor @@ -191,6 +192,7 @@ object AuthModule { factoryOf(::UserRestoreInteractor) factoryOf(::GatewayMetadataMerger) factoryOf(::MetadataInteractor) + factoryOf(::MetadataChangesLogger) singleOf(::RestoreStateMachine) } } diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt index 883c4c1b2d..d6f243f575 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/parser/CountryCodeXmlParser.kt @@ -62,11 +62,15 @@ class CountryCodeXmlParser( private fun getMaskForCountryCode(countryCode: String, phoneCode: String): String { return try { val exampleNumber: Phonenumber.PhoneNumber? = phoneNumberUtil.getExampleNumber(countryCode) - val internationalFormat = phoneNumberUtil.format( - exampleNumber, - PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL - ) - internationalFormat.replace("+$phoneCode", emptyString()) + if (exampleNumber != null) { + val internationalFormat = phoneNumberUtil.format( + exampleNumber, + PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL + ) + internationalFormat.replace("+$phoneCode", emptyString()) + } else { + emptyString() + } } catch (e: Throwable) { Timber.i(e, "Get mask for country code failed") emptyString() diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt index 189fe2a9aa..a4c9b65c0f 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/BorshSerializable.kt @@ -34,8 +34,12 @@ interface BorshSerializable { fun BorshBuffer.write(vararg objects: Any): BorshBuffer { val validatedObjects: List = objects.map { if (it is JsonObject) it.toString() else it } validatedObjects.forEach { + if (it is ByteArray) { + this.writeFixedArray(it) + } else { + this.write(it) + } Timber.tag("BorshBuffer").d("Written to borsh: $it") - this.write(it) } return this } diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt index deee791a31..4566d589bb 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceCreateWalletMapper.kt @@ -3,6 +3,12 @@ package org.p2p.wallet.auth.gateway.repository.mapper import com.google.gson.Gson import com.google.gson.JsonObject import org.near.borshj.Borsh +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import kotlin.time.DurationUnit +import kotlin.time.toDuration +import org.p2p.core.utils.Constants import org.p2p.solanaj.utils.crypto.toBase64Instance import org.p2p.wallet.auth.gateway.api.request.ConfirmRegisterWalletRequest import org.p2p.wallet.auth.gateway.api.request.GatewayOnboardingMetadataCiphered @@ -15,16 +21,10 @@ import org.p2p.wallet.auth.gateway.repository.model.GatewayOnboardingMetadata import org.p2p.wallet.auth.gateway.repository.model.PushServiceError import org.p2p.wallet.auth.model.PhoneNumber import org.p2p.wallet.auth.web3authsdk.response.Web3AuthSignUpResponse +import org.p2p.wallet.settings.DeviceInfoHelper import org.p2p.wallet.utils.Base58String -import org.p2p.core.utils.Constants import org.p2p.wallet.utils.toByteArray import org.p2p.wallet.utils.toJsonObject -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import kotlin.time.DurationUnit -import kotlin.time.toDuration -import org.p2p.wallet.settings.DeviceInfoHelper const val TIMESTAMP_PATTERN_GATEWAY_SERVICE = "yyyy-MM-dd HH:mm:ssXXX" @@ -132,6 +132,7 @@ class GatewayServiceCreateWalletMapper( phoneNumberTimestampSec = epochUnixTimeSeconds, socialShareOwnerEmail = socialShareOwnerId, emailTimestampSec = epochUnixTimeSeconds, + authProviderTimestampSec = epochUnixTimeSeconds ) ) diff --git a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt index b1dafb721b..d46171d741 100644 --- a/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt +++ b/app/src/main/java/org/p2p/wallet/auth/gateway/repository/mapper/GatewayServiceUpdateMetadataMapper.kt @@ -4,8 +4,7 @@ import com.google.gson.Gson import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -import kotlin.time.DurationUnit -import kotlin.time.toDuration +import kotlin.time.Duration.Companion.milliseconds import org.p2p.core.rpc.JsonRpc import org.p2p.solanaj.utils.crypto.toBase64Instance import org.p2p.wallet.auth.gateway.api.request.GatewayOnboardingMetadataCiphered @@ -42,7 +41,7 @@ class GatewayServiceUpdateMetadataMapper( userSeedPhrase: List, metadata: GatewayOnboardingMetadata, ): JsonRpc, UpdateMetadataResponse> { - val epochUnixTime = System.currentTimeMillis().toDuration(DurationUnit.MILLISECONDS) + val epochUnixTime = System.currentTimeMillis().milliseconds val encryptedMetadata: GatewayOnboardingMetadataCiphered = onboardingMetadataCipher.encryptMetadata( mnemonicPhrase = userSeedPhrase, onboardingMetadata = metadata diff --git a/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt new file mode 100644 index 0000000000..b91c3b0eaa --- /dev/null +++ b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataChangesLogger.kt @@ -0,0 +1,40 @@ +package org.p2p.wallet.auth.interactor + +import androidx.core.content.edit +import android.content.SharedPreferences +import com.google.gson.Gson +import org.p2p.wallet.BuildConfig +import org.p2p.wallet.auth.gateway.repository.model.GatewayOnboardingMetadata +import org.p2p.wallet.utils.DateTimeUtils + +private const val KEY_CHANGES_LOG = "KEY_CHANGES_LOG" + +class MetadataChangesLogger( + private val sharedPreferences: SharedPreferences, + private val gson: Gson +) { + val logs: Set + get() = sharedPreferences.getStringSet(KEY_CHANGES_LOG, emptySet()).orEmpty() + + fun logChange( + metadataOld: GatewayOnboardingMetadata?, + metadataNew: GatewayOnboardingMetadata + ) { + if (BuildConfig.DEBUG) { + val metadataLogs = + sharedPreferences.getStringSet(KEY_CHANGES_LOG, emptySet()) ?: mutableSetOf() + + val logEntry = buildString { + append(DateTimeUtils.getFormattedDateAndTime(System.currentTimeMillis())) + append(":") + appendLine() + append("before=${gson.toJson(metadataOld)}") + appendLine() + append("after=${gson.toJson(metadataNew)}") + appendLine() + } + val newMetadataLogs = HashSet(metadataLogs).plus(logEntry) + sharedPreferences.edit { putStringSet(KEY_CHANGES_LOG, newMetadataLogs) } + } + } +} diff --git a/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt index 74ec2fd492..e7567fecb1 100644 --- a/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/auth/interactor/MetadataInteractor.kt @@ -26,6 +26,7 @@ class MetadataInteractor( private val gatewayMetadataMerger: GatewayMetadataMerger, private val ethereumInteractor: EthereumInteractor, private val bridgeFeatureToggle: EthAddressEnabledFeatureToggle, + private val metadataChangesLogger: MetadataChangesLogger ) { var currentMetadata: GatewayOnboardingMetadata? = null @@ -108,6 +109,10 @@ class MetadataInteractor( } suspend fun updateMetadata(metadata: GatewayOnboardingMetadata) { + metadataChangesLogger.logChange( + metadataOld = currentMetadata, + metadataNew = metadata + ) currentMetadata = metadata tryToUploadMetadata(metadata) } @@ -198,6 +203,10 @@ class MetadataInteractor( } updatedMetadata } ?: serverMetadata + metadataChangesLogger.logChange( + metadataOld = currentMetadata, + metadataNew = finalMetadata + ) currentMetadata = finalMetadata } } diff --git a/app/src/main/java/org/p2p/wallet/auth/model/MetadataLoadStatus.kt b/app/src/main/java/org/p2p/wallet/auth/model/MetadataLoadStatus.kt index 96619a73b2..2c10ea9482 100644 --- a/app/src/main/java/org/p2p/wallet/auth/model/MetadataLoadStatus.kt +++ b/app/src/main/java/org/p2p/wallet/auth/model/MetadataLoadStatus.kt @@ -8,4 +8,10 @@ sealed interface MetadataLoadStatus { val cause: Throwable, val message: String? = cause.message ) : MetadataLoadStatus + + fun throwIfFailure() { + if (this is Failure) { + throw this.cause + } + } } diff --git a/app/src/main/java/org/p2p/wallet/auth/model/PhoneNumberWithCode.kt b/app/src/main/java/org/p2p/wallet/auth/model/PhoneNumberWithCode.kt index c28c8f6a49..94adbb7db9 100644 --- a/app/src/main/java/org/p2p/wallet/auth/model/PhoneNumberWithCode.kt +++ b/app/src/main/java/org/p2p/wallet/auth/model/PhoneNumberWithCode.kt @@ -2,6 +2,7 @@ package org.p2p.wallet.auth.model import android.os.Parcelable import kotlinx.parcelize.Parcelize +import org.p2p.wallet.common.ui.SimpleMaskFormatter /** * @param phoneCode - country of phone number @@ -18,4 +19,9 @@ data class PhoneNumberWithCode( val formatterPhoneNumber: String get() = "${phoneCodeWithPlusSign}$phoneNumberNational" + + val formattedPhoneNumberByMask: String + get() = phoneCode.mask.replace(Regex("\\d"), "#") + .let { SimpleMaskFormatter(it).format(phoneNumberNational) } + .let { "${phoneCodeWithPlusSign}$it" } } diff --git a/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt b/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt index 57c2467730..8169a30f21 100644 --- a/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt +++ b/app/src/main/java/org/p2p/wallet/auth/web3authsdk/Web3AuthDurationTracker.kt @@ -1,15 +1,15 @@ package org.p2p.wallet.auth.web3authsdk -import org.p2p.wallet.auth.analytics.OnboardingAnalytics -import org.p2p.wallet.infrastructure.network.environment.TorusEnvironment -import org.p2p.wallet.utils.DateTimeUtils.PATTERN_HH_MM_SS_SS -import org.p2p.wallet.utils.DateTimeUtils.getFormattedDate -import org.p2p.wallet.utils.emptyString import timber.log.Timber import java.util.Date import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.toDuration +import org.p2p.wallet.auth.analytics.OnboardingAnalytics +import org.p2p.wallet.infrastructure.network.environment.TorusEnvironment +import org.p2p.wallet.utils.DateTimeUtils.PATTERN_HH_MM_SS_SS +import org.p2p.wallet.utils.DateTimeUtils.getFormattedDate +import org.p2p.wallet.utils.emptyString private const val EXPECTED_REQUEST_TIME_SEC = 15 private const val TAG = "Web3AuthDuration" @@ -35,10 +35,10 @@ class Web3AuthDurationTracker( Timber.tag(TAG).i( buildString { append("--> Web3Auth request: ") - append("$methodName;") - append("${torusNetwork.verifier};") - append("${torusNetwork.subVerifier};") - append("${torusNetwork.baseUrl};") + append("$methodName; ") + append("${torusNetwork.verifier}; ") + append("${torusNetwork.subVerifier}; ") + append("${torusNetwork.baseUrl}; ") append("date=${getFormattedDate(Date().time, PATTERN_HH_MM_SS_SS)}") } ) diff --git a/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt b/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt index 567332e033..f8eb6680a9 100644 --- a/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt +++ b/app/src/main/java/org/p2p/wallet/debug/pushservice/DebugWeb3Fragment.kt @@ -5,16 +5,25 @@ import androidx.lifecycle.lifecycleScope import android.annotation.SuppressLint import android.os.Bundle import android.view.View +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.gson.Gson import org.json.JSONObject import org.koin.android.ext.android.inject +import java.nio.charset.Charset +import org.p2p.solanaj.utils.crypto.toBase64Instance +import org.p2p.uikit.utils.toast import org.p2p.wallet.R +import org.p2p.wallet.auth.gateway.api.request.GatewayOnboardingMetadataCiphered +import org.p2p.wallet.auth.gateway.repository.mapper.GatewayServiceOnboardingMetadataCipher +import org.p2p.wallet.auth.interactor.MetadataChangesLogger import org.p2p.wallet.auth.interactor.MetadataInteractor import org.p2p.wallet.auth.model.MetadataLoadStatus import org.p2p.wallet.auth.repository.UserSignUpDetailsStorage import org.p2p.wallet.common.mvp.BaseFragment import org.p2p.wallet.databinding.FragmentDebugWeb3Binding +import org.p2p.wallet.utils.fromJsonReified import org.p2p.wallet.utils.popBackStack +import org.p2p.wallet.utils.shareText import org.p2p.wallet.utils.viewbinding.viewBinding @SuppressLint("SetTextI18n") @@ -27,6 +36,8 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { private val binding: FragmentDebugWeb3Binding by viewBinding() private val metadataInteractor: MetadataInteractor by inject() private val signUpDetailsStorage: UserSignUpDetailsStorage by inject() + private val metadataDecipher: GatewayServiceOnboardingMetadataCipher by inject() + private val metadataChangesLogger: MetadataChangesLogger by inject() private val gson: Gson by inject() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -38,6 +49,10 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { buttonLoadMetadata.setOnClickListener { lifecycleScope.launchWhenResumed { loadMetadata() } } + + buttonDecipherMetadata.setOnClickListener { + decipherMetadata() + } } } @@ -46,15 +61,19 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { val web3AuthData = signUpDetailsStorage.getLastSignUpUserDetails() val web3DataJson = web3AuthData - ?.let { JSONObject(gson.toJson(it)).toString(2) } + ?.toUiJson() ?: "None" val metadataJson = metadataInteractor.currentMetadata - ?.let { JSONObject(gson.toJson(it)).toString(2) } + ?.toUiJson() ?: if (web3AuthData != null) "Not loaded" else "None" textViewWeb3Value.text = web3DataJson textViewMetadataValue.text = metadataJson + + buttonMetadataLogs.setOnClickListener { + requireContext().shareText(metadataChangesLogger.logs.joinToString("\n")) + } } private suspend fun loadMetadata() { @@ -83,4 +102,44 @@ class DebugWeb3Fragment : BaseFragment(R.layout.fragment_debug_web3) { } binding.buttonLoadMetadata.setLoading(false) } + + private fun decipherMetadata() { + val cipheredMetadata = binding.editTextDecipherMetadata.text?.toString() + ?.trim() + .orEmpty() + if (cipheredMetadata.isNotBlank()) { + // get json like GatewayOnboardingMetadataCiphered + val metadataAsJson = kotlin.runCatching { + gson.fromJsonReified( + cipheredMetadata.toBase64Instance() + .decodeToBytes() + .toString(Charset.defaultCharset()) + ) + } + .onFailure { toast(it.toString()) } + .getOrNull() + val seedPhrase = signUpDetailsStorage.getLastSignUpUserDetails() + ?.signUpDetails + ?.mnemonicPhraseWords + + if (seedPhrase != null && metadataAsJson != null) { + kotlin.runCatching { metadataDecipher.decryptMetadata(seedPhrase, metadataAsJson) } + .onSuccess { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(it.toUiJson()) + .setPositiveButton(R.string.common_ok) { d, _ -> + d.dismiss() + } + .setNegativeButton(R.string.common_share) { _, _ -> + requireContext().shareText(it.toUiJson()) + } + .show() + } + } + } + } + + private fun Any.toUiJson(): String { + return JSONObject(gson.toJson(this)).toString(2) + } } diff --git a/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt b/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt index c28db0d429..8ee9794342 100644 --- a/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt +++ b/app/src/main/java/org/p2p/wallet/infrastructure/network/NetworkModule.kt @@ -18,6 +18,7 @@ import retrofit2.converter.scalars.ScalarsConverterFactory import java.math.BigDecimal import java.util.concurrent.TimeUnit import org.p2p.core.rpc.RPC_RETROFIT_QUALIFIER +import org.p2p.core.rpc.RpcApi import org.p2p.solanaj.utils.crypto.Base64String import org.p2p.wallet.BuildConfig import org.p2p.wallet.R @@ -92,6 +93,15 @@ object NetworkModule : InjectionModule { interceptor = RpcInterceptor(get(), get()) ) } + single { + getRetrofit( + // no need for baseUrl here, we pass URL inside RpcApi + baseUrl = "http://localhost/", + tag = "RpcApi", + interceptor = null + ) + .create(RpcApi::class.java) + } single(named(REN_POOL_RETROFIT_QUALIFIER)) { val environment = get().loadRpcEnvironment() val rpcApiUrl = environment.endpoint diff --git a/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt b/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt index c4118acfbb..b1bbbe9969 100644 --- a/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/sell/interactor/SellInteractor.kt @@ -56,9 +56,9 @@ class SellInteractor( sellRepository.isSellAllowedForUser() val debugInfo = buildString { - append("isFeatureEnabled=${sellEnabledFeatureToggle.isFeatureEnabled}") - append("isUserBalancePositive=${isUserBalancePositive()}") - append("isSellAllowedForUser=${sellRepository.isSellAllowedForUser()}") + append("isFeatureEnabled=${sellEnabledFeatureToggle.isFeatureEnabled} ") + append("isUserBalancePositive=${isUserBalancePositive()} ") + append("isSellAllowedForUser=${sellRepository.isSellAllowedForUser()} ") } Timber.i("Checking if sell is available: $debugInfo") return isSellAvailable diff --git a/app/src/main/java/org/p2p/wallet/settings/SettingsModule.kt b/app/src/main/java/org/p2p/wallet/settings/SettingsModule.kt index 36ccca1519..f5ad7ebf44 100644 --- a/app/src/main/java/org/p2p/wallet/settings/SettingsModule.kt +++ b/app/src/main/java/org/p2p/wallet/settings/SettingsModule.kt @@ -16,10 +16,11 @@ import org.p2p.wallet.settings.ui.mail.SettingsEmailConfirmContract import org.p2p.wallet.settings.ui.mail.SettingsEmailConfirmPresenter import org.p2p.wallet.settings.ui.network.SettingsNetworkContract import org.p2p.wallet.settings.ui.network.SettingsNetworkPresenter -import org.p2p.wallet.settings.ui.security.SecurityAndPrivacyContract -import org.p2p.wallet.settings.ui.security.SecurityAndPrivacyPresenter import org.p2p.wallet.settings.ui.resetpin.pin.ResetPinContract import org.p2p.wallet.settings.ui.resetpin.pin.ResetPinPresenter +import org.p2p.wallet.settings.ui.security.SecurityAndPrivacyContract +import org.p2p.wallet.settings.ui.security.SecurityAndPrivacyPresenter +import org.p2p.wallet.settings.ui.settings.SettingsContract import org.p2p.wallet.settings.ui.settings.SettingsPresenter import org.p2p.wallet.settings.ui.settings.SettingsPresenterAnalytics import org.p2p.wallet.smsinput.SmsInputContract @@ -48,7 +49,7 @@ object SettingsModule : InjectionModule { authInteractor = get(), analyticsInteractor = get() ) - } + } bind SettingsContract.Presenter::class factoryOf(::SecurityAndPrivacyPresenter) bind SecurityAndPrivacyContract.Presenter::class factoryOf(::ResetPinPresenter) bind ResetPinContract.Presenter::class diff --git a/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputInteractor.kt b/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputInteractor.kt index 26b7dbb0a5..0f66a7e55d 100644 --- a/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputInteractor.kt @@ -1,5 +1,6 @@ package org.p2p.wallet.smsinput.striga +import org.p2p.wallet.auth.model.PhoneNumberWithCode import org.p2p.wallet.auth.repository.CountryCodeRepository import org.p2p.wallet.common.InAppFeatureFlags import org.p2p.wallet.striga.model.StrigaApiErrorCode @@ -8,6 +9,7 @@ import org.p2p.wallet.striga.model.StrigaDataLayerError import org.p2p.wallet.striga.model.StrigaDataLayerResult import org.p2p.wallet.striga.model.toFailureResult import org.p2p.wallet.striga.signup.repository.StrigaSignupDataLocalRepository +import org.p2p.wallet.striga.signup.repository.model.StrigaSignupData import org.p2p.wallet.striga.signup.repository.model.StrigaSignupDataType import org.p2p.wallet.striga.user.repository.StrigaUserRepository @@ -18,17 +20,21 @@ class StrigaSmsInputInteractor( private val inAppFeatureFlags: InAppFeatureFlags, ) { - suspend fun getUserPhoneCodeToPhoneNumber(): Pair { - val userSignupData = strigaSignupDataRepository.getUserSignupDataAsMap().unwrap() - val phoneCode = userSignupData[StrigaSignupDataType.PHONE_CODE_WITH_PLUS]?.value - ?: error("Failed to find phone code") - val phoneNumber = userSignupData[StrigaSignupDataType.PHONE_NUMBER]?.value - ?: error("Failed to find phone number") - return phoneCode to phoneNumber + suspend fun getUserPhoneCodeToPhoneNumber(): PhoneNumberWithCode { + val userSignupData = strigaSignupDataRepository.getUserSignupDataAsMap() + .unwrap() + return getPhoneNumberWithCodeFromLocal(userSignupData) } - fun getUserPhoneMask(phoneCode: String): String? { - return phoneCodeRepository.findCountryCodeByPhoneCode(phoneCode)?.mask + private fun getPhoneNumberWithCodeFromLocal( + userSignupData: Map + ): PhoneNumberWithCode { + val phoneCode = userSignupData[StrigaSignupDataType.PHONE_CODE_WITH_PLUS]?.value + ?.let { phoneCodeRepository.findCountryCodeByPhoneCode(it) } + ?: error("Failed to find phone code in signup details") + val phoneNumber = userSignupData[StrigaSignupDataType.PHONE_NUMBER]?.value + ?: error("Failed to find phone number in signup details") + return PhoneNumberWithCode(phoneCode, phoneNumber) } suspend fun validateSms(smsCode: String): StrigaDataLayerResult { diff --git a/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputPresenter.kt b/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputPresenter.kt index 46c9df88b6..8e93975b6c 100644 --- a/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/smsinput/striga/StrigaSmsInputPresenter.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.launch import org.p2p.wallet.R import org.p2p.wallet.auth.model.PhoneNumber import org.p2p.wallet.common.mvp.BasePresenter -import org.p2p.wallet.common.ui.SimpleMaskFormatter import org.p2p.wallet.smsinput.SmsInputContract import org.p2p.wallet.smsinput.SmsInputContract.Presenter.SmsInputTimerState import org.p2p.wallet.striga.model.StrigaApiErrorCode @@ -26,13 +25,8 @@ class StrigaSmsInputPresenter( private fun initPhoneNumber() { launch { try { - val (phoneCode, phoneNumber) = interactor.getUserPhoneCodeToPhoneNumber() - val phoneMask = interactor.getUserPhoneMask(phoneCode) - ?.replace(Regex("\\d"), "#") - ?: error("Couldn't find any masks for given code $phoneCode") - - val formattedPhoneNumber = SimpleMaskFormatter(phoneMask).format(phoneNumber) - view?.initView(PhoneNumber(formattedValue = "$phoneCode$formattedPhoneNumber")) + val phoneNumber = interactor.getUserPhoneCodeToPhoneNumber() + view?.initView(PhoneNumber(formattedValue = phoneNumber.formattedPhoneNumberByMask)) } catch (initViewError: Throwable) { view?.showUiKitSnackBar(messageResId = R.string.error_general_message) Timber.e(initViewError, "failed to init view for sms input") diff --git a/app/src/main/java/org/p2p/wallet/striga/signup/interactor/StrigaSignupInteractor.kt b/app/src/main/java/org/p2p/wallet/striga/signup/interactor/StrigaSignupInteractor.kt index bae9cdf115..ee595911d1 100644 --- a/app/src/main/java/org/p2p/wallet/striga/signup/interactor/StrigaSignupInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/striga/signup/interactor/StrigaSignupInteractor.kt @@ -14,6 +14,7 @@ import org.p2p.wallet.striga.model.StrigaApiErrorCode import org.p2p.wallet.striga.model.StrigaApiErrorResponse import org.p2p.wallet.striga.model.StrigaDataLayerError import org.p2p.wallet.striga.model.StrigaDataLayerResult +import org.p2p.wallet.striga.model.map import org.p2p.wallet.striga.signup.model.StrigaSignupFieldState import org.p2p.wallet.striga.signup.repository.StrigaSignupDataLocalRepository import org.p2p.wallet.striga.signup.repository.model.StrigaSignupData @@ -60,14 +61,12 @@ class StrigaSignupInteractor( ) } - suspend fun loadAndSaveSignupData() { - if (!userInteractor.isUserCreated()) { - return - } - val signupData = getSignupData() - if (signupData.isEmpty()) { + suspend fun loadAndSaveSignupData(): StrigaDataLayerResult { + return if (userInteractor.isUserCreated() && getSignupData().isEmpty()) { Timber.d("Striga signup data: loading from remote") - loadSignupDataFromRemote() + loadAndSaveSignupDataFromRemote().map { } + } else { + StrigaDataLayerResult.Success(Unit) } } @@ -226,14 +225,16 @@ class StrigaSignupInteractor( metadataInteractor.updateMetadata(newMetadata) } - private suspend fun loadSignupDataFromRemote() { - when (val userDetails = userInteractor.getUserDetails()) { + private suspend fun loadAndSaveSignupDataFromRemote(): StrigaDataLayerResult { + return when (val userDetails = userInteractor.getUserDetails()) { is StrigaDataLayerResult.Success -> { val signupData = userDetails.value.toSignupData() signupDataRepository.updateSignupData(signupData) + userDetails } is StrigaDataLayerResult.Failure -> { Timber.e(userDetails.error, "Unable to load striga user details") + userDetails } } } diff --git a/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt b/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt index e01c870f3e..b594582867 100644 --- a/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt @@ -7,11 +7,11 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.p2p.wallet.R import org.p2p.wallet.auth.interactor.MetadataInteractor -import org.p2p.wallet.auth.model.MetadataLoadStatus import org.p2p.wallet.common.InAppFeatureFlags import org.p2p.wallet.common.feature_toggles.toggles.remote.StrigaSignupEnabledFeatureToggle import org.p2p.wallet.common.mvp.BasePresenter import org.p2p.wallet.infrastructure.network.provider.SeedPhraseProvider +import org.p2p.wallet.striga.signup.interactor.StrigaSignupInteractor import org.p2p.wallet.striga.user.interactor.StrigaUserInteractor import org.p2p.wallet.striga.user.model.StrigaUserStatusDestination import org.p2p.wallet.user.interactor.UserInteractor @@ -22,6 +22,7 @@ class TopUpWalletPresenter( private val strigaSignupFeatureToggle: StrigaSignupEnabledFeatureToggle, private val seedPhraseProvider: SeedPhraseProvider, private val strigaUserInteractor: StrigaUserInteractor, + private val strigaSignupInteractor: StrigaSignupInteractor, private val metadataInteractor: MetadataInteractor, private val inAppFeatureFlags: InAppFeatureFlags, ) : BasePresenter(), @@ -51,61 +52,59 @@ class TopUpWalletPresenter( } override fun onBankTransferClicked() { - val strigaDestination = strigaUserInteractor.getUserDestination() - if (strigaDestination == StrigaUserStatusDestination.SMS_VERIFICATION) { - launch { - strigaUserInteractor.resendSmsForVerifyPhoneNumber() - } - } - // in case of simulation web3 user, we don't need to check metadata if (inAppFeatureFlags.strigaSimulateWeb3Flag.featureValue) { view?.navigateToBankTransferTarget(StrigaUserStatusDestination.ONBOARDING) return } - when { - // cannot fill the form or check whether user is created without metadata, loading if it's not loaded - metadataInteractor.currentMetadata == null -> { - Timber.i("Metadata is not fetched. Trying again...") - launch { - loadUserMetadata() + launch { + try { + strigaBankTransferProgress.emit(true) + + checkNeededStrigaDataLoaded() + + val strigaDestination = strigaUserInteractor.getUserDestination() + if (strigaDestination == StrigaUserStatusDestination.SMS_VERIFICATION) { + strigaUserInteractor.resendSmsForVerifyPhoneNumber().unwrap() } + view?.navigateToBankTransferTarget(strigaDestination) + } catch (strigaDataLoadFailed: Throwable) { + Timber.e(strigaDataLoadFailed, "failed to load needed data for bank transfer") + view?.showUiKitSnackBar(messageResId = R.string.error_general_message) + } finally { + strigaBankTransferProgress.emit(false) } - // if status is not fetched, fetching it - strigaDestination == null -> { + } + } + + private suspend fun checkNeededStrigaDataLoaded() { + if (metadataInteractor.currentMetadata == null) { + Timber.i("Metadata is not fetched. Trying again...") + loadUserMetadata() + } + + if (strigaUserInteractor.isUserCreated()) { + if (strigaUserInteractor.isUserVerificationStatusLoaded()) { Timber.i("Striga user status is not fetched. Trying again...") - launch { - loadStrigaUserStatus() - } + loadStrigaUserStatus() } - else -> { - view?.navigateToBankTransferTarget(strigaDestination) + if (!strigaUserInteractor.isUserDetailsLoaded()) { + Timber.i("Striga user signup data is not fetched. Trying again...") + loadStrigaUserDetails() } } } private suspend fun loadUserMetadata() { - withBankTransferProgress { - if (metadataInteractor.tryLoadAndSaveMetadata() is MetadataLoadStatus.Failure) { - view?.showUiKitSnackBar(messageResId = R.string.error_general_message) - } - } + metadataInteractor.tryLoadAndSaveMetadata().throwIfFailure() } private suspend fun loadStrigaUserStatus() { - withBankTransferProgress { - strigaUserInteractor.loadAndSaveUserStatusData() - .onFailure { - Timber.e(it, "failed to load user status for Striga") - view?.showUiKitSnackBar(messageResId = R.string.error_general_message) - } - } + strigaUserInteractor.loadAndSaveUserStatusData().unwrap() } - private suspend fun withBankTransferProgress(block: suspend () -> Unit) { - strigaBankTransferProgress.emit(true) - block() - strigaBankTransferProgress.emit(false) + private suspend fun loadStrigaUserDetails() { + strigaSignupInteractor.loadAndSaveSignupData().unwrap() } } diff --git a/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorage.kt b/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorage.kt index 5dbfcdd8fd..5860b7fe69 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorage.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorage.kt @@ -3,7 +3,7 @@ package org.p2p.wallet.striga.user import androidx.core.content.edit import android.content.SharedPreferences import com.google.gson.Gson -import org.p2p.wallet.striga.user.model.StrigaUserStatus +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails import org.p2p.wallet.utils.fromJsonReified private const val KEY_USER_STATUS = "KEY_USER_STATUS" @@ -13,7 +13,7 @@ class StrigaStorage( private val gson: Gson ) : StrigaStorageContract { - override var userStatus: StrigaUserStatus? + override var userStatus: StrigaUserStatusDetails? get() = sharedPreferences.getString(KEY_USER_STATUS, null)?.let(gson::fromJsonReified) set(value) { sharedPreferences.edit { diff --git a/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorageContract.kt b/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorageContract.kt index 65a75e2b1b..bfb868110d 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorageContract.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/StrigaStorageContract.kt @@ -1,7 +1,7 @@ package org.p2p.wallet.striga.user -import org.p2p.wallet.striga.user.model.StrigaUserStatus +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails interface StrigaStorageContract { - var userStatus: StrigaUserStatus? + var userStatus: StrigaUserStatusDetails? } diff --git a/app/src/main/java/org/p2p/wallet/striga/user/interactor/StrigaUserInteractor.kt b/app/src/main/java/org/p2p/wallet/striga/user/interactor/StrigaUserInteractor.kt index b382259f11..da1ff2e47f 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/interactor/StrigaUserInteractor.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/interactor/StrigaUserInteractor.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.flow.StateFlow import org.p2p.wallet.kyc.model.StrigaKycStatusBanner import org.p2p.wallet.striga.StrigaUserIdProvider import org.p2p.wallet.striga.model.StrigaDataLayerResult +import org.p2p.wallet.striga.signup.repository.StrigaSignupDataLocalRepository import org.p2p.wallet.striga.signup.repository.model.StrigaSignupData import org.p2p.wallet.striga.user.model.StrigaUserDetails import org.p2p.wallet.striga.user.model.StrigaUserInitialDetails @@ -14,7 +15,8 @@ import org.p2p.wallet.striga.user.repository.StrigaUserStatusRepository class StrigaUserInteractor( private val userRepository: StrigaUserRepository, private val strigaUserIdProvider: StrigaUserIdProvider, - private val userStatusRepository: StrigaUserStatusRepository + private val userStatusRepository: StrigaUserStatusRepository, + private val strigaSignupDataRepository: StrigaSignupDataLocalRepository ) { suspend fun createUser(data: List): StrigaDataLayerResult { @@ -25,20 +27,20 @@ class StrigaUserInteractor( return userRepository.getUserDetails() } - suspend fun verifyPhoneNumber(verificationCode: String): StrigaDataLayerResult { - return userRepository.verifyPhoneNumber(verificationCode) - } - suspend fun resendSmsForVerifyPhoneNumber(): StrigaDataLayerResult { return userRepository.resendSmsForVerifyPhoneNumber() } - fun isUserCreated(): Boolean { - return strigaUserIdProvider.getUserId() != null + fun isUserCreated(): Boolean = strigaUserIdProvider.getUserId() != null + + suspend fun isUserDetailsLoaded(): Boolean { + return strigaSignupDataRepository.getUserSignupData().unwrap().isEmpty() } + fun isUserVerificationStatusLoaded(): Boolean = userStatusRepository.getUserVerificationStatus() != null + suspend fun loadAndSaveUserStatusData(): StrigaDataLayerResult { - return userStatusRepository.loadUserKycStatus() + return userStatusRepository.loadAndSaveUserKycStatus() } fun getUserStatusBannerFlow(): StateFlow { @@ -46,9 +48,9 @@ class StrigaUserInteractor( } /** - * Returns user destination based on user status or null if unable to detect (no user status) + * Returns user destination based on user status or NONE if unable to detect (no user status) */ - fun getUserDestination(): StrigaUserStatusDestination? { + fun getUserDestination(): StrigaUserStatusDestination { return userStatusRepository.getUserDestination() } } diff --git a/app/src/main/java/org/p2p/wallet/striga/user/model/StrigaUserDetails.kt b/app/src/main/java/org/p2p/wallet/striga/user/model/StrigaUserDetails.kt index 5a562830ce..a7eab0ba4a 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/model/StrigaUserDetails.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/model/StrigaUserDetails.kt @@ -76,7 +76,7 @@ data class StrigaUserKycInfo( val rejectionUserToAutoComments: Pair? = null ) -data class StrigaUserStatus( +data class StrigaUserStatusDetails( val userId: String, val isEmailVerified: Boolean, val isMobileVerified: Boolean, diff --git a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRemoteRepository.kt b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRemoteRepository.kt index f7464131bc..030ef85de0 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRemoteRepository.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRemoteRepository.kt @@ -11,7 +11,7 @@ import org.p2p.wallet.striga.user.api.request.StrigaStartKycRequest import org.p2p.wallet.striga.user.api.request.StrigaVerifyMobileNumberRequest import org.p2p.wallet.striga.user.model.StrigaUserDetails import org.p2p.wallet.striga.user.model.StrigaUserInitialDetails -import org.p2p.wallet.striga.user.model.StrigaUserStatus +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails class StrigaUserRemoteRepository( private val api: StrigaApi, @@ -44,7 +44,7 @@ class StrigaUserRemoteRepository( } } - override suspend fun getUserStatus(): StrigaDataLayerResult { + override suspend fun getUserVerificationStatus(): StrigaDataLayerResult { return try { val response = api.getUserVerificationStatus(strigaUserIdProvider.getUserIdOrThrow()) mapper.fromNetwork(response).toSuccessResult() diff --git a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepository.kt b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepository.kt index 82fa3ef7bb..f01389c311 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepository.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepository.kt @@ -4,12 +4,12 @@ import org.p2p.wallet.striga.model.StrigaDataLayerResult import org.p2p.wallet.striga.signup.repository.model.StrigaSignupData import org.p2p.wallet.striga.user.model.StrigaUserDetails import org.p2p.wallet.striga.user.model.StrigaUserInitialDetails -import org.p2p.wallet.striga.user.model.StrigaUserStatus +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails interface StrigaUserRepository { suspend fun createUser(data: List): StrigaDataLayerResult suspend fun getUserDetails(): StrigaDataLayerResult - suspend fun getUserStatus(): StrigaDataLayerResult + suspend fun getUserVerificationStatus(): StrigaDataLayerResult suspend fun verifyPhoneNumber(verificationCode: String): StrigaDataLayerResult suspend fun resendSmsForVerifyPhoneNumber(): StrigaDataLayerResult diff --git a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepositoryMapper.kt b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepositoryMapper.kt index 8c3a558963..33b4578731 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepositoryMapper.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserRepositoryMapper.kt @@ -18,7 +18,7 @@ import org.p2p.wallet.striga.user.model.StrigaUserInfo import org.p2p.wallet.striga.user.model.StrigaUserInitialDetails import org.p2p.wallet.striga.user.model.StrigaUserInitialKycDetails import org.p2p.wallet.striga.user.model.StrigaUserKycInfo -import org.p2p.wallet.striga.user.model.StrigaUserStatus +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails import org.p2p.wallet.striga.user.model.StrigaUserVerificationStatus class StrigaUserRepositoryMapper { @@ -37,8 +37,8 @@ class StrigaUserRepositoryMapper { } } - fun fromNetwork(response: StrigaUserStatusResponse): StrigaUserStatus { - return StrigaUserStatus( + fun fromNetwork(response: StrigaUserStatusResponse): StrigaUserStatusDetails { + return StrigaUserStatusDetails( userId = response.userId, isMobileVerified = response.isMobileVerified, isEmailVerified = response.isEmailVerified, diff --git a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusDestinationMapper.kt b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusDestinationMapper.kt index fc5960757f..bc721b83f1 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusDestinationMapper.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusDestinationMapper.kt @@ -2,41 +2,22 @@ package org.p2p.wallet.striga.user.repository import timber.log.Timber import org.p2p.wallet.kyc.model.StrigaKycStatusBanner -import org.p2p.wallet.striga.user.model.StrigaUserStatus import org.p2p.wallet.striga.user.model.StrigaUserStatusDestination +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails import org.p2p.wallet.striga.user.model.StrigaUserVerificationStatus class StrigaUserStatusDestinationMapper { - fun mapToDestination(userStatus: StrigaUserStatus?, isUserCreated: Boolean): StrigaUserStatusDestination { - return mapToSignUpStatus(userStatus, isUserCreated) - } - - fun mapToStatusBanner(userStatus: StrigaUserStatus?): StrigaKycStatusBanner? { - return when (userStatus?.kysStatus) { - StrigaUserVerificationStatus.NOT_STARTED, - StrigaUserVerificationStatus.INITIATED -> StrigaKycStatusBanner.IDENTIFY - StrigaUserVerificationStatus.PENDING_REVIEW, - StrigaUserVerificationStatus.ON_HOLD -> StrigaKycStatusBanner.PENDING - StrigaUserVerificationStatus.APPROVED -> StrigaKycStatusBanner.VERIFICATION_DONE - StrigaUserVerificationStatus.REJECTED -> StrigaKycStatusBanner.ACTION_REQUIRED - StrigaUserVerificationStatus.REJECTED_FINAL -> StrigaKycStatusBanner.REJECTED - else -> null - } - } - - private fun isMobileVerified(userDetails: StrigaUserStatus?) = - userDetails?.isMobileVerified ?: false - - private fun mapToSignUpStatus(status: StrigaUserStatus?, isUserCreated: Boolean): StrigaUserStatusDestination { + fun mapToDestination(userStatus: StrigaUserStatusDetails?): StrigaUserStatusDestination { + val isUserNotCreated = userStatus == null return when { - !isUserCreated -> { + isUserNotCreated -> { StrigaUserStatusDestination.ONBOARDING } - !isMobileVerified(status) -> { + userStatus?.isMobileVerified == false -> { StrigaUserStatusDestination.SMS_VERIFICATION } - status?.kysStatus != null && status.kysStatus.ordinal > StrigaUserVerificationStatus.UNKNOWN.ordinal -> { + userStatus?.kysStatus != StrigaUserVerificationStatus.UNKNOWN -> { StrigaUserStatusDestination.SUM_SUB_VERIFICATION } else -> { @@ -45,4 +26,18 @@ class StrigaUserStatusDestinationMapper { } } } + + fun mapToStatusBanner(userStatus: StrigaUserStatusDetails?): StrigaKycStatusBanner? { + if (userStatus == null || !userStatus.isMobileVerified) return null + return when (userStatus.kysStatus) { + StrigaUserVerificationStatus.NOT_STARTED, + StrigaUserVerificationStatus.INITIATED -> StrigaKycStatusBanner.IDENTIFY + StrigaUserVerificationStatus.PENDING_REVIEW, + StrigaUserVerificationStatus.ON_HOLD -> StrigaKycStatusBanner.PENDING + StrigaUserVerificationStatus.APPROVED -> StrigaKycStatusBanner.VERIFICATION_DONE + StrigaUserVerificationStatus.REJECTED -> StrigaKycStatusBanner.ACTION_REQUIRED + StrigaUserVerificationStatus.REJECTED_FINAL -> StrigaKycStatusBanner.REJECTED + StrigaUserVerificationStatus.UNKNOWN -> null + } + } } diff --git a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusRepository.kt b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusRepository.kt index 83cb36d1cb..bc26d0249e 100644 --- a/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusRepository.kt +++ b/app/src/main/java/org/p2p/wallet/striga/user/repository/StrigaUserStatusRepository.kt @@ -13,8 +13,8 @@ import org.p2p.wallet.striga.model.StrigaDataLayerError import org.p2p.wallet.striga.model.StrigaDataLayerResult import org.p2p.wallet.striga.model.toSuccessResult import org.p2p.wallet.striga.user.StrigaStorageContract -import org.p2p.wallet.striga.user.model.StrigaUserStatus import org.p2p.wallet.striga.user.model.StrigaUserStatusDestination +import org.p2p.wallet.striga.user.model.StrigaUserStatusDetails private const val TAG = "StrigaUserStatusRepository" @@ -22,12 +22,12 @@ class StrigaUserStatusRepository( private val dispatchers: CoroutineDispatchers, private val strigaUserIdProvider: StrigaUserIdProvider, private val mapper: StrigaUserStatusDestinationMapper, - private val userRepository: StrigaUserRepository, + private val strigaUserRepository: StrigaUserRepository, private val strigaStorage: StrigaStorageContract, private val inAppFeatureFlags: InAppFeatureFlags ) : CoroutineScope by CoroutineScope(dispatchers.io) { - private val strigaUserDestinationFlow = MutableStateFlow(null) + private var strigaUserDestination: StrigaUserStatusDestination = StrigaUserStatusDestination.NONE private val strigaBannerFlow = MutableStateFlow(null) private val bannerMock: StrigaKycStatusBanner? @@ -36,26 +36,28 @@ class StrigaUserStatusRepository( ?.let { StrigaKycStatusBanner.valueOf(it) } init { - handleUserStatus(strigaStorage.userStatus) + mapUserStatusToFlows(strigaStorage.userStatus) } fun getBannerFlow(): StateFlow { return if (bannerMock != null) MutableStateFlow(bannerMock) else strigaBannerFlow } - fun getUserDestination(): StrigaUserStatusDestination? = strigaUserDestinationFlow.value + fun getUserDestination(): StrigaUserStatusDestination = strigaUserDestination - suspend fun loadUserKycStatus(): StrigaDataLayerResult = withContext(coroutineContext) { + fun getUserVerificationStatus(): StrigaUserStatusDetails? = strigaStorage.userStatus + + suspend fun loadAndSaveUserKycStatus(): StrigaDataLayerResult = withContext(coroutineContext) { try { // null user status can be only in case if user is not created at all (no id), // otherwise we must be sure that user status is not null or throw an exception val userStatus = if (strigaUserIdProvider.getUserId() == null) { null } else { - loadUserStatus() + strigaUserRepository.getUserVerificationStatus().unwrap() } strigaStorage.userStatus = userStatus - handleUserStatus(userStatus) + mapUserStatusToFlows(userStatus) Unit.toSuccessResult() } catch (e: Throwable) { Timber.tag(TAG).i(e, "Error while fetching user kyc status") @@ -66,24 +68,8 @@ class StrigaUserStatusRepository( } } - @Throws(StrigaDataLayerError::class) - private suspend fun loadUserStatus(): StrigaUserStatus { - return userRepository.getUserStatus().unwrap() - } - - private fun handleUserStatus(userStatus: StrigaUserStatus?) { - strigaUserDestinationFlow.value = userStatus.let(::mapToDestination) - strigaBannerFlow.value = userStatus.let(::mapToBanner) - } - - private fun mapToDestination(status: StrigaUserStatus?): StrigaUserStatusDestination { - return mapper.mapToDestination( - userStatus = status, - isUserCreated = strigaUserIdProvider.getUserId() != null - ) - } - - private fun mapToBanner(status: StrigaUserStatus?): StrigaKycStatusBanner? { - return mapper.mapToStatusBanner(status) + private fun mapUserStatusToFlows(userStatus: StrigaUserStatusDetails?) { + strigaUserDestination = userStatus.let(mapper::mapToDestination) + strigaBannerFlow.value = userStatus.let(mapper::mapToStatusBanner) } } diff --git a/app/src/main/res/layout/fragment_debug_web3.xml b/app/src/main/res/layout/fragment_debug_web3.xml index 3d6f3f9bb5..52017f2e7a 100644 --- a/app/src/main/res/layout/fragment_debug_web3.xml +++ b/app/src/main/res/layout/fragment_debug_web3.xml @@ -18,6 +18,40 @@ app:navigationIcon="@drawable/ic_back" app:title="Web3 User data" /> + + + + + + - (named(QUALIFIER_ETH_RETROFIT)).create(RpcApi::class.java) } single { get(named(QUALIFIER_ETH_RETROFIT)).create(CoinGeckoService::class.java) } } @@ -38,6 +37,7 @@ internal object EthereumNetworkModule { val requestBuilder = chain.request().newBuilder() chain.proceed(requestBuilder.build()) } + val httpClient = OkHttpClient.Builder() .addInterceptor(EthereumApiLoggingInterceptor()) .addInterceptor(headersInterceptor) From 92a2199344de40d25f76ffda595cdc53867ef364 Mon Sep 17 00:00:00 2001 From: "Gleb Levinkov (p2p)" Date: Tue, 20 Jun 2023 20:02:42 +0800 Subject: [PATCH 4/4] NO-TICKET - review fixes --- .../java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt b/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt index b594582867..ea7f3252e7 100644 --- a/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt +++ b/app/src/main/java/org/p2p/wallet/striga/ui/TopUpWalletPresenter.kt @@ -62,7 +62,7 @@ class TopUpWalletPresenter( try { strigaBankTransferProgress.emit(true) - checkNeededStrigaDataLoaded() + ensureNeededStrigaDataLoaded() val strigaDestination = strigaUserInteractor.getUserDestination() if (strigaDestination == StrigaUserStatusDestination.SMS_VERIFICATION) { @@ -78,7 +78,7 @@ class TopUpWalletPresenter( } } - private suspend fun checkNeededStrigaDataLoaded() { + private suspend fun ensureNeededStrigaDataLoaded() { if (metadataInteractor.currentMetadata == null) { Timber.i("Metadata is not fetched. Trying again...") loadUserMetadata()