fix: connect referral link to blockchain via deep link handler

When Alice shares a referral link, it now generates a deep link URL
(pezkuwiwallet://pezkuwi/open/citizenship?referrer=address) that
auto-opens the citizenship form with Alice's address pre-filled as
the referrer, fixing the bug where referrer defaulted to founder.
This commit is contained in:
2026-02-27 22:11:34 +03:00
parent 894e5dac22
commit 33c6559a41
10 changed files with 97 additions and 12 deletions
+1 -1
View File
@@ -2778,7 +2778,7 @@
<string name="citizenship_approved">Tu jixwe hemwelatî yî</string>
<string name="citizenship_insufficient_balance">Di People Chain de balance têr nîne. Herî kêm 1.1 HEZ pêwîst e.</string>
<string name="citizenship_success">Serlêdan bi serkeftî hat şandin</string>
<string name="citizenship_share_referral">Ji bo hemwelatîbûna Kurdistana Dîjîtal hevalê xwe vexwîne!\n\nNavnîşana min a referansê:\n%s</string>
<string name="citizenship_share_referral">Ji bo hemwelatîbûna Kurdistana Dîjîtal hevalê xwe vexwîne!\n\nDi sepanê de veke:\n%1$s\n\nNavnîşana referansê:\n%2$s</string>
<string name="citizenship_share_button">Lînka Referansê Parve Bike</string>
<string name="citizenship_referrer_hint">Navnîşana Referansê (ne mecbûrî)</string>
<string name="citizenship_sign_description">Referansa te serlêdana te pejirand. Ji bo temamkirina hemwelatîbûnê îmze bike.</string>
+1 -1
View File
@@ -45,7 +45,7 @@
<string name="citizenship_approved">Zaten vatandaşsınız</string>
<string name="citizenship_insufficient_balance">People Chain\'de yetersiz bakiye. En az 1.1 HEZ gerekli.</string>
<string name="citizenship_success">Başvuru başarıyla gönderildi</string>
<string name="citizenship_share_referral">Dijital Kurdistan vatandaşlığı için arkadaşını davet et!\n\nReferans adresim:\n%s</string>
<string name="citizenship_share_referral">Dijital Kurdistan vatandaşlığı için arkadaşını davet et!\n\nUygulamada aç:\n%1$s\n\nReferans adresi:\n%2$s</string>
<string name="citizenship_share_button">Referans Linkini Paylaş</string>
<string name="citizenship_referrer_hint">Referans Adresi (opsiyonel)</string>
<string name="citizenship_sign_description">Referansınız başvurunuzu onayladı. Vatandaşlığı tamamlamak için imzalayın.</string>
+1 -1
View File
@@ -2782,7 +2782,7 @@
<string name="citizenship_approved">You are already a citizen</string>
<string name="citizenship_insufficient_balance">Insufficient balance on People Chain. At least 1.1 HEZ required.</string>
<string name="citizenship_success">Application submitted successfully</string>
<string name="citizenship_share_referral">Refer a friend for Digital Kurdistan citizenship!\n\nMy referrer address:\n%s</string>
<string name="citizenship_share_referral">Refer a friend for Digital Kurdistan citizenship!\n\nOpen in app:\n%1$s\n\nReferrer address:\n%2$s</string>
<string name="citizenship_share_button">Share Referral Link</string>
<string name="citizenship_referrer_hint">Referrer Address (optional)</string>
<string name="citizenship_sign_description">Your referrer has approved your application. Sign to complete citizenship.</string>
@@ -8,6 +8,7 @@ import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepos
import io.novafoundation.nova.feature_assets.presentation.AssetsRouter
import io.novafoundation.nova.feature_assets.presentation.balance.detail.deeplink.AssetDetailsDeepLinkConfigurator
import io.novafoundation.nova.feature_assets.presentation.balance.detail.deeplink.AssetDetailsDeepLinkHandler
import io.novafoundation.nova.feature_assets.presentation.citizenship.deeplink.CitizenshipDeepLinkHandler
import io.novafoundation.nova.feature_assets.presentation.novacard.common.NovaCardRestrictionCheckMixin
import io.novafoundation.nova.feature_assets.presentation.novacard.overview.deeplink.NovaCardDeepLinkHandler
import io.novafoundation.nova.feature_deep_linking.presentation.configuring.LinkBuilderFactory
@@ -56,12 +57,21 @@ class DeepLinkModule {
)
}
@Provides
@FeatureScope
fun provideCitizenshipDeepLinkHandler(
automaticInteractionGate: AutomaticInteractionGate
): CitizenshipDeepLinkHandler {
return CitizenshipDeepLinkHandler(automaticInteractionGate)
}
@Provides
@FeatureScope
fun provideDeepLinks(
assetDetails: AssetDetailsDeepLinkHandler,
novaCardDeepLink: NovaCardDeepLinkHandler
novaCardDeepLink: NovaCardDeepLinkHandler,
citizenshipDeepLink: CitizenshipDeepLinkHandler
): AssetDeepLinks {
return AssetDeepLinks(listOf(assetDetails, novaCardDeepLink))
return AssetDeepLinks(listOf(assetDetails, novaCardDeepLink, citizenshipDeepLink))
}
}
@@ -178,8 +178,8 @@ class BalanceListFragment :
viewModel.filtersIndicatorIcon.observe(headerAdapter::setFilterIconRes)
viewModel.assetViewModeModelFlow.observe { manageAssetsAdapter.setAssetViewModeModel(it) }
viewModel.openCitizenshipEvent.observeEvent {
CitizenshipBottomSheet().show(childFragmentManager, "citizenship")
viewModel.openCitizenshipEvent.observeEvent { referrer ->
CitizenshipBottomSheet.newInstance(referrer).show(childFragmentManager, "citizenship")
}
viewModel.shareReferralEvent.observeEvent { shareText ->
@@ -40,6 +40,7 @@ import io.novafoundation.nova.feature_assets.presentation.balance.breakdown.mode
import io.novafoundation.nova.feature_assets.presentation.balance.common.AssetListMixinFactory
import io.novafoundation.nova.feature_assets.presentation.balance.common.buySell.BuySellSelectorMixin
import io.novafoundation.nova.feature_assets.presentation.balance.common.buySell.BuySellSelectorMixinFactory
import io.novafoundation.nova.feature_assets.presentation.citizenship.PendingCitizenshipReferrer
import io.novafoundation.nova.feature_assets.presentation.balance.list.model.NftPreviewUi
import io.novafoundation.nova.feature_assets.presentation.balance.list.model.TotalBalanceModel
import io.novafoundation.nova.feature_assets.presentation.balance.list.view.AssetViewModeModel
@@ -115,8 +116,8 @@ class BalanceListViewModel(
private val _showBalanceBreakdownEvent = MutableLiveData<Event<TotalBalanceBreakdownModel>>()
val showBalanceBreakdownEvent: LiveData<Event<TotalBalanceBreakdownModel>> = _showBalanceBreakdownEvent
private val _openCitizenshipEvent = MutableLiveData<Event<Unit>>()
val openCitizenshipEvent: LiveData<Event<Unit>> = _openCitizenshipEvent
private val _openCitizenshipEvent = MutableLiveData<Event<String?>>()
val openCitizenshipEvent: LiveData<Event<String?>> = _openCitizenshipEvent
private val _shareReferralEvent = MutableLiveData<Event<String>>()
val shareReferralEvent: LiveData<Event<String>> = _shareReferralEvent
@@ -267,6 +268,10 @@ class BalanceListViewModel(
walletInteractor.nftSyncTrigger()
.onEach { trigger -> walletInteractor.syncChainNfts(selectedMetaAccount.first(), trigger.chain) }
.launchIn(viewModelScope)
PendingCitizenshipReferrer.referrerEvent
.onEach { referrer -> _openCitizenshipEvent.postValue(Event(referrer)) }
.launchIn(this)
}
fun fullSync() {
@@ -409,13 +414,14 @@ class BalanceListViewModel(
}
fun basvuruClicked() = launchUnit {
_openCitizenshipEvent.postValue(Event(Unit))
_openCitizenshipEvent.postValue(Event(null))
}
fun shareReferralClicked() = launchUnit {
val metaAccount = selectedAccountUseCase.getSelectedMetaAccount()
val address = metaAccount.defaultSubstrateAddress ?: return@launchUnit
val shareText = resourceManager.getString(R.string.citizenship_share_referral, address)
val deepLink = "pezkuwiwallet://pezkuwi/open/citizenship?referrer=$address"
val shareText = resourceManager.getString(R.string.citizenship_share_referral, deepLink, address)
_shareReferralEvent.postValue(Event(shareText))
}
@@ -3,6 +3,7 @@ package io.novafoundation.nova.feature_assets.presentation.citizenship
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
@@ -21,6 +22,16 @@ import io.novafoundation.nova.feature_assets.di.AssetsFeatureComponent
class CitizenshipBottomSheet : BaseBottomSheetFragment<CitizenshipViewModel, FragmentCitizenshipBottomSheetBinding>() {
companion object {
private const val KEY_REFERRER = "referrer"
fun newInstance(referrer: String? = null) = CitizenshipBottomSheet().apply {
arguments = Bundle().apply {
referrer?.let { putString(KEY_REFERRER, it) }
}
}
}
override fun createBinding() = FragmentCitizenshipBottomSheetBinding.inflate(layoutInflater)
override fun inject() {
@@ -35,6 +46,7 @@ class CitizenshipBottomSheet : BaseBottomSheetFragment<CitizenshipViewModel, Fra
override fun initViews() {
setupRegionSpinner()
prefillReferrer()
binder.citizenshipActionButton.setOnClickListener {
when (viewModel.citizenshipStatus.value) {
@@ -49,6 +61,13 @@ class CitizenshipBottomSheet : BaseBottomSheetFragment<CitizenshipViewModel, Fra
}
}
private fun prefillReferrer() {
val referrer = arguments?.getString(KEY_REFERRER)
if (!referrer.isNullOrBlank()) {
binder.citizenshipReferrerInput.setText(referrer)
}
}
override fun subscribe(viewModel: CitizenshipViewModel) {
viewModel.citizenshipStatus.observe(viewLifecycleOwner) { status ->
updateUiForStatus(status)
@@ -208,7 +208,8 @@ class CitizenshipViewModel(
val metaAccount = selectedAccountUseCase.getSelectedMetaAccount()
val address = metaAccount.addressIn(chain) ?: return@launch
val shareText = resourceManager.getString(R.string.citizenship_share_referral, address)
val deepLink = "pezkuwiwallet://pezkuwi/open/citizenship?referrer=$address"
val shareText = resourceManager.getString(R.string.citizenship_share_referral, deepLink, address)
_shareEvent.postValue(Event(shareText))
} catch (e: Exception) {
Log.e(TAG, "shareReferralLink failed", e)
@@ -0,0 +1,14 @@
package io.novafoundation.nova.feature_assets.presentation.citizenship
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
object PendingCitizenshipReferrer {
private val _referrerEvent = MutableSharedFlow<String>(extraBufferCapacity = 1)
val referrerEvent: Flow<String> = _referrerEvent
fun emit(referrer: String) {
_referrerEvent.tryEmit(referrer)
}
}
@@ -0,0 +1,35 @@
package io.novafoundation.nova.feature_assets.presentation.citizenship.deeplink
import android.net.Uri
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
import io.novafoundation.nova.common.utils.sequrity.awaitInteractionAllowed
import io.novafoundation.nova.feature_assets.presentation.citizenship.PendingCitizenshipReferrer
import io.novafoundation.nova.feature_deep_linking.presentation.handling.CallbackEvent
import io.novafoundation.nova.feature_deep_linking.presentation.handling.DeepLinkHandler
import kotlinx.coroutines.flow.MutableSharedFlow
class CitizenshipDeepLinkHandler(
private val automaticInteractionGate: AutomaticInteractionGate
) : DeepLinkHandler {
companion object {
private const val PATH_PREFIX = "/open/citizenship"
private const val REFERRER_PARAM = "referrer"
}
override val callbackFlow = MutableSharedFlow<CallbackEvent>()
override suspend fun matches(data: Uri): Boolean {
val path = data.path ?: return false
return path.startsWith(PATH_PREFIX)
}
override suspend fun handleDeepLink(data: Uri): Result<Unit> = runCatching {
automaticInteractionGate.awaitInteractionAllowed()
val referrer = data.getQueryParameter(REFERRER_PARAM)
?: throw IllegalArgumentException("Missing referrer parameter")
PendingCitizenshipReferrer.emit(referrer)
}
}