mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-10-20 18:56:25 +00:00
feat: tell users a new version exists, and let them rate the wallet from inside it (#17)
Two gaps, both invisible until you look for them. There was no update mechanism at all. Someone on an old build stayed on it until they happened to open the Play Store on their own. For a wallet that is worse than an inconvenience: today's multisig signing fix would have reached nobody who was not already looking. InAppUpdates asks Play on every foreground. A user a few days behind gets the flexible flow — the download runs in the background and the wallet stays usable, because interrupting someone mid-transfer to force an update is its own kind of harm. Past fourteen days of staleness, or on a release marked priority 4+ in Play Console, it switches to immediate. onResume finishes an interrupted immediate update and installs a flexible one that completed while the app was backgrounded; without that the first leaves a user stuck and the second never installs. Ratings had the same shape of gap: people who would happily rate the wallet never do, because nothing ever asks. Play's in-app card asks without sending them to the store. Play answers neither "has this user rated" nor "what did they choose" — by design. It also throttles to a handful of showings a year and silently drops the rest. So the gates in AppReviewTracker are not there to avoid nagging, which Play already handles; they exist to spend those few real chances well: three successful operations, three days since first use, ninety since the last ask, and not within two days of an error. Recording sits in RealExtrinsicService, the single point every on-chain action passes through, so transfers and staking are covered without a hook per screen. The tracker swallows everything it touches — a rating counter must never be able to fail a transfer. Both features no-op outside a Play install, so neither can be verified from a Firebase build; that needs an internal testing track. Version 1.2.0.
This commit is contained in:
1 parent
56c37615a0
commit
27aff873a6
16 files changed
+331
-8
No files matched your search
+8
-1
@@ -1,6 +1,7 @@
|
||||
package io.novafoundation.nova.feature_account_impl.data.extrinsic
|
||||
|
||||
import android.util.Log
|
||||
import io.novafoundation.nova.common.appstore.AppReviewTracker
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.DispatchError
|
||||
import io.novafoundation.nova.common.data.network.runtime.binding.bindDispatchError
|
||||
import io.novafoundation.nova.common.data.network.runtime.model.FeeResponse
|
||||
@@ -74,6 +75,7 @@ class RealExtrinsicService(
|
||||
private val feePaymentProviderRegistry: FeePaymentProviderRegistry,
|
||||
private val eventsRepository: EventsRepository,
|
||||
private val signingContextFactory: SigningContext.Factory,
|
||||
private val appReviewTracker: AppReviewTracker,
|
||||
private val coroutineScope: CoroutineScope? // TODO: Make it non-nullable
|
||||
) : ExtrinsicService {
|
||||
|
||||
@@ -86,8 +88,13 @@ class RealExtrinsicService(
|
||||
val (extrinsic, submissionOrigin, _, callExecutionType, signingHierarchy) = buildSubmissionExtrinsic(chain, origin, formExtrinsic, submissionOptions)
|
||||
val hash = rpcCalls.submitExtrinsic(chain.id, extrinsic)
|
||||
|
||||
// The one place every on-chain action passes through, so counting here covers
|
||||
// transfers, staking and the rest without a hook per screen. The tracker never
|
||||
// throws — a rating counter must not be able to fail a submission.
|
||||
appReviewTracker.onMeaningfulSuccess()
|
||||
|
||||
ExtrinsicSubmission(hash, submissionOrigin, callExecutionType, signingHierarchy)
|
||||
}
|
||||
}.onFailure { appReviewTracker.onFailure() }
|
||||
|
||||
override suspend fun submitMultiExtrinsicAwaitingInclusion(
|
||||
chain: Chain,
|
||||
|
||||
+4
-1
@@ -1,5 +1,6 @@
|
||||
package io.novafoundation.nova.feature_account_impl.data.extrinsic
|
||||
|
||||
import io.novafoundation.nova.common.appstore.AppReviewTracker
|
||||
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicService
|
||||
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicSplitter
|
||||
import io.novafoundation.nova.feature_account_api.data.fee.FeePaymentProviderRegistry
|
||||
@@ -21,6 +22,7 @@ class RealExtrinsicServiceFactory(
|
||||
private val eventsRepository: EventsRepository,
|
||||
private val feePaymentProviderRegistry: FeePaymentProviderRegistry,
|
||||
private val signingContextFactory: SigningContext.Factory,
|
||||
private val appReviewTracker: AppReviewTracker,
|
||||
) : ExtrinsicService.Factory {
|
||||
|
||||
override fun create(feeConfig: ExtrinsicService.FeePaymentConfig): ExtrinsicService {
|
||||
@@ -35,7 +37,8 @@ class RealExtrinsicServiceFactory(
|
||||
feePaymentProviderRegistry = registry,
|
||||
eventsRepository = eventsRepository,
|
||||
coroutineScope = feeConfig.coroutineScope,
|
||||
signingContextFactory = signingContextFactory
|
||||
signingContextFactory = signingContextFactory,
|
||||
appReviewTracker = appReviewTracker
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -12,6 +12,7 @@ import io.novafoundation.nova.common.data.network.NetworkApiCreator
|
||||
import io.novafoundation.nova.common.data.network.rpc.SocketSingleRequestExecutor
|
||||
import io.novafoundation.nova.common.data.secrets.v1.SecretStoreV1
|
||||
import io.novafoundation.nova.common.data.secrets.v2.SecretStoreV2
|
||||
import io.novafoundation.nova.common.appstore.AppReviewTracker
|
||||
import io.novafoundation.nova.common.data.storage.Preferences
|
||||
import io.novafoundation.nova.common.data.storage.encrypt.EncryptedPreferences
|
||||
import io.novafoundation.nova.common.di.modules.Caching
|
||||
@@ -148,6 +149,8 @@ interface AccountFeatureDependencies {
|
||||
|
||||
fun preferences(): Preferences
|
||||
|
||||
fun appReviewTracker(): AppReviewTracker
|
||||
|
||||
fun encryptedPreferences(): EncryptedPreferences
|
||||
|
||||
fun resourceManager(): ResourceManager
|
||||
|
||||
+7
-2
@@ -4,6 +4,7 @@ import com.google.gson.Gson
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.common.appstore.AppReviewTracker
|
||||
import io.novafoundation.nova.common.address.AddressIconGenerator
|
||||
import io.novafoundation.nova.common.data.mappers.mapEncryptionToCryptoType
|
||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||
@@ -254,7 +255,8 @@ class AccountFeatureModule {
|
||||
extrinsicSplitter: ExtrinsicSplitter,
|
||||
feePaymentProviderRegistry: FeePaymentProviderRegistry,
|
||||
eventsRepository: EventsRepository,
|
||||
signingContextFactory: SigningContext.Factory
|
||||
signingContextFactory: SigningContext.Factory,
|
||||
appReviewTracker: AppReviewTracker,
|
||||
): ExtrinsicService.Factory = RealExtrinsicServiceFactory(
|
||||
rpcCalls,
|
||||
chainRegistry,
|
||||
@@ -264,7 +266,8 @@ class AccountFeatureModule {
|
||||
extrinsicSplitter,
|
||||
eventsRepository,
|
||||
feePaymentProviderRegistry,
|
||||
signingContextFactory
|
||||
signingContextFactory,
|
||||
appReviewTracker
|
||||
)
|
||||
|
||||
@Provides
|
||||
@@ -279,6 +282,7 @@ class AccountFeatureModule {
|
||||
feePaymentProviderRegistry: FeePaymentProviderRegistry,
|
||||
eventsRepository: EventsRepository,
|
||||
signingContextFactory: SigningContext.Factory,
|
||||
appReviewTracker: AppReviewTracker,
|
||||
): ExtrinsicService = RealExtrinsicService(
|
||||
rpcCalls,
|
||||
chainRegistry,
|
||||
@@ -289,6 +293,7 @@ class AccountFeatureModule {
|
||||
feePaymentProviderRegistry,
|
||||
eventsRepository,
|
||||
signingContextFactory,
|
||||
appReviewTracker,
|
||||
coroutineScope = null
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user