diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
index 6078133aea..6817e44fe9 100644
--- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
+++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt
@@ -140,6 +140,7 @@ internal class ChildFactory @Inject constructor(
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
AppRoute.ManageTokens.Source.ACCOUNT -> ManageTokensSource.ACCOUNT
+ AppRoute.ManageTokens.Source.TOKEN_SYNC_BANNER -> ManageTokensSource.TOKEN_SYNC_BANNER
}
val mode = route.accountId?.let { ManageTokensMode.Account(it) } ?: ManageTokensMode.None
diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
index 8b1bc37667..bf5e754ca3 100644
--- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
+++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt
@@ -139,6 +139,7 @@ sealed class AppRoute(val path: String) : Route {
STORIES,
SETTINGS,
ACCOUNT,
+ TOKEN_SYNC_BANNER,
}
}
diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
index c70f061ee1..fbaaff2c62 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
@@ -140,6 +140,8 @@ object PreferencesKeys {
val HAS_HAD_FIRST_TOP_UP_KEY by lazy { stringPreferencesKey(name = "hasHadFirstTopUp") }
+ val PENDING_DISCOVERY_SYNC_KEY by lazy { stringPreferencesKey(name = "pendingDiscoverySync") }
+
// region Notifications
val NOTIFICATIONS_APPLICATION_ID_KEY by lazy { stringPreferencesKey(name = "notificationsApplicationId") }
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 020150535e..9dd3c09886 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -692,6 +692,9 @@
Tap to scan
Tap to sign
Tap the card or ring
+ Your wallet is synced and ready.\nSome tokens missing?
+ Wallet successfully imported
+ Restoring %d%
You have updated biometrics, scan your card or ring to enter
Your balance should be higher than the fee value to make a transfer
Not enough balance
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/tokensync/DiscoveredToken.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/tokensync/DiscoveredToken.kt
new file mode 100644
index 0000000000..ddf1a0069b
--- /dev/null
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/tokensync/DiscoveredToken.kt
@@ -0,0 +1,14 @@
+package com.tangem.domain.tokens.model.tokensync
+
+import java.math.BigDecimal
+
+data class DiscoveredToken(
+ val contractAddress: String?,
+ val symbol: String,
+ val name: String,
+ val decimals: Int,
+ val amount: BigDecimal,
+ val isNativeToken: Boolean,
+ val currencyId: String?,
+ val networkId: String,
+)
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/tokensync/TokenSyncProgress.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/tokensync/TokenSyncProgress.kt
new file mode 100644
index 0000000000..9e94d71268
--- /dev/null
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/tokensync/TokenSyncProgress.kt
@@ -0,0 +1,22 @@
+package com.tangem.domain.tokens.model.tokensync
+
+sealed class TokenSyncProgress {
+
+ data object Idle : TokenSyncProgress()
+
+ data class InProgress(
+ val completedNetworks: Int,
+ val totalNetworks: Int,
+ ) : TokenSyncProgress() {
+ val progressPercent: Int
+ get() = if (totalNetworks > 0) {
+ completedNetworks * 100 / totalNetworks
+ } else {
+ 0
+ }
+ }
+
+ data object Completed : TokenSyncProgress()
+
+ data class Error(val cause: Throwable) : TokenSyncProgress()
+}
\ No newline at end of file
diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokenSyncRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokenSyncRepository.kt
new file mode 100644
index 0000000000..5919dd5414
--- /dev/null
+++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokenSyncRepository.kt
@@ -0,0 +1,23 @@
+package com.tangem.domain.tokens.repository
+
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.tokens.model.tokensync.TokenSyncProgress
+import kotlinx.coroutines.flow.Flow
+
+interface TokenSyncRepository {
+
+ suspend fun runSync(userWalletId: UserWalletId)
+
+ suspend fun getPendingSyncWalletIds(): List
+
+ fun observeSyncProgress(userWalletId: UserWalletId): Flow
+
+ fun acknowledgeCompletion(userWalletId: UserWalletId)
+
+ suspend fun clearPendingFlag(userWalletId: UserWalletId)
+
+ suspend fun getDiscoveredCurrencies(userWalletId: UserWalletId): List
+
+ suspend fun clearDiscoveredTokens(userWalletId: UserWalletId)
+}
\ No newline at end of file
diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ManageTokensSource.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ManageTokensSource.kt
index fcce6daea7..38034501c5 100644
--- a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ManageTokensSource.kt
+++ b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/component/ManageTokensSource.kt
@@ -9,6 +9,7 @@ enum class ManageTokensSource(val analyticsName: String) {
ONBOARDING(analyticsName = "Onboarding"),
SETTINGS(analyticsName = "Wallet Settings"),
ACCOUNT(analyticsName = "Account"),
+ TOKEN_SYNC_BANNER(analyticsName = "Token Sync Banner"),
SEND_VIA_SWAP(analyticsName = "SendViaSwap"),
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt
index 41fe676459..63e329b73f 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt
@@ -122,6 +122,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.Warning.TangemPayRefreshNeeded -> null
is WalletNotification.Warning.TangemPayUnreachable -> null
is WalletNotification.UpgradeHotWalletPromo -> null
+ is WalletNotification.TokenSyncCompleted -> null
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt
index a12cd21993..c18257cf76 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt
@@ -8,4 +8,5 @@ data class WalletAdditionalInfo(
val hideable: Boolean,
val content: TextReference,
val isHotBackedUp: Boolean = false,
+ val shouldShowProgress: Boolean = false,
)
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
index ed1601ae6a..e3110857f1 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
@@ -463,4 +463,21 @@ sealed class WalletNotification(val config: NotificationConfig) {
iconSize = 72.dp,
),
)
+
+ data class TokenSyncCompleted(
+ val onCloseClick: () -> Unit,
+ val onManageTokensClick: () -> Unit,
+ ) : WalletNotification(
+ config = NotificationConfig(
+ title = resourceReference(R.string.initial_wallet_sync_banner_title),
+ subtitle = resourceReference(R.string.initial_wallet_sync_banner_description),
+ iconResId = R.drawable.ic_check_circle_24,
+ iconTint = IconTint.Accent,
+ onCloseClick = onCloseClick,
+ buttonsState = ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(R.string.main_manage_tokens),
+ onClick = onManageTokensClick,
+ ),
+ ),
+ )
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenSyncProgressTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenSyncProgressTransformer.kt
new file mode 100644
index 0000000000..283537ff7a
--- /dev/null
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenSyncProgressTransformer.kt
@@ -0,0 +1,52 @@
+package com.tangem.feature.wallet.presentation.wallet.state.transformers
+
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.wrappedList
+import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.feature.wallet.impl.R
+import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
+import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
+import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
+import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
+
+internal class SetTokenSyncProgressTransformer(
+ userWalletId: UserWalletId,
+ private val progressPercent: Int,
+) : WalletStateTransformer(userWalletId) {
+
+ override fun transform(prevState: WalletState): WalletState {
+ return when (prevState) {
+ is WalletState.MultiCurrency.Content -> {
+ val updatedCardState = updateCardState(prevState.walletCardState)
+ prevState.copy(walletCardState = updatedCardState)
+ }
+ else -> {
+ prevState
+ }
+ }
+ }
+
+ override fun transform(walletUM: WalletUM): WalletUM {
+ return walletUM
+ }
+
+ private fun updateCardState(cardState: WalletCardState): WalletCardState {
+ val additionalInfo = WalletAdditionalInfo(
+ hideable = false,
+ content = resourceReference(
+ id = R.string.initial_wallet_sync_restore_progress,
+ formatArgs = wrappedList(progressPercent),
+ ),
+ shouldShowProgress = true,
+ )
+ return when (cardState) {
+ is WalletCardState.Loading -> {
+ cardState.copy(additionalInfo = additionalInfo)
+ }
+ is WalletCardState.Content -> {
+ cardState.copy(additionalInfo = additionalInfo)
+ }
+ else -> cardState
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt
index 651707a1c3..26547001a4 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt
@@ -12,6 +12,7 @@ import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.TextAutoSize
import androidx.compose.material3.*
@@ -164,6 +165,7 @@ private fun CardContainer(state: WalletCardState, isBalanceHidden: Boolean, item
}
AdditionalInfo(
text = additionalText,
+ showProgress = state.additionalInfo?.shouldShowProgress == true,
modifier = Modifier.conditional(
state.imageResId == null,
) { fillMaxWidth() },
@@ -298,7 +300,7 @@ private fun Modifier.nonContentBalanceSize(dimens: TangemDimens): Modifier {
}
@Composable
-private fun AdditionalInfo(text: TextReference?, modifier: Modifier = Modifier) {
+private fun AdditionalInfo(text: TextReference?, showProgress: Boolean, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = text,
label = "Update the additional text",
@@ -309,7 +311,19 @@ private fun AdditionalInfo(text: TextReference?, modifier: Modifier = Modifier)
},
) { animatedText ->
if (animatedText != null) {
- AdditionalInfoText(text = animatedText)
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
+ ) {
+ AdditionalInfoText(text = animatedText)
+ if (showProgress) {
+ CircularProgressIndicator(
+ modifier = Modifier
+ .size(TangemTheme.dimens.size16),
+ color = TangemTheme.colors.icon.accent,
+ strokeWidth = TangemTheme.dimens.size2,
+ )
+ }
+ }
} else {
RectangleShimmer(modifier = Modifier.nonContentAdditionalInfoSize(dimens = TangemTheme.dimens))
}