From a27277243f26800eb14f52e0b591d476aa5d3d80 Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Thu, 2 Feb 2023 13:18:59 +0100 Subject: [PATCH 001/100] Fix: typos Fix: typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9ad5531658..3740a3ba6e 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ Track features development: [board link](https://soramitsucoltd.aha.io/shared/34 ## How to build -To build Fearless Wallet Android project, you need to provide several keys either in enviroment variables or in `local.properties` file: +To build Fearless Wallet Android project, you need to provide several keys either in environment variables or in `local.properties` file: ``` properties MOONPAY_TEST_SECRET=stub MOONPAY_PRODUCTION_SECRET=stub ``` -Note, that with stub keys buy via moonpay will not work correctly. However, other parts of application will not be affected. +Note, that with stub keys buy via moonpay will not work correctly. However, other parts of the application will not be affected. ## License Fearless Wallet Android is available under the Apache 2.0 license. See the LICENSE file for more info. From 829d3289ae3ac7c32a5de63b147d0f178eeaeb2a Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Thu, 2 Feb 2023 13:21:15 +0100 Subject: [PATCH 002/100] Fix: typos Fix: typos --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 299c51efa0..9ee7b7b254 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ In case you have found any issues with an existing app, feel free to report it d * Was your app affected eventually or right after installing the update? * Do you use the Google Play version of application, APK file from releases or running it via Android Studio (if other, mention what)? If you installed it manually, please provide backtrace if possible. * Provide steps to reproduce your bug -* If helpful, provide screenshots (or videos) of every step (addresses and balances can be blured on your choice) +* If helpful, provide screenshots (or videos) of every step (addresses and balances can be blurred on your choice) * (OPTIONAL) Provide your telegram username, and be sure to join [Our Community Channel](https://t.me/fearlesshappiness) on Telegram, so our admins can ping you here in case they need some assistance from your side Notice, that if you don't leave your Telegram username, we might ask directly in your created issue for more information if such is required. @@ -34,7 +34,7 @@ Even if you provide mockups, please understand that our design team will review If you would like to help us by contributing writing the code, please follow next steps: * Always create and issue prior to opening Pull Request (hereinafter the PR), or your PR less likely to be reviewed -* Remember, that in that case you are required to provide full description for you feature in created issue, otherwise it would be hard for us to understand what you're trying to add to our codebase +* Remember, that in that case you are required to provide full description of your feature in created issue, otherwise it would be hard for us to understand what you're trying to add to our codebase * Follow the coding standards guidelines (TO BE PROVIDED LATER), or you will be asked to make changes to follow them * Please avoid huge PRs, and if your contribution really requires lots of files, please make a base branch with series of small PRs on your fork, and then provide link to those PRs in your big one PR in our repository * Provide steps for QA engineer to test your functionality (they should cover requirements from your issue) From c443c4040260a8771de77d17c06b4cc396d8c0d6 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 28 Mar 2024 15:48:11 +0500 Subject: [PATCH 003/100] add button --- .../balance/list/BalanceListViewModel.kt | 4 ++ .../presentation/balance/list/WalletScreen.kt | 47 +++++++++++++------ .../impl/presentation/common/AssetsList.kt | 6 ++- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index c110bbf7ee..a69d0de116 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -562,6 +562,10 @@ class BalanceListViewModel @Inject constructor( } } + override fun onManageAssetClick() { + println("!!! CLICKED!!!") + } + private fun refresh() { sync() } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt index 66a524b3b3..268619f117 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight @@ -20,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import jp.co.soramitsu.common.compose.component.ActionItemType @@ -28,6 +30,7 @@ import jp.co.soramitsu.common.compose.component.AssetBalanceViewState import jp.co.soramitsu.common.compose.component.BannerBackup import jp.co.soramitsu.common.compose.component.BannerBuyXor import jp.co.soramitsu.common.compose.component.ChangeBalanceViewState +import jp.co.soramitsu.common.compose.component.GrayButton import jp.co.soramitsu.common.compose.component.MarginVertical import jp.co.soramitsu.common.compose.component.MultiToggleButton import jp.co.soramitsu.common.compose.component.MultiToggleButtonState @@ -38,11 +41,12 @@ import jp.co.soramitsu.common.compose.theme.white16 import jp.co.soramitsu.common.compose.theme.white50 import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState import jp.co.soramitsu.common.utils.rememberForeverLazyListState -import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.NFTScreen +import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.soracard.impl.presentation.SoraCardItem import jp.co.soramitsu.soracard.impl.presentation.SoraCardItemViewState import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.AssetType +import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.NFTScreen import jp.co.soramitsu.wallet.impl.presentation.common.AssetsList import jp.co.soramitsu.wallet.impl.presentation.common.AssetsListInterface @@ -56,6 +60,7 @@ interface WalletScreenInterface : AssetsListInterface { fun onBackupCloseClick() fun assetTypeChanged(type: AssetType) fun onRefresh() + fun onManageAssetClick() } @Composable @@ -92,12 +97,14 @@ fun WalletScreen( NFTScreen(collectionsScreen = data.assetsState.collectionScreenModel) } is WalletAssetsState.Assets -> { - val header = Banners(data, callback) + val header: @Composable () -> Unit = { Banners(data, callback) } + val footer: @Composable () -> Unit = { WalletScreenFooter(callback::onManageAssetClick) } AssetsList( data = data.assetsState, callback = callback, header = header, - listState = listState + listState = listState, + footer = footer ) } } @@ -106,7 +113,7 @@ fun WalletScreen( @OptIn(ExperimentalFoundationApi::class) @Composable -private fun Banners(data: WalletState, callback: WalletScreenInterface): @Composable (() -> Unit)? { +private fun Banners(data: WalletState, callback: WalletScreenInterface) { val soraCardBanner: @Composable (() -> Unit)? = if (data.soraCardState?.visible == true) { { @@ -159,14 +166,10 @@ private fun Banners(data: WalletState, callback: WalletScreenInterface): @Compos } } } - return if (soraCardBanner == null && bannersCarousel == null) { - null - } else { - { - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - soraCardBanner?.invoke() - bannersCarousel?.invoke() - } + if (soraCardBanner != null || bannersCarousel != null) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + soraCardBanner?.invoke() + bannersCarousel?.invoke() } } } @@ -208,6 +211,19 @@ fun WalletScreenWithRefresh( } } +@Composable +fun WalletScreenFooter( + onManageAssetsClick: () -> Unit +) { + GrayButton( + text = stringResource(id = R.string.wallet_manage_assets), + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + onClick = onManageAssetsClick + ) +} + @Preview @Composable private fun PreviewWalletScreen() { @@ -221,7 +237,7 @@ private fun PreviewWalletScreen() { override fun onBackupClicked() {} override fun onBackupCloseClick() {} override fun assetTypeChanged(type: AssetType) {} - override fun assetClicked(asset: AssetListItemViewState) {} + override fun assetClicked(state: AssetListItemViewState) {} override fun actionItemClicked( actionType: ActionItemType, @@ -232,6 +248,7 @@ private fun PreviewWalletScreen() { } override fun onRefresh() {} + override fun onManageAssetClick() {} } val element = AssetListItemViewState( @@ -252,7 +269,7 @@ private fun PreviewWalletScreen() { isTestnet = false ) val assets: List = listOf( - element, element, element + element, element, element.copy(isHidden = true) ).mapIndexed { index, assetListItemViewState -> assetListItemViewState.copy(index = index) } @@ -265,7 +282,7 @@ private fun PreviewWalletScreen() { AssetType.Currencies, listOf(AssetType.Currencies, AssetType.NFTs) ), - assetsState = WalletAssetsState.Assets(emptyList()), + assetsState = WalletAssetsState.Assets(assets), balance = AssetBalanceViewState( "TRANSFERABLE BALANCE", "ADDRESS", diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt index 51231b748f..825e15c68a 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt @@ -32,7 +32,8 @@ fun AssetsList( data: AssetListState, callback: AssetsListInterface, listState: LazyListState = rememberLazyListState(), - header: (@Composable () -> Unit)? = null + header: (@Composable () -> Unit)? = null, + footer: (@Composable () -> Unit)? = null ) { val isShowHidden = remember { mutableStateOf(data.visibleAssets.isEmpty()) } val onHiddenClick = remember { { isShowHidden.value = isShowHidden.value.not() } } @@ -69,6 +70,9 @@ fun AssetsList( } } } + if (footer != null) { + item { footer() } + } item { MarginVertical(margin = 80.dp) } } } From f43a668ca841010ce108e2a22fd9968230a1c078 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 28 Mar 2024 21:25:23 +0500 Subject: [PATCH 004/100] remove hide zero balances --- common/src/main/res/drawable/ic_eye.xml | 14 -------- common/src/main/res/values-in/strings.xml | 1 - common/src/main/res/values-ja/strings.xml | 1 - common/src/main/res/values-pt/strings.xml | 1 - common/src/main/res/values-ru/strings.xml | 1 - common/src/main/res/values-tr/strings.xml | 1 - common/src/main/res/values-vi/strings.xml | 1 - common/src/main/res/values-zh/strings.xml | 1 - common/src/main/res/values/strings.xml | 1 - .../coredb/migrations/V2MigrationTest.kt | 4 ++- .../presentation/profile/ProfileFragment.kt | 5 --- .../presentation/profile/ProfileViewModel.kt | 8 ----- .../src/main/res/layout/fragment_profile.xml | 36 ------------------- .../domain/interfaces/WalletInteractor.kt | 4 --- .../impl/domain/WalletInteractorImpl.kt | 24 ------------- .../impl/presentation/AssetListHelper.kt | 20 ++--------- .../balance/list/BalanceListViewModel.kt | 9 ++--- .../searchAssets/SearchAssetsViewModel.kt | 8 ++--- 18 files changed, 11 insertions(+), 129 deletions(-) delete mode 100644 common/src/main/res/drawable/ic_eye.xml diff --git a/common/src/main/res/drawable/ic_eye.xml b/common/src/main/res/drawable/ic_eye.xml deleted file mode 100644 index 68385e5f5d..0000000000 --- a/common/src/main/res/drawable/ic_eye.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/common/src/main/res/values-in/strings.xml b/common/src/main/res/values-in/strings.xml index 0b3f72e74c..c05bf3e97e 100644 --- a/common/src/main/res/values-in/strings.xml +++ b/common/src/main/res/values-in/strings.xml @@ -742,7 +742,6 @@ Kirim ke Mengirim… Atur jumlah maximal - Sembunyikan saldo Nol Bagikan kode rujukan Sosial media Jumlah yang Anda coba transfer tidak cukup untuk menutupi biaya transaksi di %s jaringan. Meskipun transaksi tidak dapat diproses, Anda tetap akan dikenakan biaya di jaringan Sora. diff --git a/common/src/main/res/values-ja/strings.xml b/common/src/main/res/values-ja/strings.xml index 5359d59f7a..e6779ed622 100644 --- a/common/src/main/res/values-ja/strings.xml +++ b/common/src/main/res/values-ja/strings.xml @@ -725,7 +725,6 @@ 資金を送る 送信先 送信中 - 残高ゼロを隠す 紹介コードを共有 ソーシャル・メディア 現在、SORAネットワークの安定性と安全性を確保するため、ブリッジングには最低%sが設定されています。ご理解のほどよろしくお願いいたします。 diff --git a/common/src/main/res/values-pt/strings.xml b/common/src/main/res/values-pt/strings.xml index 91e73058ea..00d7a24176 100644 --- a/common/src/main/res/values-pt/strings.xml +++ b/common/src/main/res/values-pt/strings.xml @@ -723,7 +723,6 @@ Enviar fundos Enviar para Enviando... - Ocultar saldos nulos Partilhar código de referência Mídia social €0 de taxa de serviço anual diff --git a/common/src/main/res/values-ru/strings.xml b/common/src/main/res/values-ru/strings.xml index 38cb412fd5..a4580dacb9 100644 --- a/common/src/main/res/values-ru/strings.xml +++ b/common/src/main/res/values-ru/strings.xml @@ -608,7 +608,6 @@ DISCLAIMER: Предложения алгоритмических валидаторов не являются финансовыми консультациями или советами. Стейкинг - это деятельность с высоким риском, и предложения алгоритмических валидаторов не обязательно снижают этот риск. Валидатор, предложенный алгоритмом, все равно может быть слэширован. Валидатор, предложенный алгоритмом, также может изменить свои параметры (например, размер комиссии и т.д.) в любое время после того, как он был предложен и/или выбран. Вы можете потерять токены или вознаграждения по этим или другим причинам. Ставьте токены и используйте предложения валидаторов только по своему усмотрению, после проведения должной проверки и тщательного рассмотрения сопутствующих рисков. Отправить Средства Отправка - Скрыть нулевые балансы Поделиться реферальным кодом Социальные сети/медиа 0 € годовой сервисный сбор diff --git a/common/src/main/res/values-tr/strings.xml b/common/src/main/res/values-tr/strings.xml index 06ea2dcb56..8b420e8424 100644 --- a/common/src/main/res/values-tr/strings.xml +++ b/common/src/main/res/values-tr/strings.xml @@ -743,7 +743,6 @@ Gönderildi Gönderiliyor… Maksimum tutarı ayarla - Sıfır bakiyeleri gizleyin Referans kodunuzu paylaşın Sosyal medya Aktarmaya çalıştığınız tutar, işlem ücretlerini karşılamaya yetmiyor. %sAğ İşlem gerçekleşmese de Sora ağındaki ücretler sizden yine de tahsil edilecektir. diff --git a/common/src/main/res/values-vi/strings.xml b/common/src/main/res/values-vi/strings.xml index 393f6483ed..485556dfc8 100644 --- a/common/src/main/res/values-vi/strings.xml +++ b/common/src/main/res/values-vi/strings.xml @@ -742,7 +742,6 @@ Gửi đến Đang gửi Số tiền tối đa - Ẩn số dư bằng không Chia sẻ mã giới thiệu Truyền thông xã hội Số tiền bạn đang cố chuyển không đủ để trả phí giao dịch trên mạng %s . Mặc dù giao dịch không được xử lý nhưng bạn vẫn sẽ bị tính phí trên mạng Sora. diff --git a/common/src/main/res/values-zh/strings.xml b/common/src/main/res/values-zh/strings.xml index 816418cdee..bb3e547c32 100644 --- a/common/src/main/res/values-zh/strings.xml +++ b/common/src/main/res/values-zh/strings.xml @@ -738,7 +738,6 @@ 发送至 发送 设置最大金额 - 隐藏零余额 分享推荐码 社交媒体 您尝试转账的金额不足以支付%s网络上的交易费用。尽管交易无法完成,您仍将在Sora网络上被收取费用。 diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index f741f5c0cd..5780a4ab50 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -747,7 +747,6 @@ Send to Sending Set max amount - Hide zero balances Share referral code Social Media The amount you\'re trying to transfer is insufficient to cover the transaction fees on the %s network. Although the transaction won\'t process, you\'ll still be charged the fees on the Sora network. diff --git a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt index 844329bcca..c99ccc8f1f 100644 --- a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt +++ b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt @@ -238,7 +238,9 @@ class V2MigrationTest { ethereumAddress = getBlob(getColumnIndex(Column.ETHEREUM_ADDRESS)), name = getString(getColumnIndex(Column.NAME)), isSelected = getInt(getColumnIndex(Column.IS_SELECTED)) == 1, - position = getInt(getColumnIndex(Column.POSITION)) + position = getInt(getColumnIndex(Column.POSITION)), + isBackedUp = false, + googleBackupAddress = null ) metaAccount.id = getLong(getColumnIndex(Column.ID)) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt index 3bbb84c5d1..3368ffd84a 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt @@ -50,7 +50,6 @@ class ProfileFragment : BaseFragment() { profileExperimentalFeatures.setOnClickListener { viewModel.onExperimentalClicked() } polkaswapDisclaimerTv.setOnClickListener { viewModel.polkaswapDisclaimerClicked() } profileSoraCard.setOnClickListener { viewModel.onSoraCardClicked() } - hideZeroBalancesContainer.setOnClickListener { viewModel.onHideZeroBalancesClick() } profileWalletConnect.setOnClickListener { viewModel.onWalletConnectClick() } viewModel.hasMissingAccountsFlow.observe { @@ -83,10 +82,6 @@ class ProfileFragment : BaseFragment() { viewModel.showFiatChooser.observeEvent(::showFiatChooser) viewModel.selectedFiatLiveData.observe(binding.selectedCurrencyTv::setText) - - viewModel.hideZeroBalancesState.observe { - binding.hideZeroBalancesSwitch.isChecked = it - } } private fun showFiatChooser(payload: DynamicListBottomSheet.Payload) { diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt index 3504b97953..d89fdbaad5 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt @@ -86,8 +86,6 @@ class ProfileViewModel @Inject constructor( // } private val soraCardState = flowOf(SoraCardItemViewState()) - val hideZeroBalancesState: Flow = walletInteractor.observeHideZeroBalanceEnabledForCurrentWallet() - fun aboutClicked() { router.openAboutScreen() } @@ -155,12 +153,6 @@ class ProfileViewModel @Inject constructor( private fun onSoraCardStatusClicked() { } - fun onHideZeroBalancesClick() { - viewModelScope.launch { - walletInteractor.toggleHideZeroBalancesForCurrentWallet() - } - } - fun onWalletConnectClick() { router.openConnectionsScreen() } diff --git a/feature-account-impl/src/main/res/layout/fragment_profile.xml b/feature-account-impl/src/main/res/layout/fragment_profile.xml index 32fa7f1d86..7a5a1befd1 100644 --- a/feature-account-impl/src/main/res/layout/fragment_profile.xml +++ b/feature-account-impl/src/main/res/layout/fragment_profile.xml @@ -255,42 +255,6 @@ - - - - - - - - - - - suspend fun toggleHideZeroBalancesForCurrentWallet() - suspend fun getHideZeroBalancesForCurrentWallet(): Boolean - suspend fun checkControllerDeprecations(): List suspend fun canUseAsset(chainId: String, chainAssetId: String): Boolean diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index f8124db27f..2c88fd0e0f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -96,30 +96,6 @@ class WalletInteractorImpl( private val chainsRepository: ChainsRepository ) : WalletInteractor, UpdatesProviderUi by updatesMixin { - override suspend fun getHideZeroBalancesForCurrentWallet(): Boolean { - val walletId = accountRepository.getSelectedMetaAccount().id - val key = getHideZeroBalancesKey(walletId) - return preferences.getBoolean(key, false) - } - - override suspend fun toggleHideZeroBalancesForCurrentWallet() { - val walletId = accountRepository.getSelectedMetaAccount().id - val key = getHideZeroBalancesKey(walletId) - val value = preferences.getBoolean(key, false) - val newValue = value.not() - preferences.putBoolean(key, newValue) - } - - override fun observeHideZeroBalanceEnabledForCurrentWallet(): Flow { - return accountRepository.selectedLightMetaAccountFlow().flatMapMerge { wallet -> - preferences.booleanFlow(getHideZeroBalancesKey(wallet.id), false) - }.distinctUntilChanged() - } - - private fun getHideZeroBalancesKey(walletId: Long): String { - return "${HIDE_ZERO_BALANCES_PREFS_KEY}_$walletId" - } - @OptIn(ExperimentalCoroutinesApi::class) override fun assetsFlow(): Flow> { return accountRepository.selectedMetaAccountFlow() diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/AssetListHelper.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/AssetListHelper.kt index 17de8922a4..2a6ecf7af5 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/AssetListHelper.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/AssetListHelper.kt @@ -1,10 +1,7 @@ package jp.co.soramitsu.wallet.impl.presentation -import android.util.Log import java.math.BigDecimal import jp.co.soramitsu.common.compose.component.NetworkIssueItemState -import jp.co.soramitsu.common.utils.formatCrypto -import jp.co.soramitsu.common.utils.isZero import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.common.utils.sumByBigDecimal import jp.co.soramitsu.core.models.ChainId @@ -19,8 +16,7 @@ object AssetListHelper { assets: List, filteredChains: List, selectedChainId: ChainId? = null, - networkIssues: Set, - hideZeroBalancesEnabled: Boolean + networkIssues: Set ): List { val result = mutableListOf() assets.groupBy { it.asset.token.configuration.symbol } @@ -63,19 +59,7 @@ object AssetListHelper { val assetTotal = symbolAssets.sumByBigDecimal { it.asset.total.orZero() } val assetTotalFiat = symbolAssets.sumByBigDecimal { it.asset.fiatAmount.orZero() } - val assetVisibleTotal = try { - assetTotal.formatCrypto().replace(',', '.').toBigDecimal() - } catch (e: NumberFormatException) { - Log.e("AssetListHelper", "assetVisibleTotal calculation failure", e) - assetTotal - } - val isZeroBalance = assetVisibleTotal.isZero() - val assetDisabledByUser = symbolAssets.any { it.asset.enabled == false } - val assetManagedByUser = symbolAssets.any { it.asset.enabled != null } - - val isHidden = - assetDisabledByUser || (!assetManagedByUser && isZeroBalance && hideZeroBalancesEnabled) val token = symbolAssets.first().asset.token @@ -87,7 +71,7 @@ object AssetListHelper { fiatAmount = assetTotalFiat, transferable = assetTransferable, chainUrls = assetChainUrls, - isHidden = isHidden + isHidden = assetDisabledByUser ) result.add(model) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index a69d0de116..096941a9ec 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -199,15 +199,13 @@ class BalanceListViewModel @Inject constructor( selectedChainId, interactor.selectedMetaAccountFlow(), networkIssuesFlow, - interactor.observeSelectedAccountChainSelectFilter(), - interactor.observeHideZeroBalanceEnabledForCurrentWallet() + interactor.observeSelectedAccountChainSelectFilter() ) { (walletId: Long, assets: List), chains: List, selectedChainId: ChainId?, currentMetaAccountFlow: MetaAccount, networkIssues: Set, - appliedFilterAsString: String, - hideZeroBalancesEnabled: Boolean -> + appliedFilterAsString: String -> val filter = ChainSelectorViewStateWithFilters.Filter.entries.find { it.name == appliedFilterAsString @@ -251,8 +249,7 @@ class BalanceListViewModel @Inject constructor( assets = filteredAssets, filteredChains = filteredChains, selectedChainId = selectedChainId, - networkIssues = networkIssues, - hideZeroBalancesEnabled = hideZeroBalancesEnabled + networkIssues = networkIssues ) val assetStates: List = balanceListItems diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt index 1f608ba175..692ea45ed2 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt @@ -54,15 +54,13 @@ class SearchAssetsViewModel @Inject constructor( private val assetStates = combine( interactor.assetsFlow(), chainInteractor.getChainsFlow(), - networkIssuesFlow, - interactor.observeHideZeroBalanceEnabledForCurrentWallet() - ) { assets: List, chains: List, networkIssues: Set, hideZeroBalancesEnabled -> + networkIssuesFlow + ) { assets: List, chains: List, networkIssues: Set -> val balanceListItems = AssetListHelper.processAssets( assets = assets, filteredChains = chains, - networkIssues = networkIssues, - hideZeroBalancesEnabled = hideZeroBalancesEnabled + networkIssues = networkIssues ) val assetStates: List = balanceListItems From 20a9c74cdf49cac082534a2c5a5040297e5bb3be Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 4 Apr 2024 14:15:08 +0500 Subject: [PATCH 005/100] FLW-4463 Asset management screen --- .../app/root/navigation/Navigator.kt | 4 + .../main/res/navigation/main_nav_graph.xml | 5 + .../common/compose/component/Text.kt | 6 +- .../common/model/AssetBooleanState.kt | 9 + common/src/main/res/drawable/ic_edit_20.xml | 24 + common/src/main/res/values/strings.xml | 5 + .../domain/interfaces/WalletInteractor.kt | 3 + .../domain/interfaces/WalletRepository.kt | 2 +- .../data/repository/WalletRepositoryImpl.kt | 4 + .../impl/domain/WalletInteractorImpl.kt | 23 + .../wallet/impl/presentation/WalletRouter.kt | 2 + .../balance/list/BalanceListViewModel.kt | 18 +- .../presentation/common/AssetListState.kt | 7 +- .../impl/presentation/common/AssetsList.kt | 53 +-- .../manageassets/ManageAssetsContent.kt | 421 ++++++++++++++++++ .../manageassets/ManageAssetsFragment.kt | 40 ++ .../manageassets/ManageAssetsViewModel.kt | 192 ++++++++ 17 files changed, 774 insertions(+), 44 deletions(-) create mode 100644 common/src/main/java/jp/co/soramitsu/common/model/AssetBooleanState.kt create mode 100644 common/src/main/res/drawable/ic_edit_20.xml create mode 100644 feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt create mode 100644 feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt create mode 100644 feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt diff --git a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt index 58898cbf63..900f75cd3d 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt @@ -1526,6 +1526,10 @@ class Navigator : navController?.navigate(R.id.nftFiltersFragment) } + override fun openManageAssets() { + navController?.navigate(R.id.manageAssetsFragment) + } + override fun openServiceScreen() { navController?.navigate(R.id.serviceFragment) } diff --git a/app/src/main/res/navigation/main_nav_graph.xml b/app/src/main/res/navigation/main_nav_graph.xml index 904ee0cb70..699ee2d05f 100644 --- a/app/src/main/res/navigation/main_nav_graph.xml +++ b/app/src/main/res/navigation/main_nav_graph.xml @@ -813,6 +813,11 @@ android:name="jp.co.soramitsu.nft.impl.presentation.NFTFlowFragment" android:label="nftFlowFragment" /> + + Unit = {} + onTextLayout: (TextLayoutResult) -> Unit = {}, + fontWeight: FontWeight = FontWeight.Normal ) { Text( textAlign = textAlign, text = text, - style = MaterialTheme.customTypography.body1, + style = MaterialTheme.customTypography.body1.copy(fontWeight = fontWeight), modifier = modifier, color = color, overflow = overflow, diff --git a/common/src/main/java/jp/co/soramitsu/common/model/AssetBooleanState.kt b/common/src/main/java/jp/co/soramitsu/common/model/AssetBooleanState.kt new file mode 100644 index 0000000000..c726ef598e --- /dev/null +++ b/common/src/main/java/jp/co/soramitsu/common/model/AssetBooleanState.kt @@ -0,0 +1,9 @@ +package jp.co.soramitsu.common.model + +import jp.co.soramitsu.core.models.ChainId + +data class AssetBooleanState( + val chainId: ChainId, + val assetId: String, + val value: Boolean +) \ No newline at end of file diff --git a/common/src/main/res/drawable/ic_edit_20.xml b/common/src/main/res/drawable/ic_edit_20.xml new file mode 100644 index 0000000000..d9d1a3e5d6 --- /dev/null +++ b/common/src/main/res/drawable/ic_edit_20.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 5780a4ab50..c32e007cd8 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -244,6 +244,10 @@ Module My networks Network + + %d network + %d networks + Network fee Network management Next @@ -1078,6 +1082,7 @@ Vesting Locked View in %s View wallet + You have hidden all assets Buy %s with Receive Receive %s diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt index 699a019bb8..2dfb1f5739 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt @@ -9,6 +9,7 @@ import jp.co.soramitsu.common.data.model.CursorPage import jp.co.soramitsu.common.data.network.runtime.binding.EqAccountInfo import jp.co.soramitsu.common.data.network.runtime.binding.EqOraclePricePoint import jp.co.soramitsu.common.data.secrets.v2.MetaAccountSecrets +import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.core.models.ChainId import jp.co.soramitsu.coredb.model.AddressBookContact import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain @@ -105,6 +106,8 @@ interface WalletInteractor { suspend fun markAssetAsHidden(chainId: ChainId, chainAssetId: String) + suspend fun updateAssetsHiddenState(state: List) + suspend fun markAssetAsShown(chainId: ChainId, chainAssetId: String) fun selectedMetaAccountFlow(): Flow diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt index 2ac3998bbf..31580b1e7a 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt @@ -7,7 +7,6 @@ import jp.co.soramitsu.common.data.network.config.AppConfigRemote import jp.co.soramitsu.common.data.network.runtime.binding.EqAccountInfo import jp.co.soramitsu.common.data.network.runtime.binding.EqOraclePricePoint import jp.co.soramitsu.core.models.IChain -import jp.co.soramitsu.coredb.model.AssetLocal import jp.co.soramitsu.coredb.model.AssetUpdateItem import jp.co.soramitsu.coredb.model.PhishingLocal import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain @@ -115,4 +114,5 @@ interface WalletRepository { suspend fun getVestingLockedAmount(chainId: ChainId): BigInteger? suspend fun estimateClaimRewardsFee(chainId: ChainId): BigInteger suspend fun claimRewards(chain: IChain, accountId: AccountId): Result + suspend fun updateAssetsHidden(state: List) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index 4696a97ba7..eec18664f7 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -350,6 +350,10 @@ class WalletRepositoryImpl( assetCache.updateAsset(updateItems) } + override suspend fun updateAssetsHidden(state: List) { + assetCache.updateAsset(state) + } + override suspend fun observeTransferFee( chain: Chain, transfer: Transfer, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index 2c88fd0e0f..5805eece98 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -20,6 +20,7 @@ import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.interfaces.FileProvider import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi +import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.common.utils.Modules import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.common.utils.orZero @@ -27,6 +28,7 @@ import jp.co.soramitsu.common.utils.requireValue import jp.co.soramitsu.core.models.Asset.StakingType import jp.co.soramitsu.core.models.ChainId import jp.co.soramitsu.core.utils.isValidAddress +import jp.co.soramitsu.coredb.model.AssetUpdateItem import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain @@ -422,6 +424,27 @@ class WalletInteractorImpl( manageAssetHidden(chainId, chainAssetId, true) } + override suspend fun updateAssetsHiddenState(state: List) { + val wallet = getSelectedMetaAccount() + val updateItems = state.mapNotNull { + val chain = getChain(it.chainId) + val asset = chain.assetsById[it.assetId] + val tokenPriceId = asset?.priceProvider?.id?.takeIf { selectedFiat.isUsd() } ?: asset?.priceId + wallet.accountId(chain)?.let { accountId -> + AssetUpdateItem( + metaId = wallet.id, + chainId = it.chainId, + accountId = accountId, + id = it.assetId, + sortIndex = Int.MAX_VALUE, // Int.MAX_VALUE on sorting because we don't use it anymore - just random value + enabled = it.value, + tokenPriceId = tokenPriceId + ) + } + } + walletRepository.updateAssetsHidden(updateItems) + } + override suspend fun markAssetAsShown(chainId: ChainId, chainAssetId: String) { manageAssetHidden(chainId, chainAssetId, false) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt index 0ee1642bf3..b417e1d051 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt @@ -195,5 +195,7 @@ interface WalletRouter : SecureRouter, WalletRouterApi { fun openNFTFilter() + fun openManageAssets() + fun openServiceScreen() } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 096941a9ec..8adbc4c450 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -10,6 +10,7 @@ import co.jp.soramitsu.walletconnect.domain.WalletConnectInteractor import com.walletconnect.android.internal.common.exception.MalformedWalletConnectUri import dagger.hilt.android.lifecycle.HiltViewModel import java.math.BigDecimal +import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario import jp.co.soramitsu.account.api.domain.interfaces.AccountInteractor @@ -31,6 +32,7 @@ import jp.co.soramitsu.common.compose.component.SwipeState import jp.co.soramitsu.common.compose.component.ToolbarHomeIconState import jp.co.soramitsu.common.compose.models.LoadableListPage import jp.co.soramitsu.common.compose.models.ScreenLayout +import jp.co.soramitsu.common.compose.utils.PageScrollingCallback import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState import jp.co.soramitsu.common.data.network.coingecko.FiatChooserEvent import jp.co.soramitsu.common.data.network.coingecko.FiatCurrency @@ -56,10 +58,7 @@ import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.nft.data.pagination.PaginationRequest import jp.co.soramitsu.nft.domain.NFTInteractor -import jp.co.soramitsu.common.compose.utils.PageScrollingCallback import jp.co.soramitsu.nft.domain.models.NFTCollection -import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenModel -import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenView import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.defaultChainSort @@ -82,6 +81,8 @@ import jp.co.soramitsu.wallet.impl.presentation.balance.chainselector.toChainIte import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.AssetType import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.BalanceListItemModel import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.toAssetState +import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenModel +import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenView import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.ScreenModel import jp.co.soramitsu.wallet.impl.presentation.model.ControllerDeprecationWarningModel import jp.co.soramitsu.wallet.impl.presentation.model.toModel @@ -99,7 +100,6 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn @@ -112,7 +112,6 @@ import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.concurrent.atomic.AtomicBoolean private const val CURRENT_ICON_SIZE = 40 @@ -240,9 +239,10 @@ class BalanceListViewModel @Inject constructor( val filteredAssets = assets .filter { - selectedChainId == it.asset.token.configuration.chainId || - selectedChainId == null || - it.asset.token.configuration.chainId in filteredChains.map { it.id } + it.asset.enabled != false && + (selectedChainId == it.asset.token.configuration.chainId || + selectedChainId == null || + it.asset.token.configuration.chainId in filteredChains.map { it.id }) } val balanceListItems = AssetListHelper.processAssets( @@ -560,7 +560,7 @@ class BalanceListViewModel @Inject constructor( } override fun onManageAssetClick() { - println("!!! CLICKED!!!") + router.openManageAssets() } private fun refresh() { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetListState.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetListState.kt index 547f67081b..3062599898 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetListState.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetListState.kt @@ -4,9 +4,4 @@ import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState abstract class AssetListState( open val assets: List -) { - val visibleAssets: List - get() = assets.filter { !it.isHidden } - val hiddenAssets: List - get() = assets.filter { it.isHidden } -} +) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt index 825e15c68a..3bbc9c0183 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt @@ -1,7 +1,10 @@ package jp.co.soramitsu.wallet.impl.presentation.common import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items @@ -9,15 +12,17 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.SwipeableState import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import jp.co.soramitsu.common.compose.component.ActionItemType -import jp.co.soramitsu.common.compose.component.HiddenAssetsItem -import jp.co.soramitsu.common.compose.component.HiddenItemState +import jp.co.soramitsu.common.compose.component.B0 import jp.co.soramitsu.common.compose.component.MarginVertical import jp.co.soramitsu.common.compose.component.SwipeState import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState +import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId interface AssetsListInterface { @@ -35,9 +40,6 @@ fun AssetsList( header: (@Composable () -> Unit)? = null, footer: (@Composable () -> Unit)? = null ) { - val isShowHidden = remember { mutableStateOf(data.visibleAssets.isEmpty()) } - val onHiddenClick = remember { { isShowHidden.value = isShowHidden.value.not() } } - LazyColumn( state = listState, verticalArrangement = Arrangement.spacedBy(8.dp), @@ -46,29 +48,28 @@ fun AssetsList( if (header != null) { item { header() } } - items(data.visibleAssets, key = { it.key }) { assetState -> - SwipeableAssetListItem( - assetState = assetState, - assetClicked = callback::assetClicked, - actionItemClicked = callback::actionItemClicked - ) - } - if (data.hiddenAssets.isNotEmpty()) { + if (data.assets.isEmpty()) { item { - HiddenAssetsItem( - state = HiddenItemState(isShowHidden.value), - onClick = onHiddenClick - ) - } - if (isShowHidden.value) { - items(data.hiddenAssets, key = { it.key }) { assetState -> - SwipeableAssetListItem( - assetState = assetState, - assetClicked = callback::assetClicked, - actionItemClicked = callback::actionItemClicked + Box( + modifier = Modifier + .defaultMinSize(minHeight = 48.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + B0( + text = stringResource(id = R.string.wallet_all_assets_hidden), + textAlign = TextAlign.Center ) } } + } else { + items(data.assets, key = { it.key }) { assetState -> + SwipeableAssetListItem( + assetState = assetState, + assetClicked = callback::assetClicked, + actionItemClicked = callback::actionItemClicked + ) + } } if (footer != null) { item { footer() } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt new file mode 100644 index 0000000000..ae12f11395 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt @@ -0,0 +1,421 @@ +package jp.co.soramitsu.wallet.impl.presentation.manageassets + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Icon +import androidx.compose.material.Switch +import androidx.compose.material.SwitchColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.CenterHorizontally +import androidx.compose.ui.Alignment.Companion.CenterStart +import androidx.compose.ui.Alignment.Companion.CenterVertically +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import jp.co.soramitsu.common.compose.component.B0 +import jp.co.soramitsu.common.compose.component.B1 +import jp.co.soramitsu.common.compose.component.B2 +import jp.co.soramitsu.common.compose.component.CorneredInput +import jp.co.soramitsu.common.compose.component.FearlessProgress +import jp.co.soramitsu.common.compose.component.H3 +import jp.co.soramitsu.common.compose.component.H5 +import jp.co.soramitsu.common.compose.component.Image +import jp.co.soramitsu.common.compose.component.MarginHorizontal +import jp.co.soramitsu.common.compose.component.MarginVertical +import jp.co.soramitsu.common.compose.component.getImageRequest +import jp.co.soramitsu.common.compose.theme.black2 +import jp.co.soramitsu.common.compose.theme.black3 +import jp.co.soramitsu.common.compose.theme.black4 +import jp.co.soramitsu.common.compose.theme.colorAccentDark +import jp.co.soramitsu.common.compose.theme.darkButtonBackground +import jp.co.soramitsu.common.compose.theme.gray2 +import jp.co.soramitsu.common.compose.theme.transparent +import jp.co.soramitsu.common.compose.theme.white +import jp.co.soramitsu.common.compose.theme.white64 +import jp.co.soramitsu.common.utils.clickableSingle +import jp.co.soramitsu.common.utils.clickableWithNoIndication +import jp.co.soramitsu.feature_wallet_impl.R +import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId + +data class ManageAssetsScreenViewState( + val assets: Map>? = null, + val selectedChainName: String = "", + val selectedAssetId: String? = null, + val searchQuery: String? = null, + val showAllChains: Boolean = true +) { + companion object { + val default = ManageAssetsScreenViewState() + } +} + +interface ManageAssetsContentInterface { + fun onSearchInput(input: String) + fun onChecked(assetItemState: ManageAssetItemState, checked: Boolean) + fun onItemClicked(assetItemState: ManageAssetItemState) + fun onEditClicked(assetItemState: ManageAssetItemState) + fun onDoneClicked() + fun onSelectedChainClicked() +} + +@Composable +fun ManageAssetsContent( + state: ManageAssetsScreenViewState, + callback: ManageAssetsContentInterface +) { + Column( + modifier = Modifier + .nestedScroll(rememberNestedScrollInteropConnection()) + .padding(horizontal = 16.dp) + .fillMaxWidth() + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + B0( + text = stringResource(id = R.string.common_done), + modifier = Modifier + .align(CenterVertically) + .clickableSingle(onClick = callback::onDoneClicked), + color = colorAccentDark + ) + B0( + text = state.selectedChainName, + modifier = Modifier + .align(CenterVertically) + .clickableSingle(onClick = callback::onSelectedChainClicked) + ) + } + MarginVertical(margin = 16.dp) + CorneredInput(state = state.searchQuery, onInput = callback::onSearchInput, hintLabel = stringResource(id = R.string.common_search)) + MarginVertical(margin = 8.dp) + + if (state.assets == null) { + Box( + Modifier + .weight(1f) + .fillMaxWidth() + ) { + FearlessProgress( + Modifier.align(Alignment.Center) + ) + } + } else if (state.assets.isEmpty()) { + MarginVertical(margin = 16.dp) + Column( + horizontalAlignment = CenterHorizontally, + modifier = Modifier + .weight(1f) + .align(CenterHorizontally) + ) { + EmptyResultContent() + } + } else { + LazyColumn(modifier = Modifier.weight(1f)) { + item { + ManageAssetsHeader() + } + + items(state.assets.entries.toList()) { assetsGroup -> + val assets = assetsGroup.value + + if (assets.size == 1) { + ManageAssetItem(assets[0], callback::onEditClicked, callback::onItemClicked, callback::onChecked) + } else { + val isCollapsed = remember { mutableStateOf(true) } + GroupItem(assets, isCollapsed) + + if (isCollapsed.value.not() || state.searchQuery.isNullOrBlank().not()) { + Column { + assets.map { + ManageAssetItem(it.copy(isGrouped = true), callback::onEditClicked, callback::onItemClicked, callback::onChecked) + } + } + } + } + } + } + } + MarginVertical(margin = 52.dp) + } +} + +@Composable +private fun ManageAssetsHeader() { + Box( + modifier = Modifier + .height(44.dp) + .fillMaxWidth(), + contentAlignment = CenterStart + ) { + H5( + text = stringResource(id = R.string.wallet_manage_assets), + textAlign = TextAlign.Start + ) + } +} + +@Composable +fun EmptyResultContent() { + Column( + horizontalAlignment = CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_alert), + contentDescription = null, + tint = gray2 + ) + H3(text = stringResource(id = R.string.common_search_assets_alert_title)) + B0( + text = stringResource(id = R.string.common_empty_search), + color = gray2 + ) + } +} + +data class ManageAssetItemState( + val id: String, + val imageUrl: String?, + val chainName: String, + val assetName: String?, + val symbol: String, + val amount: String, + val fiatAmount: String, + val chainId: ChainId, + val isChecked: Boolean, + val showEdit: Boolean, + val isGrouped: Boolean = false +) + +@Composable +fun ManageAssetItem( + state: ManageAssetItemState, + onEditClick: (ManageAssetItemState) -> Unit, + onItemClick: (ManageAssetItemState) -> Unit, + onChecked: (ManageAssetItemState, Boolean) -> Unit +) { + val switchColors = object : SwitchColors { + @Composable + override fun thumbColor(enabled: Boolean, checked: Boolean): State { + val color = if (enabled) { + white + } else { + white64 + } + return rememberUpdatedState(color) + } + + @Composable + override fun trackColor(enabled: Boolean, checked: Boolean): State { + return rememberUpdatedState(transparent) + } + } + + Row( + verticalAlignment = CenterVertically, + modifier = Modifier + .height(48.dp) + .fillMaxWidth() + .background(if (state.isGrouped) darkButtonBackground else Color.Unspecified) + .clickableWithNoIndication { + onItemClick(state) + } + ) { + AsyncImage( + model = state.imageUrl?.let { getImageRequest(LocalContext.current, it) }, + contentDescription = null, + modifier = Modifier + .testTag("ManageAssetItem_image_${state.id}") + .size(32.dp), + colorFilter = ColorFilter.tint(white64, BlendMode.DstOut).takeIf { state.isChecked.not() } + ) + MarginHorizontal(margin = 10.dp) + Row( + verticalAlignment = CenterVertically + ) { + Column { + Row( + verticalAlignment = CenterVertically + ) { + B1( + text = state.symbol, + fontWeight = FontWeight.W600, + color = if (state.isChecked) Color.Unspecified else black2 + ) + if (state.showEdit) { + MarginHorizontal(margin = 6.dp) + Image( + res = R.drawable.ic_edit_20, + modifier = Modifier + .size(20.dp) + .clickableSingle { + onEditClick(state) + } + ) + } + } + + B2(text = state.chainName, color = black2) + } + Spacer(modifier = Modifier.weight(1f)) + Column( + horizontalAlignment = Alignment.End + ) { + B1( + text = state.amount, + fontWeight = FontWeight.W600, + color = if (state.isChecked) Color.Unspecified else black2 + ) + B2(text = state.fiatAmount, color = black2) + } + MarginHorizontal(margin = 8.dp) + val trackColor = when { + state.isChecked -> colorAccentDark + else -> black3 + } + Switch( + colors = switchColors, + checked = state.isChecked, + onCheckedChange = { onChecked(state, it) }, + modifier = Modifier + .background(color = trackColor, shape = RoundedCornerShape(20.dp)) + .padding(3.dp) + .height(20.dp) + .width(36.dp) + .align(CenterVertically) + ) + } + } +} + +@Composable +private fun GroupItem( + groupAssets: List, + isCollapsed: MutableState +) { + Row( + verticalAlignment = CenterVertically, + modifier = Modifier + .height(48.dp) + .fillMaxWidth() + .clickableWithNoIndication { + isCollapsed.value = isCollapsed.value.not() + } + ) { + val image = groupAssets.firstOrNull { it.imageUrl != null }?.imageUrl?.let { + getImageRequest(LocalContext.current, it) + } + val assetName = groupAssets.firstOrNull { it.assetName != null }?.assetName.orEmpty() + + AsyncImage( + model = image, + contentDescription = null, + modifier = Modifier + .testTag("ManageGroupItem_image_${groupAssets.getOrNull(0)?.symbol}") + .size(32.dp) + ) + MarginHorizontal(margin = 10.dp) + Row( + verticalAlignment = CenterVertically + ) { + Column { + B1(text = assetName, fontWeight = FontWeight.W600) + B2(text = pluralStringResource(id = R.plurals.common_networks_format, groupAssets.size, groupAssets.size), color = black2) + } + Spacer(modifier = Modifier.weight(1f)) + Image( + res = R.drawable.ic_chevron_up_white, + modifier = Modifier + .size(20.dp) + .rotate(if (isCollapsed.value) 180f else 0f) + ) + MarginHorizontal(margin = 8.dp) + } + } +} + +@Preview +@Composable +private fun ManageAssetsScreenPreview() { + val items = listOf( + ManageAssetItemState( + id = "1", + imageUrl = "https://raw.githubusercontent.com/soramitsu/fearless-utils/master/icons/chains/white/Moonriver.svg", + chainName = "Kusama", + assetName = "Asset on Kusama", + symbol = "KSM", + amount = "0", + fiatAmount = "0$", + chainId = "", + isChecked = true, + showEdit = false + ), + ManageAssetItemState( + id = "2", + imageUrl = "https://raw.githubusercontent.com/soramitsu/fearless-utils/master/icons/chains/white/Kusama.svg", + chainName = "Moonriver", + assetName = "Asset on Moonriver", + symbol = "MOVR", + amount = "10", + fiatAmount = "23240$", + chainId = "", + isChecked = false, + showEdit = true + ) + ) + val state = ManageAssetsScreenViewState( + selectedChainName = "All chains", + assets = mapOf("DOT" to items, "MOVR" to items.filter { it.symbol == "MOVR" }), + searchQuery = null + ) + ManageAssetItem(items[0], {}, {}, { _, _ -> }) + Column( + Modifier.background(black4) + ) { + ManageAssetsContent( + state = state, + callback = object : ManageAssetsContentInterface { + override fun onSearchInput(input: String) {} + override fun onChecked(assetItemState: ManageAssetItemState, checked: Boolean) {} + override fun onItemClicked(assetItemState: ManageAssetItemState) {} + override fun onEditClicked(assetItemState: ManageAssetItemState) {} + override fun onDoneClicked() {} + override fun onSelectedChainClicked() {} + } + ) + } +} diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt new file mode 100644 index 0000000000..540398ad85 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt @@ -0,0 +1,40 @@ +package jp.co.soramitsu.wallet.impl.presentation.manageassets + +import android.content.DialogInterface +import android.widget.FrameLayout +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.fragment.app.viewModels +import com.google.android.material.bottomsheet.BottomSheetBehavior +import dagger.hilt.android.AndroidEntryPoint +import jp.co.soramitsu.common.base.BaseComposeBottomSheetDialogFragment +import jp.co.soramitsu.common.compose.component.BottomSheetScreen + +@AndroidEntryPoint +class ManageAssetsFragment : BaseComposeBottomSheetDialogFragment() { + + override val viewModel: ManageAssetsViewModel by viewModels() + + @Composable + override fun Content(padding: PaddingValues) { + val state by viewModel.state.collectAsState() + BottomSheetScreen { + ManageAssetsContent( + state = state, + callback = viewModel + ) + } + } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + viewModel.onDialogClose() + } + override fun setupBehavior(behavior: BottomSheetBehavior) { + behavior.state = BottomSheetBehavior.STATE_EXPANDED + behavior.isHideable = false + behavior.skipCollapsed = true + } +} diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt new file mode 100644 index 0000000000..dea275ee75 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt @@ -0,0 +1,192 @@ +package jp.co.soramitsu.wallet.impl.presentation.manageassets + +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import jp.co.soramitsu.account.api.domain.interfaces.AccountInteractor +import jp.co.soramitsu.common.base.BaseViewModel +import jp.co.soramitsu.common.model.AssetBooleanState +import jp.co.soramitsu.common.resources.ResourceManager +import jp.co.soramitsu.common.utils.formatCrypto +import jp.co.soramitsu.common.utils.mapList +import jp.co.soramitsu.common.utils.orZero +import jp.co.soramitsu.core.models.ChainId +import jp.co.soramitsu.feature_wallet_impl.R +import jp.co.soramitsu.wallet.impl.data.mappers.mapAssetToAssetModel +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor +import jp.co.soramitsu.wallet.impl.presentation.WalletRouter +import jp.co.soramitsu.wallet.impl.presentation.model.AssetModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +@HiltViewModel +class ManageAssetsViewModel @Inject constructor( + private val walletRouter: WalletRouter, + private val walletInteractor: WalletInteractor, + private val accountInteractor: AccountInteractor, + private val resourceManager: ResourceManager +) : BaseViewModel(), ManageAssetsContentInterface { + + private val initialAssetStates = MutableStateFlow>(emptyList()) + private val currentAssetStates = MutableStateFlow>(emptyList()) + + private val selectedChainIdFlow = MutableStateFlow(null) + + private val savedChainFlow = selectedChainIdFlow.map { chainId -> + chainId?.let { walletInteractor.getChain(it) } + } + + private val assetModelsFlow: Flow> = + combine( + walletInteractor.assetsFlow(), + selectedChainIdFlow + ) { assets, chainId -> + assets.filter { + chainId == null || it.asset.token.configuration.chainId == chainId + } + } + .mapList { + when { + it.hasAccount -> it.asset + else -> null + } + } + .map { it.filterNotNull() } + .mapList { mapAssetToAssetModel(it) } + + + private val enteredTokenQueryFlow = MutableStateFlow("") + + val state = MutableStateFlow(ManageAssetsScreenViewState.default) + + private fun subscribeScreenState() { + savedChainFlow.onEach { + state.value = state.value.copy(selectedChainName = it?.name ?: resourceManager.getString(R.string.chain_selection_all_networks)) + }.launchIn(this) + + combine(assetModelsFlow, enteredTokenQueryFlow, currentAssetStates) { assetModels, searchQuery, currentStates -> + val sortedAssets = assetModels + .filter { + it.token.configuration.isUtility + } + .filter { + searchQuery.isEmpty() || + it.token.configuration.symbol.contains(searchQuery, true) || + it.token.configuration.name.orEmpty().contains(searchQuery, true) + } + .map { model -> + model.copy(isHidden = currentStates.firstOrNull { + it.assetId == model.token.configuration.id && it.chainId == model.token.configuration.chainId + }?.value == false) + } + .sortedWith(compareBy { + "skip" // it.isHidden == true + }.thenByDescending { + it.fiatAmount.orZero() + }.thenByDescending { + it.available.orZero() + }.thenBy { + it.token.configuration.chainName + }) + + val assets = sortedAssets.map { + it.toManageAssetItemState() + } + + val groupedAssets: Map> = assets.groupBy { + it.symbol + } + + groupedAssets to searchQuery + }.onEach { (assets, searchQuery) -> + state.value = state.value.copy(assets = assets, searchQuery = searchQuery) + }.launchIn(this) + } + + init { + subscribeScreenState() + + accountInteractor.selectedMetaAccountFlow().map { it.id }.distinctUntilChanged().map { + selectedChainIdFlow.value = walletInteractor.getSavedChainId(it) + + walletInteractor.assetsFlow().firstOrNull()?.let { assets -> + val assetStates = assets.map { + AssetBooleanState( + chainId = it.asset.token.configuration.chainId, + assetId = it.asset.token.configuration.id, + value = it.asset.enabled != false + ) + } + initialAssetStates.value = assetStates + currentAssetStates.value = assetStates + } + }.launchIn(this) + + walletRouter.chainSelectorPayloadFlow.map { chainId -> + val walletId = accountInteractor.selectedLightMetaAccount().id + walletInteractor.saveChainId(walletId, chainId) + selectedChainIdFlow.value = chainId + }.launchIn(this) + } + + private fun AssetModel.toManageAssetItemState() = ManageAssetItemState( + id = token.configuration.id, + imageUrl = token.configuration.iconUrl, + chainName = token.configuration.chainName, + assetName = token.configuration.name, + symbol = token.configuration.symbol.uppercase(), + amount = available.orZero().formatCrypto(), + fiatAmount = getAsFiatWithCurrency(available) ?: "${token.fiatSymbol.orEmpty()}0", + chainId = token.configuration.chainId, + isChecked = isHidden != true, + showEdit = false + ) + + override fun onSearchInput(input: String) { + enteredTokenQueryFlow.value = input + } + + override fun onChecked(assetItemState: ManageAssetItemState, checked: Boolean) { + currentAssetStates.value = currentAssetStates.value.map { + if (it.assetId == assetItemState.id && it.chainId == assetItemState.chainId) { + it.copy(value = checked) + } else { + it + } + } + } + + override fun onItemClicked(assetItemState: ManageAssetItemState) { + } + + override fun onEditClicked(assetItemState: ManageAssetItemState) { + } + + override fun onDoneClicked() { + walletRouter.back() + } + + override fun onSelectedChainClicked() { + launch { + val selectedChainId = savedChainFlow.firstOrNull()?.id + walletRouter.openSelectChain(selectedChainId, isFilteringEnabled = true) + } + } + + fun onDialogClose() { + val initial = initialAssetStates.value + val changes = currentAssetStates.value.filter { + it !in initial + } + kotlinx.coroutines.MainScope().launch { + walletInteractor.updateAssetsHiddenState(changes) + } + } +} + From 9ebc2790686b9e9408c3468d7ba9d1c0a4e27c34 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 4 Apr 2024 18:07:22 +0500 Subject: [PATCH 006/100] FLW-4512 We do not show all asset on the Acala network --- .../impl/presentation/balance/list/BalanceListViewModel.kt | 5 ----- .../impl/presentation/manageassets/ManageAssetsViewModel.kt | 4 +--- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 8adbc4c450..c9112984bf 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -177,11 +177,6 @@ class BalanceListViewModel @Inject constructor( } private val currentMetaAccountFlow = interactor.selectedLightMetaAccountFlow() - .onEach { - if (pendulumPreInstalledAccountsScenario.isPendulumMode(it.id)) { - selectedChainId.value = pendulumChainId - } - } private val assetTypeSelectorState = MutableStateFlow( MultiToggleButtonState( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt index dea275ee75..65a3f10b0e 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt @@ -72,9 +72,6 @@ class ManageAssetsViewModel @Inject constructor( combine(assetModelsFlow, enteredTokenQueryFlow, currentAssetStates) { assetModels, searchQuery, currentStates -> val sortedAssets = assetModels - .filter { - it.token.configuration.isUtility - } .filter { searchQuery.isEmpty() || it.token.configuration.symbol.contains(searchQuery, true) || @@ -180,6 +177,7 @@ class ManageAssetsViewModel @Inject constructor( } fun onDialogClose() { + walletRouter.setChainSelectorPayload(selectedChainIdFlow.value) val initial = initialAssetStates.value val changes = currentAssetStates.value.filter { it !in initial From 212d62e76bf29e75dd861c39c22ce2fb9805f554 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 4 Apr 2024 22:48:53 +0500 Subject: [PATCH 007/100] FLW-4513 There are all toggles On: add popular and favourite to chain select in asset management --- .../balance/list/BalanceListViewModel.kt | 4 +- .../manageassets/ManageAssetsContent.kt | 6 +- .../manageassets/ManageAssetsViewModel.kt | 58 +++++++++++++++++-- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index c9112984bf..fc1f2d484a 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -235,8 +235,8 @@ class BalanceListViewModel @Inject constructor( val filteredAssets = assets .filter { it.asset.enabled != false && - (selectedChainId == it.asset.token.configuration.chainId || - selectedChainId == null || + (selectedChainId == null || + it.asset.token.configuration.chainId == selectedChainId || it.asset.token.configuration.chainId in filteredChains.map { it.id }) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt index ae12f11395..16784a0be7 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt @@ -71,7 +71,7 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId data class ManageAssetsScreenViewState( val assets: Map>? = null, - val selectedChainName: String = "", + val selectedChainTitle: String = "", val selectedAssetId: String? = null, val searchQuery: String? = null, val showAllChains: Boolean = true @@ -113,7 +113,7 @@ fun ManageAssetsContent( color = colorAccentDark ) B0( - text = state.selectedChainName, + text = state.selectedChainTitle, modifier = Modifier .align(CenterVertically) .clickableSingle(onClick = callback::onSelectedChainClicked) @@ -398,7 +398,7 @@ private fun ManageAssetsScreenPreview() { ) ) val state = ManageAssetsScreenViewState( - selectedChainName = "All chains", + selectedChainTitle = "All chains", assets = mapOf("DOT" to items, "MOVR" to items.filter { it.symbol == "MOVR" }), searchQuery = null ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt index 65a3f10b0e..4ec4347558 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt @@ -4,6 +4,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import jp.co.soramitsu.account.api.domain.interfaces.AccountInteractor import jp.co.soramitsu.common.base.BaseViewModel +import jp.co.soramitsu.common.compose.component.ChainSelectorViewStateWithFilters import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCrypto @@ -12,6 +13,7 @@ import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.core.models.ChainId import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.wallet.impl.data.mappers.mapAssetToAssetModel +import jp.co.soramitsu.wallet.impl.domain.ChainInteractor import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import jp.co.soramitsu.wallet.impl.presentation.WalletRouter import jp.co.soramitsu.wallet.impl.presentation.model.AssetModel @@ -30,6 +32,7 @@ class ManageAssetsViewModel @Inject constructor( private val walletRouter: WalletRouter, private val walletInteractor: WalletInteractor, private val accountInteractor: AccountInteractor, + private val chainInteractor: ChainInteractor, private val resourceManager: ResourceManager ) : BaseViewModel(), ManageAssetsContentInterface { @@ -45,10 +48,40 @@ class ManageAssetsViewModel @Inject constructor( private val assetModelsFlow: Flow> = combine( walletInteractor.assetsFlow(), + chainInteractor.getChainsFlow(), + walletInteractor.selectedMetaAccountFlow(), + walletInteractor.observeSelectedAccountChainSelectFilter(), selectedChainIdFlow - ) { assets, chainId -> + ) { assets, chains, currentMetaAccount, appliedFilterAsString, selectedChainId -> + val filter = ChainSelectorViewStateWithFilters.Filter.entries.find { + it.name == appliedFilterAsString + } ?: ChainSelectorViewStateWithFilters.Filter.All + + val selectedAccountFavoriteChains = currentMetaAccount.favoriteChains + val chainsWithFavoriteInfo = chains.map { chain -> + chain to (selectedAccountFavoriteChains[chain.id]?.isFavorite == true) + } + val filteredChains = when { + selectedChainId != null -> chains.filter { it.id == selectedChainId } + filter == ChainSelectorViewStateWithFilters.Filter.All -> chainsWithFavoriteInfo.map { it.first } + + filter == ChainSelectorViewStateWithFilters.Filter.Favorite -> + chainsWithFavoriteInfo.filter { (_, isFavorite) -> isFavorite }.map { it.first } + + filter == ChainSelectorViewStateWithFilters.Filter.Popular -> + chainsWithFavoriteInfo.filter { (chain, _) -> + chain.rank != null + }.sortedBy { (chain, _) -> + chain.rank + }.map { it.first } + + else -> emptyList() + } + assets.filter { - chainId == null || it.asset.token.configuration.chainId == chainId + selectedChainId == null || + it.asset.token.configuration.chainId == selectedChainId || + it.asset.token.configuration.chainId in filteredChains.map { it.id } } } .mapList { @@ -66,8 +99,25 @@ class ManageAssetsViewModel @Inject constructor( val state = MutableStateFlow(ManageAssetsScreenViewState.default) private fun subscribeScreenState() { - savedChainFlow.onEach { - state.value = state.value.copy(selectedChainName = it?.name ?: resourceManager.getString(R.string.chain_selection_all_networks)) + combine( + savedChainFlow, + walletInteractor.observeSelectedAccountChainSelectFilter() + ) { chain, filterAsText -> + val filterApplied = ChainSelectorViewStateWithFilters.Filter.entries.find { + it.name == filterAsText + } ?: ChainSelectorViewStateWithFilters.Filter.All + + val selectedChainTitle = chain?.name ?: when(filterApplied) { + ChainSelectorViewStateWithFilters.Filter.All -> + resourceManager.getString(R.string.chain_selection_all_networks) + + ChainSelectorViewStateWithFilters.Filter.Popular -> + resourceManager.getString(R.string.network_management_popular) + + ChainSelectorViewStateWithFilters.Filter.Favorite -> + resourceManager.getString(R.string.network_managment_favourite) + } + state.value = state.value.copy(selectedChainTitle = selectedChainTitle) }.launchIn(this) combine(assetModelsFlow, enteredTokenQueryFlow, currentAssetStates) { assetModels, searchQuery, currentStates -> From 9d23d319fd1b1207e93e36326ad0a74fede144af Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Mon, 8 Apr 2024 13:17:35 +0500 Subject: [PATCH 008/100] FLW-4511 After first start we should scroll down automatically --- .../domain/interfaces/WalletInteractor.kt | 3 ++ .../impl/domain/WalletInteractorImpl.kt | 11 ++++- .../balance/list/BalanceListViewModel.kt | 26 ++++++++++++ .../presentation/balance/list/WalletScreen.kt | 41 ++++++++++++++++++- .../presentation/balance/list/WalletState.kt | 6 ++- 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt index 2dfb1f5739..a06e266677 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt @@ -154,4 +154,7 @@ interface WalletInteractor { suspend fun estimateClaimRewardsFee(chainId: ChainId): BigInteger suspend fun getVestingLockedAmount(chainId: ChainId): BigInteger? suspend fun claimRewards(chainId: ChainId): Result + + fun getAssetManagementIntroPassed(): Boolean + suspend fun saveAssetManagementIntroPassed() } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index 5805eece98..f016a3b096 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -66,7 +66,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flatMapMerge import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.withIndex @@ -78,11 +77,11 @@ const val QR_PREFIX_WALLET_CONNECT = "wc" private const val PREFS_WALLET_SELECTED_CHAIN_ID = "wallet_selected_chain_id" private const val PREFS_SORA_CARD_HIDDEN_SESSIONS_COUNT = "prefs_sora_card_hidden_sessions_count" private const val SORA_CARD_HIDDEN_SESSIONS_LIMIT = 5 -private const val HIDE_ZERO_BALANCES_PREFS_KEY = "hideZeroBalances" private const val CHAIN_SELECT_FILTER_APPLIED = "chain_select_filter_applied" private const val ACCOUNT_ID_MIN_TAG = 26 private const val ACCOUNT_ID_MAX_TAG = 51 private const val ASSET_SORTING_KEY = "ASSET_SORTING_KEY" +private const val ASSET_MANAGEMENT_INTRO_PASSED_KEY = "ASSET_MANAGEMENT_INTRO_PASSED_KEY" class WalletInteractorImpl( private val walletRepository: WalletRepository, @@ -636,6 +635,14 @@ class WalletInteractorImpl( preferences.putString(key, filter) } + override suspend fun saveAssetManagementIntroPassed() { + preferences.putBoolean(ASSET_MANAGEMENT_INTRO_PASSED_KEY, true) + } + + override fun getAssetManagementIntroPassed(): Boolean { + return preferences.getBoolean(ASSET_MANAGEMENT_INTRO_PASSED_KEY, defaultValue = false) + } + @OptIn(ExperimentalCoroutinesApi::class) override fun observeSelectedAccountChainSelectFilter(): Flow { return accountRepository.selectedMetaAccountFlow().map { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index fc1f2d484a..ac4fa0ba42 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -1,5 +1,6 @@ package jp.co.soramitsu.wallet.impl.presentation.balance.list +import android.util.Log import android.widget.LinearLayout import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.SwipeableState @@ -88,6 +89,7 @@ import jp.co.soramitsu.wallet.impl.presentation.model.ControllerDeprecationWarni import jp.co.soramitsu.wallet.impl.presentation.model.toModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -97,6 +99,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first @@ -136,6 +139,7 @@ class BalanceListViewModel @Inject constructor( ) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateMixin, WalletScreenInterface { + private var awaitAssetsJob: Job? = null private val accountAddressToChainIdMap = mutableMapOf() private val _showFiatChooser = MutableLiveData() @@ -477,6 +481,28 @@ class BalanceListViewModel @Inject constructor( state.value = state.value.copy(hasNetworkIssues = it) }.launchIn(this) subscribeTotalBalance() + if (interactor.getAssetManagementIntroPassed().not()) { + startManageAssetsIntroAnimation() + } + } + + @OptIn(FlowPreview::class) + private fun startManageAssetsIntroAnimation() { + awaitAssetsJob?.cancel() + awaitAssetsJob = assetStates.filter { it.isNotEmpty() } + .map { it.size } + .distinctUntilChanged() + .debounce(200L) + .onEach { + state.value = state.value.copy(scrollToBottomEvent = Event(Unit)) + interactor.saveAssetManagementIntroPassed() + awaitAssetsJob?.cancel() + } + .catch { + Log.d("BalanceListViewModel", it.message, it) + } + .launchIn(this) + awaitAssetsJob?.start() } private fun subscribeTotalBalance() { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt index 268619f117..141f850233 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt @@ -1,5 +1,9 @@ package jp.co.soramitsu.wallet.impl.presentation.balance.list +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -19,8 +23,10 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.SwipeableState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.scale import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -70,12 +76,34 @@ fun WalletScreen( ) { val listState = rememberForeverLazyListState("wallet_screen") + val scale = remember { Animatable(initialValue = 1f) } + LaunchedEffect(data.scrollToTopEvent) { data.scrollToTopEvent?.getContentIfNotHandled()?.let { listState.animateScrollToItem(0) } } + LaunchedEffect(data.scrollToBottomEvent) { + data.scrollToBottomEvent?.getContentIfNotHandled()?.let { + if (data.assetsState is WalletAssetsState.Assets) { + val items = data.assetsState.assets.size + listOf("header", "footer").size + val lastItemIndex = items - 1 + listState.animateScrollToItem(lastItemIndex) + + scale.animateTo( + targetValue = 1.2f, + animationSpec = tween(durationMillis = 600) + ) + scale.animateTo( + targetValue = 1f, + animationSpec = tween(durationMillis = 600) + ) + } + } + } + + Column(modifier = Modifier.padding(horizontal = 16.dp)) { MarginVertical(margin = 16.dp) AssetBalance( @@ -98,7 +126,7 @@ fun WalletScreen( } is WalletAssetsState.Assets -> { val header: @Composable () -> Unit = { Banners(data, callback) } - val footer: @Composable () -> Unit = { WalletScreenFooter(callback::onManageAssetClick) } + val footer: @Composable () -> Unit = { WalletScreenFooter(scale.value, callback::onManageAssetClick) } AssetsList( data = data.assetsState, callback = callback, @@ -213,12 +241,20 @@ fun WalletScreenWithRefresh( @Composable fun WalletScreenFooter( + scale: Float, onManageAssetsClick: () -> Unit ) { GrayButton( text = stringResource(id = R.string.wallet_manage_assets), modifier = Modifier + .scale(scale) .fillMaxWidth() + .animateContentSize( + animationSpec = tween( + durationMillis = 300, + easing = LinearOutSlowInEasing + ) + ) .height(48.dp), onClick = onManageAssetsClick ) @@ -292,7 +328,8 @@ private fun PreviewWalletScreen() { hasNetworkIssues = true, soraCardState = SoraCardItemViewState(null, null, null, true), isBackedUp = false, - scrollToTopEvent = null + scrollToTopEvent = null, + scrollToBottomEvent = null ), callback = emptyCallback ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt index 7b0a84a515..7d1fd1bd5a 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt @@ -17,7 +17,8 @@ data class WalletState( val hasNetworkIssues: Boolean, val soraCardState: SoraCardItemViewState?, val isBackedUp: Boolean, - val scrollToTopEvent: Event? + val scrollToTopEvent: Event?, + val scrollToBottomEvent: Event?, ) { companion object { val default = WalletState( @@ -27,7 +28,8 @@ data class WalletState( hasNetworkIssues = false, soraCardState = null, isBackedUp = true, - scrollToTopEvent = null + scrollToTopEvent = null, + scrollToBottomEvent = null ) } } From 2e31bf3f10899f109fc61acfdad2e7c5cf0c8104 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Mon, 8 Apr 2024 22:03:26 +0500 Subject: [PATCH 009/100] FLW-4510 We should show Popular but not All networks --- .../mnemonic/confirm/ConfirmMnemonicViewModel.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt index 37656c857f..e2a29334a6 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt @@ -11,6 +11,7 @@ import jp.co.soramitsu.account.api.domain.interfaces.AccountInteractor import jp.co.soramitsu.account.impl.presentation.AccountRouter import jp.co.soramitsu.account.impl.presentation.mnemonic.confirm.ConfirmMnemonicFragment.Companion.KEY_PAYLOAD import jp.co.soramitsu.common.base.BaseViewModel +import jp.co.soramitsu.common.compose.component.ChainSelectorViewStateWithFilters import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.combine @@ -19,12 +20,14 @@ import jp.co.soramitsu.common.utils.requireException import jp.co.soramitsu.common.utils.sendEvent import jp.co.soramitsu.common.vibration.DeviceVibrator import jp.co.soramitsu.feature_account_impl.R +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import kotlinx.coroutines.launch @HiltViewModel class ConfirmMnemonicViewModel @Inject constructor( private val resourceManager: ResourceManager, private val interactor: AccountInteractor, + private val walletInteractor: WalletInteractor, private val router: AccountRouter, private val deviceVibrator: DeviceVibrator, private val savedStateHandle: SavedStateHandle @@ -133,6 +136,11 @@ class ConfirmMnemonicViewModel @Inject constructor( val result = interactor.createAccount(accountName, mnemonicString, cryptoType, substrateDerivationPath, ethereumDerivationPath, isBackedUp) if (result.isSuccess) { + walletInteractor.saveChainSelectFilter( + walletInteractor.getSelectedMetaAccount().id, + ChainSelectorViewStateWithFilters.Filter.Popular.toString() + ) + continueBasedOnCodeStatus() } else { showError(result.requireException()) From 3dfc366152e3ea8a8cc73c626e17487d0d612acc Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 9 Apr 2024 12:58:37 +0500 Subject: [PATCH 010/100] FLW-4510 We should show Popular but not All networks: disable all except utility assets of popular networks for newly created accounts --- .../confirm/ConfirmMnemonicViewModel.kt | 30 ++++++++++++++++--- .../manageassets/ManageAssetsContent.kt | 6 +++- .../manageassets/ManageAssetsViewModel.kt | 2 ++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt index e2a29334a6..5dd02b9483 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt @@ -12,6 +12,7 @@ import jp.co.soramitsu.account.impl.presentation.AccountRouter import jp.co.soramitsu.account.impl.presentation.mnemonic.confirm.ConfirmMnemonicFragment.Companion.KEY_PAYLOAD import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.compose.component.ChainSelectorViewStateWithFilters +import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.combine @@ -21,6 +22,8 @@ import jp.co.soramitsu.common.utils.sendEvent import jp.co.soramitsu.common.vibration.DeviceVibrator import jp.co.soramitsu.feature_account_impl.R import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch @HiltViewModel @@ -136,10 +139,7 @@ class ConfirmMnemonicViewModel @Inject constructor( val result = interactor.createAccount(accountName, mnemonicString, cryptoType, substrateDerivationPath, ethereumDerivationPath, isBackedUp) if (result.isSuccess) { - walletInteractor.saveChainSelectFilter( - walletInteractor.getSelectedMetaAccount().id, - ChainSelectorViewStateWithFilters.Filter.Popular.toString() - ) + setupNewAccountAssetsVisibility() continueBasedOnCodeStatus() } else { @@ -149,6 +149,28 @@ class ConfirmMnemonicViewModel @Inject constructor( } } + private suspend fun setupNewAccountAssetsVisibility() { + walletInteractor.saveChainSelectFilter( + walletInteractor.getSelectedMetaAccount().id, + ChainSelectorViewStateWithFilters.Filter.Popular.toString() + ) + + walletInteractor.getChains().filter { it.isNotEmpty() }.firstOrNull()?.let { chains -> + val defaultAssetStatesForNewAccount = chains.map { chain -> + val isPopular = chain.rank != null + chain.assets.map { + AssetBooleanState( + chainId = it.chainId, + assetId = it.id, + value = it.isUtility && isPopular + ) + } + }.flatten() + + walletInteractor.updateAssetsHiddenState(defaultAssetStatesForNewAccount) + } + } + private fun createChainAccount(extras: ConfirmMnemonicPayload.CreateChainExtras) { viewModelScope.launch { val mnemonicString = originMnemonic.joinToString(" ") diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt index 16784a0be7..7997bbb532 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt @@ -218,6 +218,7 @@ data class ManageAssetItemState( val chainId: ChainId, val isChecked: Boolean, val showEdit: Boolean, + val isZeroAmount: Boolean, val isGrouped: Boolean = false ) @@ -271,10 +272,11 @@ fun ManageAssetItem( Row( verticalAlignment = CenterVertically ) { + val symbolColor = if (!state.isChecked || state.isZeroAmount) black2 else Color.Unspecified B1( text = state.symbol, fontWeight = FontWeight.W600, - color = if (state.isChecked) Color.Unspecified else black2 + color = symbolColor ) if (state.showEdit) { MarginHorizontal(margin = 6.dp) @@ -382,6 +384,7 @@ private fun ManageAssetsScreenPreview() { fiatAmount = "0$", chainId = "", isChecked = true, + isZeroAmount = false, showEdit = false ), ManageAssetItemState( @@ -394,6 +397,7 @@ private fun ManageAssetsScreenPreview() { fiatAmount = "23240$", chainId = "", isChecked = false, + isZeroAmount = true, showEdit = true ) ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt index 4ec4347558..680fdb2296 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt @@ -8,6 +8,7 @@ import jp.co.soramitsu.common.compose.component.ChainSelectorViewStateWithFilter import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCrypto +import jp.co.soramitsu.common.utils.isZero import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.core.models.ChainId @@ -192,6 +193,7 @@ class ManageAssetsViewModel @Inject constructor( fiatAmount = getAsFiatWithCurrency(available) ?: "${token.fiatSymbol.orEmpty()}0", chainId = token.configuration.chainId, isChecked = isHidden != true, + isZeroAmount = available.orZero().isZero(), showEdit = false ) From e70e99ecbc0d4932b941d4a3bae3ec1be56ec11c Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 9 Apr 2024 19:38:15 +0500 Subject: [PATCH 011/100] FLW-4463 Asset management screen: update ui/ux, implement correct sorting for disabled FLW-4523 If we switch Off the toggles Then we should not show the balance --- .../manageassets/ManageAssetsContent.kt | 68 +++++++++++++++---- .../manageassets/ManageAssetsViewModel.kt | 14 ++-- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt index 7997bbb532..8479f576df 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt @@ -214,7 +214,7 @@ data class ManageAssetItemState( val assetName: String?, val symbol: String, val amount: String, - val fiatAmount: String, + val fiatAmount: String?, val chainId: ChainId, val isChecked: Boolean, val showEdit: Boolean, @@ -294,15 +294,19 @@ fun ManageAssetItem( B2(text = state.chainName, color = black2) } Spacer(modifier = Modifier.weight(1f)) - Column( - horizontalAlignment = Alignment.End - ) { - B1( - text = state.amount, - fontWeight = FontWeight.W600, - color = if (state.isChecked) Color.Unspecified else black2 - ) - B2(text = state.fiatAmount, color = black2) + if (state.isChecked) { + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.Center + ) { + B1( + text = state.amount, + fontWeight = FontWeight.W600 + ) + state.fiatAmount?.let { + B2(text = it, color = black2) + } + } } MarginHorizontal(margin = 8.dp) val trackColor = when { @@ -342,20 +346,24 @@ private fun GroupItem( getImageRequest(LocalContext.current, it) } val assetName = groupAssets.firstOrNull { it.assetName != null }?.assetName.orEmpty() + val allAssetsAreHidden = groupAssets.all { it.isChecked.not() } AsyncImage( model = image, contentDescription = null, modifier = Modifier .testTag("ManageGroupItem_image_${groupAssets.getOrNull(0)?.symbol}") - .size(32.dp) + .size(32.dp), + colorFilter = ColorFilter.tint(white64, BlendMode.DstOut).takeIf { allAssetsAreHidden } ) MarginHorizontal(margin = 10.dp) Row( verticalAlignment = CenterVertically ) { + val groupNameColor = if (allAssetsAreHidden) black2 else Color.Unspecified + Column { - B1(text = assetName, fontWeight = FontWeight.W600) + B1(text = assetName, fontWeight = FontWeight.W600, color = groupNameColor) B2(text = pluralStringResource(id = R.plurals.common_networks_format, groupAssets.size, groupAssets.size), color = black2) } Spacer(modifier = Modifier.weight(1f)) @@ -384,7 +392,7 @@ private fun ManageAssetsScreenPreview() { fiatAmount = "0$", chainId = "", isChecked = true, - isZeroAmount = false, + isZeroAmount = true, showEdit = false ), ManageAssetItemState( @@ -399,11 +407,43 @@ private fun ManageAssetsScreenPreview() { isChecked = false, isZeroAmount = true, showEdit = true + ), + ManageAssetItemState( + id = "3", + imageUrl = "https://raw.githubusercontent.com/soramitsu/fearless-utils/master/icons/chains/white/Kusama.svg", + chainName = "Westend", + assetName = "WND from the Westend", + symbol = "WND", + amount = "42", + fiatAmount = null, + chainId = "", + isChecked = true, + isZeroAmount = true, + showEdit = true + ), + ManageAssetItemState( + id = "4", + imageUrl = "https://raw.githubusercontent.com/soramitsu/fearless-utils/master/icons/chains/white/Kusama.svg", + chainName = "TWO-TEE", + assetName = "TWO TEE TO TWO-TWO", + symbol = "TWO", + amount = "333", + fiatAmount = null, + chainId = "", + isChecked = false, + isZeroAmount = true, + showEdit = true ) ) val state = ManageAssetsScreenViewState( selectedChainTitle = "All chains", - assets = mapOf("DOT" to items, "MOVR" to items.filter { it.symbol == "MOVR" }), + assets = mapOf( + "DOT" to items, + "disabled assets" to items.filter { it.isChecked.not() }, + "MOVR" to items.filter { it.symbol == "MOVR" }, + "KSM" to items.filter { it.symbol == "KSM" }, + "WND" to items.filter { it.symbol == "WND" }, + ), searchQuery = null ) ManageAssetItem(items[0], {}, {}, { _, _ -> }) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt index 680fdb2296..91caf08429 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt @@ -128,13 +128,8 @@ class ManageAssetsViewModel @Inject constructor( it.token.configuration.symbol.contains(searchQuery, true) || it.token.configuration.name.orEmpty().contains(searchQuery, true) } - .map { model -> - model.copy(isHidden = currentStates.firstOrNull { - it.assetId == model.token.configuration.id && it.chainId == model.token.configuration.chainId - }?.value == false) - } .sortedWith(compareBy { - "skip" // it.isHidden == true + it.isHidden == true }.thenByDescending { it.fiatAmount.orZero() }.thenByDescending { @@ -142,6 +137,11 @@ class ManageAssetsViewModel @Inject constructor( }.thenBy { it.token.configuration.chainName }) + .map { model -> + model.copy(isHidden = currentStates.firstOrNull { + it.assetId == model.token.configuration.id && it.chainId == model.token.configuration.chainId + }?.value == false) + } val assets = sortedAssets.map { it.toManageAssetItemState() @@ -190,7 +190,7 @@ class ManageAssetsViewModel @Inject constructor( assetName = token.configuration.name, symbol = token.configuration.symbol.uppercase(), amount = available.orZero().formatCrypto(), - fiatAmount = getAsFiatWithCurrency(available) ?: "${token.fiatSymbol.orEmpty()}0", + fiatAmount = getAsFiatWithCurrency(available) ?: "${token.fiatSymbol.orEmpty()}0".takeIf { token.configuration.priceId != null || token.configuration.priceProvider != null }, chainId = token.configuration.chainId, isChecked = isHidden != true, isZeroAmount = available.orZero().isZero(), From efec9d0fc82c44251ecae6c30b6ed84eef595c81 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 10 Apr 2024 22:07:31 +0500 Subject: [PATCH 012/100] FLW-4525 Hide all assets. We should show alert and sumimasen text --- .../impl/presentation/common/AssetsList.kt | 78 ++++++++++++------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt index 3bbc9c0183..e94d6ae5bd 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt @@ -2,8 +2,8 @@ package jp.co.soramitsu.wallet.impl.presentation.common import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState @@ -15,12 +15,15 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import jp.co.soramitsu.common.compose.component.ActionItemType import jp.co.soramitsu.common.compose.component.B0 +import jp.co.soramitsu.common.compose.component.GradientIcon +import jp.co.soramitsu.common.compose.component.H3 import jp.co.soramitsu.common.compose.component.MarginVertical import jp.co.soramitsu.common.compose.component.SwipeState +import jp.co.soramitsu.common.compose.theme.alertYellow +import jp.co.soramitsu.common.compose.theme.white50 import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId @@ -40,29 +43,28 @@ fun AssetsList( header: (@Composable () -> Unit)? = null, footer: (@Composable () -> Unit)? = null ) { - LazyColumn( - state = listState, - verticalArrangement = Arrangement.spacedBy(8.dp), - contentPadding = PaddingValues(top = 8.dp) - ) { - if (header != null) { - item { header() } + if (data.assets.isEmpty()) { + Column { + MarginVertical(margin = 8.dp) + header?.invoke() + Box( + modifier = Modifier.weight(1f), + contentAlignment = Alignment.Center + ) { + EmptyAssetsContent() + } + footer?.invoke() + MarginVertical(margin = 80.dp) } - if (data.assets.isEmpty()) { - item { - Box( - modifier = Modifier - .defaultMinSize(minHeight = 48.dp) - .fillMaxWidth(), - contentAlignment = Alignment.Center - ) { - B0( - text = stringResource(id = R.string.wallet_all_assets_hidden), - textAlign = TextAlign.Center - ) - } + } else { + LazyColumn( + state = listState, + verticalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(top = 8.dp) + ) { + if (header != null) { + item { header() } } - } else { items(data.assets, key = { it.key }) { assetState -> SwipeableAssetListItem( assetState = assetState, @@ -70,10 +72,32 @@ fun AssetsList( actionItemClicked = callback::actionItemClicked ) } + if (footer != null) { + item { footer() } + } + item { MarginVertical(margin = 80.dp) } } - if (footer != null) { - item { footer() } - } - item { MarginVertical(margin = 80.dp) } + } +} + +@Composable +fun EmptyAssetsContent() { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + GradientIcon( + iconRes = R.drawable.ic_alert_24, + color = alertYellow, + modifier = Modifier.align(Alignment.CenterHorizontally), + contentPadding = PaddingValues(bottom = 4.dp) + ) + + H3(text = stringResource(id = R.string.common_search_assets_alert_title)) + B0( + text = stringResource(id = R.string.wallet_all_assets_hidden), + color = white50 + ) } } From d17077a48e5a47683995fd5a56ebc913aed8305f Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 10 Apr 2024 22:42:50 +0500 Subject: [PATCH 013/100] FLW-4527 On favourite screen we can see all networks and assets --- .../impl/presentation/balance/list/BalanceListViewModel.kt | 2 +- .../impl/presentation/manageassets/ManageAssetsViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index ac4fa0ba42..ef8585f4a0 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -239,7 +239,7 @@ class BalanceListViewModel @Inject constructor( val filteredAssets = assets .filter { it.asset.enabled != false && - (selectedChainId == null || + ((selectedChainId == null && filter == ChainSelectorViewStateWithFilters.Filter.All) || it.asset.token.configuration.chainId == selectedChainId || it.asset.token.configuration.chainId in filteredChains.map { it.id }) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt index 91caf08429..22abe724f6 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsViewModel.kt @@ -80,7 +80,7 @@ class ManageAssetsViewModel @Inject constructor( } assets.filter { - selectedChainId == null || + (selectedChainId == null && filter == ChainSelectorViewStateWithFilters.Filter.All) || it.asset.token.configuration.chainId == selectedChainId || it.asset.token.configuration.chainId in filteredChains.map { it.id } } From 0446a8c899bdf7e9a47898a2ca30df67202983e6 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 11 Apr 2024 13:37:09 +0500 Subject: [PATCH 014/100] SendSetupViewModel state flow update --- .../send/setup/SendSetupViewModel.kt | 156 +++++++++++------- 1 file changed, 93 insertions(+), 63 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt index 522bc23022..fae584bd17 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt @@ -150,9 +150,10 @@ class SendSetupViewModel @Inject constructor( } private val defaultAddressInputState = AddressInputState( - title = resourceManager.getString(R.string.send_fund), - "", - R.drawable.ic_address_placeholder + title = resourceManager.getString(R.string.send_to), + input = "", + image = R.drawable.ic_address_placeholder, + editable = false ) private val defaultAmountInputState = AmountInputViewState( @@ -389,66 +390,7 @@ class SendSetupViewModel @Inject constructor( private val sendAllToggleState: MutableStateFlow = MutableStateFlow(ToggleState.INITIAL) private var existentialDepositCheckJob: Job? = null - val state = combine( - selectedChain, - addressInputTrimmedFlow, - chainSelectorStateFlow, - amountInputViewState, - feeInfoViewStateFlow, - warningInfoStateFlow, - buttonStateFlow, - isSoftKeyboardOpenFlow, - lockInputFlow, - assetFlow, - sendAllToggleState - ) { chain, address, chainSelectorState, amountInputState, feeInfoState, warningInfoState, buttonState, isSoftKeyboardOpen, isInputLocked, asset, sendAllState -> - val isAddressValid = when (chain) { - null -> false - else -> walletInteractor.validateSendAddress(chain.id, address) - } - - confirmedValidations.clear() - - val quickAmountInputValues = if (asset?.token?.configuration?.currencyId == bokoloCashTokenId) { - emptyList() - } else { - QuickAmountInput.values().toList() - } - - val isHistorySupportedByChain = chain?.externalApi?.history != null - - val existentialDeposit = asset?.token?.configuration?.let { existentialDepositUseCase(it) }.orZero() - val sendAllAllowed = existentialDeposit > BigInteger.ZERO - - SendSetupViewState( - toolbarState = toolbarViewState, - addressInputState = AddressInputState( - title = resourceManager.getString(R.string.send_to), - input = address, - image = when { - isAddressValid.not() -> R.drawable.ic_address_placeholder - else -> addressIconGenerator.createAddressIcon( - chain?.isEthereumBased == true, - address, - AddressIconGenerator.SIZE_BIG - ) - }, - editable = false, - showClear = isInputLocked.not() - ), - chainSelectorState = chainSelectorState, - amountInputState = amountInputState, - feeInfoState = feeInfoState, - warningInfoState = warningInfoState, - buttonState = buttonState, - isSoftKeyboardOpen = isSoftKeyboardOpen, - isInputLocked = isInputLocked, - quickAmountInputValues = quickAmountInputValues, - isHistoryAvailable = isHistorySupportedByChain, - sendAllChecked = sendAllState in listOf(ToggleState.CHECKED, ToggleState.CONFIRMED), - sendAllAllowed = sendAllAllowed - ) - }.stateIn(viewModelScope, SharingStarted.Eagerly, defaultState) + val state = MutableStateFlow(defaultState) init { sharedState.clear() @@ -460,9 +402,97 @@ class SendSetupViewModel @Inject constructor( sharedState.update(payload.chainId, payload.chainAssetId) } initSendToAddress?.let { sharedState.updateAddress(it) } + + state.onEach { + confirmedValidations.clear() + }.launchIn(this) + sharedState.addressFlow.onEach { it?.let { addressInputFlow.value = it } }.launchIn(this) + + subscribeScreenState() + } + + private fun subscribeScreenState() { + chainSelectorStateFlow.onEach { + state.value = state.value.copy(chainSelectorState = it) + }.launchIn(this) + + amountInputViewState.onEach { + state.value = state.value.copy(amountInputState = it) + }.launchIn(this) + + feeInfoViewStateFlow.onEach { + state.value = state.value.copy(feeInfoState = it) + }.launchIn(this) + + warningInfoStateFlow.onEach { + state.value = state.value.copy(warningInfoState = it) + }.launchIn(this) + + buttonStateFlow.onEach { + state.value = state.value.copy(buttonState = it) + }.launchIn(this) + + isSoftKeyboardOpenFlow.onEach { + state.value = state.value.copy(isSoftKeyboardOpen = it) + }.launchIn(this) + + sendAllToggleState.onEach { + state.value = state.value.copy(sendAllChecked = it in listOf(ToggleState.CHECKED, ToggleState.CONFIRMED)) + }.launchIn(this) + + lockInputFlow.onEach { isInputLocked -> + state.value = state.value.copy( + isInputLocked = isInputLocked, + addressInputState = state.value.addressInputState.copy(showClear = isInputLocked.not()) + ) + }.launchIn(this) + + assetFlow.onEach { asset -> + val quickAmountInputValues = if (asset?.token?.configuration?.currencyId == bokoloCashTokenId) { + emptyList() + } else { + QuickAmountInput.entries + } + + val existentialDeposit = asset?.token?.configuration?.let { existentialDepositUseCase(it) }.orZero() + val sendAllAllowed = existentialDeposit > BigInteger.ZERO + + state.value = state.value.copy( + quickAmountInputValues = quickAmountInputValues, + sendAllAllowed = sendAllAllowed + ) + }.launchIn(this) + + combine( + selectedChain, + addressInputTrimmedFlow + ) { chain, address -> + val isAddressValid = when (chain) { + null -> false + else -> walletInteractor.validateSendAddress(chain.id, address) + } + + val image: Any = if (isAddressValid.not()) { + R.drawable.ic_address_placeholder + } else { + addressIconGenerator.createAddressIcon( + chain?.isEthereumBased == true, + address, + AddressIconGenerator.SIZE_BIG + ) + } + + state.value = state.value.copy( + addressInputState = state.value.addressInputState.copy( + input = address, + image = image + ), + isHistoryAvailable = chain?.externalApi?.history != null + ) + }.launchIn(this) } private fun observeExistentialDeposit(showMaxInput: Boolean) { From 4cd114aece11b2604e164a3482c731680ce87935 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 11 Apr 2024 15:23:05 +0500 Subject: [PATCH 015/100] hide asset from main list logic update --- .../domain/interfaces/WalletRepository.kt | 7 ---- .../data/repository/WalletRepositoryImpl.kt | 23 ----------- .../impl/domain/WalletInteractorImpl.kt | 38 ++----------------- .../balance/list/BalanceListViewModel.kt | 10 +++-- .../presentation/balance/list/WalletScreen.kt | 2 +- .../presentation/balance/list/WalletState.kt | 7 +++- .../impl/presentation/common/AssetsList.kt | 2 + .../common/SwipeableAssetListItem.kt | 15 +++++--- 8 files changed, 28 insertions(+), 76 deletions(-) diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt index 31580b1e7a..52cfa6f7c0 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt @@ -43,13 +43,6 @@ interface WalletRepository { minSupportedVersion: String? ): Asset? - suspend fun updateAssetHidden( - metaId: Long, - accountId: AccountId, - isHidden: Boolean, - chainAsset: CoreAsset - ) - suspend fun getTransferFee( chain: Chain, transfer: Transfer, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index eec18664f7..e4c109b7a3 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -327,29 +327,6 @@ class WalletRepositoryImpl( return assetLocal?.let { mapAssetLocalToAsset(it, chainAsset, minSupportedVersion) } } - override suspend fun updateAssetHidden( - metaId: Long, - accountId: AccountId, - isHidden: Boolean, - chainAsset: CoreAsset - ) { - val tokenPriceId = - chainAsset.priceProvider?.id?.takeIf { selectedFiat.isUsd() } ?: chainAsset.priceId - val updateItems = listOf( - AssetUpdateItem( - metaId = metaId, - chainId = chainAsset.chainId, - accountId = accountId, - id = chainAsset.id, - sortIndex = Int.MAX_VALUE, // Int.MAX_VALUE on sorting because we don't use it anymore - just random value - enabled = !isHidden, - tokenPriceId = tokenPriceId - ) - ) - - assetCache.updateAsset(updateItems) - } - override suspend fun updateAssetsHidden(state: List) { assetCache.updateAsset(state) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index f016a3b096..a8cccd9d04 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -419,10 +419,6 @@ class WalletInteractorImpl( override suspend fun getChainAddressForSelectedMetaAccount(chainId: ChainId) = getSelectedMetaAccount().address(getChain(chainId)) - override suspend fun markAssetAsHidden(chainId: ChainId, chainAssetId: String) { - manageAssetHidden(chainId, chainAssetId, true) - } - override suspend fun updateAssetsHiddenState(state: List) { val wallet = getSelectedMetaAccount() val updateItems = state.mapNotNull { @@ -444,38 +440,12 @@ class WalletInteractorImpl( walletRepository.updateAssetsHidden(updateItems) } - override suspend fun markAssetAsShown(chainId: ChainId, chainAssetId: String) { - manageAssetHidden(chainId, chainAssetId, false) + override suspend fun markAssetAsHidden(chainId: ChainId, chainAssetId: String) { + updateAssetsHiddenState(listOf(AssetBooleanState(chainId, chainAssetId, false))) } - private suspend fun manageAssetHidden( - chainId: ChainId, - chainAssetId: String, - isHidden: Boolean - ) { - val metaAccount = accountRepository.getSelectedMetaAccount() - val chain = chainsRepository.getChain(chainId) - val accountId = metaAccount.accountId(chain) - val chainAsset = chain.assetsById[chainAssetId] ?: return - - val chainsWithAsset = chainsRepository.getChains().filter { chainItem -> - chainItem.assets.any { it.symbol == chainAsset.symbol } - } - - val assetsToManage = chainsWithAsset.map { - it.assets.filter { it.symbol == chainAsset.symbol } - }.flatten() - - accountId?.let { - assetsToManage.forEach { - walletRepository.updateAssetHidden( - chainAsset = it, - metaId = metaAccount.id, - accountId = accountId, - isHidden = isHidden - ) - } - } + override suspend fun markAssetAsShown(chainId: ChainId, chainAssetId: String) { + updateAssetsHiddenState(listOf(AssetBooleanState(chainId, chainAssetId, true))) } override fun selectedMetaAccountFlow(): Flow { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index ef8585f4a0..1229419dd3 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -359,14 +359,18 @@ class BalanceListViewModel @Inject constructor( } private val assetTypeState = combine( + selectedChainId, assetTypeSelectorState, assetStates, nftInteractor.nftFiltersFlow(), - createNFTCollectionScreenViewsFlow() - ) { selectorState, assetStates, filters, (pageViews, screenLayout) -> + createNFTCollectionScreenViewsFlow(), + ) { selectedChainId, selectorState, assetStates, filters, (pageViews, screenLayout) -> when (selectorState.currentSelection) { AssetType.Currencies -> { - WalletAssetsState.Assets(assetStates) + WalletAssetsState.Assets( + assets = assetStates, + isHideVisible = selectedChainId != null + ) } AssetType.NFTs -> { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt index 141f850233..2b87296f9c 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt @@ -318,7 +318,7 @@ private fun PreviewWalletScreen() { AssetType.Currencies, listOf(AssetType.Currencies, AssetType.NFTs) ), - assetsState = WalletAssetsState.Assets(assets), + assetsState = WalletAssetsState.Assets(assets, isHideVisible = true), balance = AssetBalanceViewState( "TRANSFERABLE BALANCE", "ADDRESS", diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt index 7d1fd1bd5a..ba450f83e5 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt @@ -23,7 +23,7 @@ data class WalletState( companion object { val default = WalletState( multiToggleButtonState = MultiToggleButtonState(AssetType.Currencies, listOf(AssetType.Currencies, AssetType.NFTs)), - assetsState = WalletAssetsState.Assets(emptyList()), + assetsState = WalletAssetsState.Assets(emptyList(), isHideVisible = true), balance = AssetBalanceViewState("", "", false, ChangeBalanceViewState("", "")), hasNetworkIssues = false, soraCardState = null, @@ -35,7 +35,10 @@ data class WalletState( } sealed interface WalletAssetsState { - data class Assets(override val assets: List): WalletAssetsState, AssetListState(assets) + data class Assets( + override val assets: List, + val isHideVisible: Boolean + ): WalletAssetsState, AssetListState(assets) @JvmInline value class NftAssets( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt index e94d6ae5bd..ec69bd7082 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt @@ -27,6 +27,7 @@ import jp.co.soramitsu.common.compose.theme.white50 import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId +import jp.co.soramitsu.wallet.impl.presentation.balance.list.WalletAssetsState interface AssetsListInterface { @OptIn(ExperimentalMaterialApi::class) @@ -68,6 +69,7 @@ fun AssetsList( items(data.assets, key = { it.key }) { assetState -> SwipeableAssetListItem( assetState = assetState, + isHideVisible = (data as? WalletAssetsState.Assets)?.isHideVisible == true, assetClicked = callback::assetClicked, actionItemClicked = callback::actionItemClicked ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/SwipeableAssetListItem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/SwipeableAssetListItem.kt index 3108f10cd2..acc939c0f1 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/SwipeableAssetListItem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/SwipeableAssetListItem.kt @@ -21,6 +21,7 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId @Composable fun SwipeableAssetListItem( assetState: AssetListItemViewState, + isHideVisible: Boolean, assetClicked: (AssetListItemViewState) -> Unit, actionItemClicked: (actionType: ActionItemType, chainId: ChainId, chainAssetId: String, swipeableState: SwipeableState) -> Unit ) { @@ -33,7 +34,7 @@ fun SwipeableAssetListItem( val swipeBoxViewState = remember { SwipeBoxViewState( leftStateWidth = 170.dp, - rightStateWidth = 90.dp + rightStateWidth = if (isHideVisible) 90.dp else 0.dp ) } @@ -57,11 +58,13 @@ fun SwipeableAssetListItem( } }, rightContent = { - BackgroundCornered { - ActionBar( - state = rightBarActionViewState, - onItemClick = ::onItemClick - ) + if (isHideVisible) { + BackgroundCornered { + ActionBar( + state = rightBarActionViewState, + onItemClick = ::onItemClick + ) + } } } ) From db3ad3ae214d48d274609e3539e690392e23fd02 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 11 Apr 2024 21:53:07 +0500 Subject: [PATCH 016/100] update versions --- app/build.gradle | 4 ++-- build.gradle | 2 +- gradle/libs.versions.toml | 26 ++++++++++++------------ gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7f3ef17680..83ef89f8bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,8 +130,8 @@ android { resources.excludes.add("META-INF/*") } - configurations{ - all*.exclude module: 'bcprov-jdk15on' + configurations.configureEach { + exclude group: "org.bouncycastle", module: "bcprov-jdk15on" } } diff --git a/build.gradle b/build.gradle index 71227e844a..21c92871a6 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { minSdkVersion = 24 targetSdkVersion = 33 - composeCompilerVersion = '1.5.9' + composeCompilerVersion = '1.5.11' withoutBasic = { exclude group: 'jp.co.soramitsu.xnetworking', module: 'basic' } withoutJna = { exclude group: 'net.java.dev.jna' } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fdad1dad87..5a70148e25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,16 +1,16 @@ [versions] -accompanistVersion = "0.28.0" +accompanistVersion = "0.34.0" activityCompose = "1.8.2" -android_plugin = "8.2.2" +android_plugin = "8.3.2" appcompat = "1.6.1" architectureComponentVersion = "2.7.0" beaconVersion = "3.2.4" biometricVersion = "1.1.0" -bouncyCastleVersion = "1.77" +bouncyCastleVersion = "1.78" cardViewVersion = "1.0.0" -coilVersion = "2.2.2" -compose = "1.6.3" -composeCompiler = "1.5.10" +coilVersion = "2.6.0" +compose = "1.6.5" +composeCompiler = "1.5.11" composeShimmer = "1.0.4" composeThemeAdapter = "1.2.1" constraintlayoutComposeVersion = "1.0.1" @@ -20,17 +20,17 @@ coroutines = "1.8.0" customview = "1.2.0-alpha02" customviewPoolingcontainer = "1.0.0" dagger = "2.49" -detekt = "1.23.0" +detekt = "1.23.6" firebaseAppdistributionGradle = "4.2.0" fragmentKtx = "1.6.2" googleServices = "4.4.1" gson = "2.10.1" hiltNavComposeVersion = "1.2.0" insetterVersion = "0.5.0" -jna = "5.9.0" +jna = "5.14.0" junit = "4.13.2" junitVersion = "1.1.5" -kotlin = "1.9.22" +kotlin = "1.9.23" kotlinxSerializationjson = "1.6.3" legacySupportV4 = "1.0.0" material = "1.11.0" @@ -40,8 +40,8 @@ mockitoInlineVersion = "5.2.0" navControllerVersion = "2.7.7" okhttpVersion = "4.11.0" opencsv = "5.7.1" -orgJacocoCore = "0.8.8" -playPublisher = "3.8.4" +orgJacocoCore = "0.8.12" +playPublisher = "3.9.1" progressButtonsVersion = "2.1.0" recyclerviewVersion = "1.3.2" retrofit = "2.9.0" @@ -51,14 +51,14 @@ runner = "1.5.2" sharedFeaturesVersion = "1.1.1.28-FLW" shimmerVersion = "0.5.0" sonarqubeGradlePlugin = "3.3" -soraUiCore = "0.2.20" +soraUiCore = "0.2.22" storiesVersion = "3.0.1" walletconnectBom = "1.18.0" web3j = "4.8.8-android" wsVersion = "2.14" xNetworking = "0.2.5-temp7" zxingEmbeddedVersion = "4.3.0" -zxingVersion = "3.5.1" +zxingVersion = "3.5.3" [libraries] appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c657399023..a781b73d47 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Aug 04 19:19:31 YEKT 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 3d15b4c77882a4789ccab2dec261ea42fa6c9fff Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 11 Apr 2024 22:02:56 +0500 Subject: [PATCH 017/100] fix detekt --- .../jp/co/soramitsu/nft/impl/presentation/NFTFlowFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/NFTFlowFragment.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/NFTFlowFragment.kt index bd58d4c648..77a5e3c1fc 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/NFTFlowFragment.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/NFTFlowFragment.kt @@ -190,7 +190,9 @@ class NFTFlowFragment : BaseComposeBottomSheetDialogFragment() title = loadingState.data.first.retrieveString(), navigationIconResId = loadingState.data.second, onNavigationClick = remember { - { viewModel.onNavigationClick() } + { + viewModel.onNavigationClick() + } } ) From d14f60530a06d495025017b2dc666e23b04eecc9 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 12 Apr 2024 13:21:32 +0500 Subject: [PATCH 018/100] FLW-4534 Hide all assets. Restart the App. There is overlapping icons --- .../soramitsu/wallet/impl/presentation/common/AssetsList.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt index ec69bd7082..168ab72795 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/AssetsList.kt @@ -66,10 +66,11 @@ fun AssetsList( if (header != null) { item { header() } } - items(data.assets, key = { it.key }) { assetState -> + val isHideVisible = (data as? WalletAssetsState.Assets)?.isHideVisible == true + items(data.assets, key = { "${it.key}$isHideVisible" }) { assetState -> SwipeableAssetListItem( assetState = assetState, - isHideVisible = (data as? WalletAssetsState.Assets)?.isHideVisible == true, + isHideVisible = isHideVisible, assetClicked = callback::assetClicked, actionItemClicked = callback::actionItemClicked ) From 56debb965bbcf5b990bba127ded4ae0af9ce817e Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 12 Apr 2024 13:46:59 +0500 Subject: [PATCH 019/100] FLW-4535 There is a delay when we are creating new wallet --- .../mnemonic/confirm/ConfirmMnemonicViewModel.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt index 5dd02b9483..03abfc1031 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt @@ -139,9 +139,10 @@ class ConfirmMnemonicViewModel @Inject constructor( val result = interactor.createAccount(accountName, mnemonicString, cryptoType, substrateDerivationPath, ethereumDerivationPath, isBackedUp) if (result.isSuccess) { - setupNewAccountAssetsVisibility() - continueBasedOnCodeStatus() + kotlinx.coroutines.MainScope().launch { + setupNewAccountAssetsVisibility() + } } else { showError(result.requireException()) } From b5b7f4923461ac21cfc33cfa36bcec5ed450776b Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 12 Apr 2024 19:51:19 +0500 Subject: [PATCH 020/100] =?UTF-8?q?FLW-4537=20Swipe=20close=20doesn?= =?UTF-8?q?=E2=80=99t=20work?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/presentation/manageassets/ManageAssetsFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt index 540398ad85..84925e01fc 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsFragment.kt @@ -34,7 +34,7 @@ class ManageAssetsFragment : BaseComposeBottomSheetDialogFragment) { behavior.state = BottomSheetBehavior.STATE_EXPANDED - behavior.isHideable = false + behavior.isHideable = true behavior.skipCollapsed = true } } From 90a8955e1d4442cdbd1e351f0bb1522ec28feece Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 12 Apr 2024 23:47:58 +0500 Subject: [PATCH 021/100] transaction detail logic update --- .../soramitsu/app/root/navigation/Navigator.kt | 4 ++-- .../wallet/impl/presentation/WalletRouter.kt | 2 +- .../balance/detail/BalanceDetailViewModel.kt | 18 ++++-------------- .../transfer/TransactionDetailViewModel.kt | 10 +++++----- .../detail/transfer/TransferDetailFragment.kt | 8 ++++---- .../history/mixin/TransactionHistoryMixin.kt | 3 +-- .../mixin/TransactionHistoryProvider.kt | 6 +++--- 7 files changed, 20 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt index 900f75cd3d..ddbd497b94 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt @@ -1042,8 +1042,8 @@ class Navigator : navController?.popBackStack() } - override fun openTransferDetail(transaction: OperationParcelizeModel.Transfer, assetPayload: AssetPayload, chainHistoryType: Chain.ExternalApi.Section.Type?) { - val bundle = TransferDetailFragment.getBundle(transaction, assetPayload, chainHistoryType) + override fun openTransferDetail(transaction: OperationParcelizeModel.Transfer, assetPayload: AssetPayload, chainExplorerType: Chain.Explorer.Type?) { + val bundle = TransferDetailFragment.getBundle(transaction, assetPayload, chainExplorerType) navController?.navigate(R.id.open_transfer_detail, bundle) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt index b417e1d051..70a6ccc270 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt @@ -89,7 +89,7 @@ interface WalletRouter : SecureRouter, WalletRouterApi { fun finishSendFlow() - fun openTransferDetail(transaction: OperationParcelizeModel.Transfer, assetPayload: AssetPayload, chainHistoryType: Chain.ExternalApi.Section.Type?) + fun openTransferDetail(transaction: OperationParcelizeModel.Transfer, assetPayload: AssetPayload, chainExplorerType: Chain.Explorer.Type?) fun openSwapDetail(operation: OperationParcelizeModel.Swap) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt index c09d25599d..1af8864e2c 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt @@ -34,7 +34,6 @@ import jp.co.soramitsu.common.utils.formatFiat import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.feature_wallet_impl.R -import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.soraMainChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.soraTestChainId @@ -487,19 +486,10 @@ class BalanceDetailViewModel @Inject constructor( } override fun transactionClicked(transactionModel: OperationModel) { - launch { - val chain = interactor.getChain(assetPayload.value.chainId) - val chainHistoryType: Chain.ExternalApi.Section.Type? = chain.externalApi?.history?.type - - transactionHistoryProvider.transactionClicked( - transactionModel = transactionModel, - assetPayload = AssetPayload( - chainId = assetPayload.value.chainId, - chainAssetId = assetPayload.value.chainAssetId - ), - chainHistoryType = chainHistoryType - ) - } + transactionHistoryProvider.transactionClicked( + transactionModel = transactionModel, + assetPayload = assetPayload.value + ) } override fun tableItemClicked(itemId: Int) { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt index 9a33951f07..b2c5088383 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt @@ -45,7 +45,7 @@ class TransactionDetailViewModel @Inject constructor( val operation = savedStateHandle.get(KEY_TRANSACTION)!! val assetPayload = savedStateHandle.get(KEY_ASSET_PAYLOAD)!! - val historyType = savedStateHandle.get(KEY_HISTORY_TYPE)!! + val explorerType = savedStateHandle.get(KEY_EXPLORER_TYPE) private val _showExternalViewEvent = MutableLiveData>() val showExternalTransactionActionsEvent: LiveData> = _showExternalViewEvent @@ -62,10 +62,10 @@ class TransactionDetailViewModel @Inject constructor( private val chainExplorers = flow { emit(chainRegistry.getChain(assetPayload.chainId).explorers) }.share() - fun getSupportedExplorers(historyType: Chain.ExternalApi.Section.Type, value: String): Map { - val explorerUrlType: BlockExplorerUrlBuilder.Type = when (historyType) { - Chain.ExternalApi.Section.Type.ETHERSCAN -> BlockExplorerUrlBuilder.Type.TX - Chain.ExternalApi.Section.Type.REEF -> BlockExplorerUrlBuilder.Type.TRANSFER + fun getSupportedExplorers(explorerType: Chain.Explorer.Type, value: String): Map { + val explorerUrlType: BlockExplorerUrlBuilder.Type = when (explorerType) { + Chain.Explorer.Type.ETHERSCAN -> BlockExplorerUrlBuilder.Type.TX + Chain.Explorer.Type.REEF -> BlockExplorerUrlBuilder.Type.TRANSFER else -> BlockExplorerUrlBuilder.Type.EXTRINSIC } return chainExplorers.replayCache.firstOrNull()?.getSupportedExplorers(explorerUrlType, value).orEmpty() diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt index 4f09288501..9dfcfc47ff 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt @@ -25,17 +25,17 @@ import jp.co.soramitsu.wallet.impl.presentation.model.OperationStatusAppearance const val KEY_TRANSACTION = "KEY_DRAFT" const val KEY_ASSET_PAYLOAD = "KEY_ASSET_PAYLOAD" -const val KEY_HISTORY_TYPE = "KEY_HISTORY_TYPE" +const val KEY_EXPLORER_TYPE = "KEY_EXPLORER_TYPE" @AndroidEntryPoint class TransferDetailFragment : BaseFragment(R.layout.fragment_transfer_details) { companion object { - fun getBundle(operation: OperationParcelizeModel.Transfer, assetPayload: AssetPayload, chainHistoryType: Chain.ExternalApi.Section.Type?) = + fun getBundle(operation: OperationParcelizeModel.Transfer, assetPayload: AssetPayload, chainExplorerType: Chain.Explorer.Type?) = bundleOf( KEY_TRANSACTION to operation, KEY_ASSET_PAYLOAD to assetPayload, - KEY_HISTORY_TYPE to chainHistoryType + KEY_EXPLORER_TYPE to chainExplorerType ) } @@ -156,7 +156,7 @@ class TransferDetailFragment : BaseFragment(R.layout showExternalActionsSheet( copyLabelRes = R.string.transaction_details_copy_hash, value = hash, - explorers = viewModel.getSupportedExplorers(viewModel.historyType, hash), + explorers = viewModel.explorerType?.let { viewModel.getSupportedExplorers(it, hash) }.orEmpty(), externalViewCallback = viewModel::openUrl ) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryMixin.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryMixin.kt index 5a0fe35a0d..aac4686f99 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryMixin.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryMixin.kt @@ -29,8 +29,7 @@ interface TransactionHistoryUi { fun transactionClicked( transactionModel: OperationModel, - assetPayload: AssetPayload, - chainHistoryType: Chain.ExternalApi.Section.Type? + assetPayload: AssetPayload ) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryProvider.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryProvider.kt index d355c45132..87b1931ab0 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryProvider.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/history/mixin/TransactionHistoryProvider.kt @@ -207,8 +207,7 @@ class TransactionHistoryProvider( override fun transactionClicked( transactionModel: OperationModel, - assetPayload: AssetPayload, - chainHistoryType: Chain.ExternalApi.Section.Type? + assetPayload: AssetPayload ) { launch { val operations = currentData @@ -217,11 +216,12 @@ class TransactionHistoryProvider( val chain = walletInteractor.getChain(assetPayload.chainId) val utilityAsset = chain.assets.firstOrNull { it.isUtility } + val chainExplorerType: Chain.Explorer.Type? = chain.explorers.firstOrNull()?.type withContext(Dispatchers.Main) { when (val operation = mapOperationToParcel(clickedOperation, resourceManager, utilityAsset)) { is OperationParcelizeModel.Transfer -> { - router.openTransferDetail(operation, assetPayload, chainHistoryType) + router.openTransferDetail(operation, assetPayload, chainExplorerType) } is OperationParcelizeModel.Extrinsic -> { From 216bfacf9f595c74481efeb6b50c96e50c875d3b Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 25 Apr 2024 11:24:27 +0500 Subject: [PATCH 022/100] runtime version request updates --- gradle/libs.versions.toml | 2 +- .../runtime/multiNetwork/ChainRegistry.kt | 6 +- .../runtime/RuntimeSubscriptionPool.kt | 4 +- .../runtime/RuntimeVersionSubscription.kt | 56 ++++++++++++++++++- .../runtime/SubscribeRuntimeVersionRequest.kt | 8 --- 5 files changed, 61 insertions(+), 15 deletions(-) delete mode 100644 runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/SubscribeRuntimeVersionRequest.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5a70148e25..45b3315df4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,7 +48,7 @@ retrofit = "2.9.0" roomVersion = "2.6.1" rules = "1.5.0" runner = "1.5.2" -sharedFeaturesVersion = "1.1.1.28-FLW" +sharedFeaturesVersion = "1.1.1.30-FLW" shimmerVersion = "0.5.0" sonarqubeGradlePlugin = "3.3" soraUiCore = "0.2.22" diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 364ce67809..9464aeeb3c 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -25,7 +25,6 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.mapNodeLocalToNode import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.NodeId -import jp.co.soramitsu.runtime.multiNetwork.chain.model.polkadotChainId import jp.co.soramitsu.runtime.multiNetwork.connection.ConnectionPool import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumConnectionPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProvider @@ -128,12 +127,13 @@ class ChainRegistry @Inject constructor( notifyNodeSwitched(NodeId(chainId to newNodeUrl)) } ) + val runtimeProvider = runtimeProviderPool.setupRuntimeProvider(chain) runtimeSubscriptionPool.setupRuntimeSubscription( chain, - connection + connection, + runtimeProvider ) runtimeSyncService.registerChain(chain) - runtimeProviderPool.setupRuntimeProvider(chain) }.onFailure { networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) } .onSuccess { networkStateMixin.notifyChainSyncSuccess( diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt index 6c805cb26c..e863f6fdcb 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt @@ -15,9 +15,9 @@ class RuntimeSubscriptionPool( fun getRuntimeSubscription(chainId: String) = pool.getValue(chainId) - fun setupRuntimeSubscription(chain: Chain, connection: ChainConnection): RuntimeVersionSubscription { + fun setupRuntimeSubscription(chain: Chain, connection: ChainConnection, runtimeProvider: RuntimeProvider): RuntimeVersionSubscription { return pool.getOrPut(chain.id) { - RuntimeVersionSubscription(chain.id, connection, chainDao, runtimeSyncService) + RuntimeVersionSubscription(chain.id, connection, chainDao, runtimeSyncService, runtimeProvider) } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt index 04a3045e7d..fe8761a851 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt @@ -1,11 +1,25 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime import android.util.Log +import jp.co.soramitsu.common.data.network.runtime.binding.bindNumber +import jp.co.soramitsu.common.data.network.runtime.binding.requireType +import jp.co.soramitsu.common.utils.constant +import jp.co.soramitsu.common.utils.system import jp.co.soramitsu.core.runtime.ChainConnection import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.runtime.multiNetwork.ChainState import jp.co.soramitsu.runtime.multiNetwork.ChainsStateTracker +import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot +import jp.co.soramitsu.shared_utils.runtime.definitions.types.composite.Struct +import jp.co.soramitsu.shared_utils.runtime.definitions.types.fromByteArrayOrNull +import jp.co.soramitsu.shared_utils.wsrpc.executeAsync +import jp.co.soramitsu.shared_utils.wsrpc.mappers.nonNull +import jp.co.soramitsu.shared_utils.wsrpc.mappers.pojo +import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.RuntimeVersion +import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.RuntimeVersionRequest +import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.StateRuntimeVersionRequest import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.SubscribeRuntimeVersionRequest +import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.SubscribeStateRuntimeVersionRequest import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.runtimeVersionChange import jp.co.soramitsu.shared_utils.wsrpc.state.SocketStateMachine import jp.co.soramitsu.shared_utils.wsrpc.subscriptionFlow @@ -13,6 +27,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -23,7 +38,8 @@ class RuntimeVersionSubscription( private val chainId: String, connection: ChainConnection, private val chainDao: ChainDao, - private val runtimeSyncService: RuntimeSyncService + private val runtimeSyncService: RuntimeSyncService, + runtimeProvider: RuntimeProvider ) : CoroutineScope by CoroutineScope(Dispatchers.IO + SupervisorJob()) { init { @@ -34,6 +50,23 @@ class RuntimeVersionSubscription( connection.state.first { it is SocketStateMachine.State.Connected } connection.socketService.subscriptionFlow(SubscribeRuntimeVersionRequest) .map { it.runtimeVersionChange().specVersion } + .catch { + emitAll( + connection.socketService.subscriptionFlow(SubscribeStateRuntimeVersionRequest) + .map { it.runtimeVersionChange().specVersion } + .catch { + val runtime = runtimeProvider.getOrNullWithTimeout() + + val version = runtime?.getVersionConstant() + ?: connection.getVersionChainRpc() + ?: connection.getVersionStateRpc() + ?: error("Runtime version not obtained") + + emit(version) + } + ) + } + .onEach { runtimeVersionResult -> chainDao.updateRemoteRuntimeVersion( chainId, @@ -61,4 +94,25 @@ class RuntimeVersionSubscription( } } } + + private fun RuntimeSnapshot.getVersionConstant(): Int? = runCatching { + val versionConstant = metadata.system().constant("Version") + val decodedVersion = versionConstant.type?.fromByteArrayOrNull(this, versionConstant.value) + requireType(decodedVersion) + bindNumber(decodedVersion["specVersion"]).toInt() + }.getOrNull() + + private suspend fun ChainConnection.getVersionChainRpc(): Int? = runCatching { + socketService.executeAsync( + request = RuntimeVersionRequest(), + mapper = pojo().nonNull() + ).specVersion + }.getOrNull() + + private suspend fun ChainConnection.getVersionStateRpc(): Int? = runCatching { + socketService.executeAsync( + request = StateRuntimeVersionRequest(), + mapper = pojo().nonNull() + ).specVersion + }.getOrNull() } \ No newline at end of file diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/SubscribeRuntimeVersionRequest.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/SubscribeRuntimeVersionRequest.kt deleted file mode 100644 index 0207e7408c..0000000000 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/SubscribeRuntimeVersionRequest.kt +++ /dev/null @@ -1,8 +0,0 @@ -package jp.co.soramitsu.runtime.multiNetwork.runtime - -import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.RuntimeRequest - -object SubscribeRuntimeVersionRequest : RuntimeRequest( - method = "state_subscribeRuntimeVersion", - params = listOf() -) From dd544fc72eb47dde6e45447394feb4221e9f72d2 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Mon, 15 Apr 2024 13:23:53 +0500 Subject: [PATCH 023/100] FLW-4474 There is no enough space on history between address and amount --- .../balance/detail/TransactionItem.kt | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/TransactionItem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/TransactionItem.kt index ba67d26fdd..b492488f3a 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/TransactionItem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/TransactionItem.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable @@ -82,21 +83,25 @@ fun TransactionItem( ) if (item.type == OperationModel.Type.Transfer) { - val headerModifier = Modifier.constrainAs(header) { - top.linkTo(parent.top) - start.linkTo(imageSpacer.end) - end.linkTo(amount.start) - width = Dimension.fillToConstraints - } + val headerModifier = Modifier + .constrainAs(header) { + top.linkTo(parent.top) + start.linkTo(imageSpacer.end) + end.linkTo(amount.start) + width = Dimension.fillToConstraints + } + .padding(end = 8.dp) B1EllipsizeMiddle( text = item.header, modifier = headerModifier ) } else { - val headerModifier = Modifier.constrainAs(header) { - top.linkTo(parent.top) - start.linkTo(imageSpacer.end) - } + val headerModifier = Modifier + .constrainAs(header) { + top.linkTo(parent.top) + start.linkTo(imageSpacer.end) + } + .padding(end = 8.dp) B1( text = item.header, textAlign = TextAlign.Start, From 6615c2ec8fc5645b39442f0e7b17563aaedda130 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Mon, 15 Apr 2024 18:59:26 +0500 Subject: [PATCH 024/100] warnings clean --- .../wallet/impl/presentation/send/setup/SendSetupViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt index fae584bd17..d3fde705be 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt @@ -64,6 +64,7 @@ import jp.co.soramitsu.wallet.impl.presentation.WalletRouter import jp.co.soramitsu.wallet.impl.presentation.balance.chainselector.ChainSelectScreenContract import jp.co.soramitsu.wallet.impl.presentation.send.SendSharedState import jp.co.soramitsu.wallet.impl.presentation.send.TransferDraft +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -263,6 +264,7 @@ class SendSetupViewModel @Inject constructor( } }.stateIn(this, SharingStarted.Eagerly, defaultAmountInputState) + @OptIn(ExperimentalCoroutinesApi::class) private val feeAmountFlow = combine( addressInputTrimmedFlow, isInputAddressValidFlow, @@ -353,7 +355,7 @@ class SendSetupViewModel @Inject constructor( message = getPhishingMessage(phishing.type), extras = listOf( phishing.name?.let { resourceManager.getString(R.string.username_setup_choose_title) to it }, - phishing.type?.let { resourceManager.getString(R.string.reason) to it.capitalizedName }, + phishing.type.let { resourceManager.getString(R.string.reason) to it.capitalizedName }, phishing.subtype?.let { resourceManager.getString(R.string.scam_additional_stub) to it } ).mapNotNull { it }, isExpanded = isExpanded, From a615d9360694d267df06e3123fc53c6ac4624aff Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 16 Apr 2024 17:02:45 +0500 Subject: [PATCH 025/100] FLW-4480 There is wrong text on the alert on the Polkadot staking --- .../validations/setup/MinimumAmountValidation.kt | 11 ++++++++++- .../scenarios/StakingRelaychainScenarioViewModel.kt | 6 +++++- .../relaychain/StakingRelayChainScenarioInteractor.kt | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt index f8b90f68a6..65f7ef2826 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt @@ -1,8 +1,11 @@ package jp.co.soramitsu.staking.impl.domain.validations.setup +import java.math.BigDecimal import jp.co.soramitsu.common.validation.DefaultFailureLevel import jp.co.soramitsu.common.validation.Validation import jp.co.soramitsu.common.validation.ValidationStatus +import jp.co.soramitsu.runtime.multiNetwork.chain.model.polkadotChainId +import jp.co.soramitsu.staking.impl.presentation.staking.main.scenarios.StakingRelaychainScenarioViewModel import jp.co.soramitsu.staking.impl.scenarios.StakingScenarioInteractor import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks @@ -14,7 +17,13 @@ class MinimumAmountValidation( val assetConfiguration = value.asset.token.configuration val minimumBondInPlanks = stakingScenarioInteractor.getMinimumStake(assetConfiguration) - val minimumBond = assetConfiguration.amountFromPlanks(minimumBondInPlanks) + val minStakeMultiplier: Double = if (assetConfiguration.chainId == polkadotChainId) { + StakingRelaychainScenarioViewModel.POLKADOT_STAKE_EXTRA_MULTIPLIER // 15% increase + } else { + 1.0 + } + + val minimumBond = assetConfiguration.amountFromPlanks(minimumBondInPlanks) * BigDecimal(minStakeMultiplier) // either first time bond or already existing bonded balance val amountToCheckAgainstMinimum = value.bondAmount ?: value.asset.bonded diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt index b744478e44..ecee429d4f 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt @@ -58,6 +58,10 @@ class StakingRelaychainScenarioViewModel( stakingSharedState: StakingSharedState ) : StakingScenarioViewModel { + companion object { + const val STAKE_EXTRA_MULTIPLIER = 1.15 // allow to be not at the bottom of reward list and not be excluded soon + } + override val enteredAmountFlow: MutableStateFlow = MutableStateFlow(BigDecimal.ZERO) private val welcomeStakingValidationSystem = ValidationSystem( @@ -164,7 +168,7 @@ class StakingRelaychainScenarioViewModel( ) { networkInfo, asset -> val minStakeMultiplier: Double = if (networkInfo.shouldUseMinimumStakeMultiplier) { - 1.15 // 15% increase + STAKE_EXTRA_MULTIPLIER // 15% increase } else { 1.0 } diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt index 919029460e..8a78574d31 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt @@ -96,6 +96,7 @@ import jp.co.soramitsu.wallet.impl.domain.model.Asset import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks import jp.co.soramitsu.wallet.impl.domain.validation.EnoughToPayFeesValidation import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -133,6 +134,7 @@ class StakingRelayChainScenarioInteractor( private val walletConstants: WalletConstants ) : StakingScenarioInteractor { + @OptIn(ExperimentalCoroutinesApi::class) override suspend fun observeNetworkInfoState(): Flow { return stakingSharedState.assetWithChain.filter { it.asset.staking == StakingType.RELAYCHAIN } .distinctUntilChanged() From 6f42e67970b2b7c2c26204f65e86b6a0dc5ac524 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 16 Apr 2024 17:46:47 +0500 Subject: [PATCH 026/100] FLW-4542 [AUTO] Crash --- .../impl/data/repository/datasource/AccountDataSourceImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/datasource/AccountDataSourceImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/datasource/AccountDataSourceImpl.kt index 9bd7edaaa1..eb4ca939da 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/datasource/AccountDataSourceImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/datasource/AccountDataSourceImpl.kt @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn @@ -123,7 +123,7 @@ class AccountDataSourceImpl( } } - override suspend fun anyAccountSelected(): Boolean = selectedMetaAccountLocal.first() != null + override suspend fun anyAccountSelected(): Boolean = selectedMetaAccountLocal.firstOrNull() != null override suspend fun saveSelectedAccount(account: Account) = withContext(Dispatchers.Default) { val raw = jsonMapper.toJson(account) From a709ef777efb441f4f7c6d99c58f012ab82a7ca4 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 16 Apr 2024 18:53:53 +0500 Subject: [PATCH 027/100] FLW-4479 There is shimmers on the Polkadot staking and because of that the entered amount is changing --- .../jp/co/soramitsu/staking/api/data/StakingSharedState.kt | 6 +++++- .../impl/presentation/common/StakingAssetSelector.kt | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt b/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt index 0bc4fce1ee..4873c41adf 100644 --- a/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt +++ b/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt @@ -18,6 +18,7 @@ import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository import jp.co.soramitsu.wallet.impl.domain.model.Asset import jp.co.soramitsu.wallet.impl.domain.model.Token import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -65,6 +66,7 @@ class StakingSharedState( private const val DELIMITER = ":" } + @OptIn(ExperimentalCoroutinesApi::class) val selectionItem: Flow = accountRepository.selectedMetaAccountFlow() .flatMapLatest { preferences.stringFlow( @@ -74,11 +76,12 @@ class StakingSharedState( encode(defaultAsset) } - ).distinctUntilChanged() + ) } .map { encoded -> encoded?.let { decode(it) } } + .distinctUntilChanged() .filterNotNull() .shareIn(scope, SharingStarted.Eagerly, replay = 1) @@ -97,6 +100,7 @@ class StakingSharedState( SingleAssetSharedState.AssetWithChain(chain, asset) } + @OptIn(ExperimentalCoroutinesApi::class) fun currentAssetFlow() = combine( assetWithChain, accountRepository.selectedMetaAccountFlow() diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/common/StakingAssetSelector.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/common/StakingAssetSelector.kt index 4807b847a3..d44aca530f 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/common/StakingAssetSelector.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/common/StakingAssetSelector.kt @@ -23,11 +23,8 @@ class StakingAssetSelector( val showAssetChooser = MutableLiveData>>() - val selectedItem = stakingSharedState.selectionItem - .shareIn(this, SharingStarted.Eagerly, replay = 1) - val selectedAssetModelFlow: SharedFlow = combine( - selectedItem, + stakingSharedState.selectionItem, stakingSharedState.currentAssetFlow() ) { selectedItem, asset -> val assetBalance = if (selectedItem.type == StakingType.POOL) { From d84cb432fd2f2bdbad8b4e4cf744774e6ebcfeef Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 16 Apr 2024 19:40:57 +0500 Subject: [PATCH 028/100] xml update - soft keyborad content padding --- .../src/main/res/layout/fragment_bond_more.xml | 3 ++- .../src/main/res/layout/fragment_select_unbond.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/feature-staking-impl/src/main/res/layout/fragment_bond_more.xml b/feature-staking-impl/src/main/res/layout/fragment_bond_more.xml index 9fcba2e342..8123a03d81 100644 --- a/feature-staking-impl/src/main/res/layout/fragment_bond_more.xml +++ b/feature-staking-impl/src/main/res/layout/fragment_bond_more.xml @@ -6,7 +6,8 @@ android:layout_height="match_parent" android:background="@drawable/drawable_background_image" android:backgroundTint="#63000000" - android:backgroundTintMode="src_atop"> + android:backgroundTintMode="src_atop" + android:fitsSystemWindows="true"> + android:backgroundTintMode="src_atop" + android:fitsSystemWindows="true"> Date: Tue, 16 Apr 2024 22:04:13 +0500 Subject: [PATCH 029/100] FLW-4477 There is shimmers on the wallet; AccountDetalsViewModel state rework --- .../account/details/AccountDetailsContent.kt | 5 +--- .../details/AccountDetailsViewModel.kt | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsContent.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsContent.kt index 6f06e6d36d..2023d8189a 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsContent.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsContent.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import jp.co.soramitsu.account.impl.presentation.importing.remote_backup.views.CompactWalletItemViewState import jp.co.soramitsu.common.R import jp.co.soramitsu.common.compose.component.B0 import jp.co.soramitsu.common.compose.component.CapsTitle2 @@ -81,9 +80,7 @@ internal fun AccountDetailsContent( WalletItem( modifier = Modifier .padding(horizontal = 16.dp), - state = CompactWalletItemViewState( - title = state.walletItem.title - ), + state = state.walletItem, onSelected = {} ) } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt index a1afe67f64..38dee3abc3 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt @@ -69,7 +69,7 @@ class AccountDetailsViewModel @Inject constructor( private val walletId = savedStateHandle.get(ACCOUNT_ID_KEY)!! private val wallet = flowOf { interactor.getMetaAccount(walletId) - } + }.share() private val walletItem = wallet .map { wallet -> @@ -123,18 +123,7 @@ class AccountDetailsViewModel @Inject constructor( .inBackground() .share() - val state = combine( - walletItem, - chainAccountProjections, - enteredQueryFlow - ) { walletItem, chainProjections, query -> - AccountDetailsState( - walletItem = walletItem, - chainProjections = chainProjections, - searchQuery = query - ) - } - .stateIn(viewModelScope, SharingStarted.Eagerly, AccountDetailsState.Empty) + val state = MutableStateFlow(AccountDetailsState.Empty) init { launch { @@ -142,6 +131,21 @@ class AccountDetailsViewModel @Inject constructor( } syncNameChangesWithDb() + subscribeScreenState() + } + + private fun subscribeScreenState() { + walletItem.onEach { + state.value = state.value.copy(walletItem = it) + }.launchIn(this) + + chainAccountProjections.onEach { + state.value = state.value.copy(chainProjections = it) + }.launchIn(this) + + enteredQueryFlow.onEach { + state.value = state.value.copy(searchQuery = it) + }.launchIn(this) } override fun onBackClick() { From 8df25a1b85d4e33716cdb2da0f470e97649eb3eb Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 17 Apr 2024 15:01:47 +0500 Subject: [PATCH 030/100] FLW-4476 Wallet details. 3 dots. On ETH there is no View in etherscan button and Switch node button --- .../data/network/BlockExplorerUrlBuilder.kt | 2 +- .../account/details/AccountDetailsViewModel.kt | 12 ++++-------- .../confirm/ConfirmContributeViewModel.kt | 5 ++--- .../confirm/ConfirmStakingViewModel.kt | 7 +++---- .../payouts/confirm/ConfirmPayoutViewModel.kt | 5 ++--- .../payouts/detail/PayoutDetailsViewModel.kt | 8 ++------ .../bond/confirm/ConfirmBondMoreViewModel.kt | 5 ++--- .../confirm/ConfirmSetControllerViewModel.kt | 7 +++---- .../controller/set/SetControllerViewModel.kt | 5 ++--- .../rebond/confirm/ConfirmRebondViewModel.kt | 8 ++------ .../staking/redeem/RedeemViewModel.kt | 5 ++--- .../confirm/ConfirmRewardDestinationViewModel.kt | 5 ++--- .../unbond/confirm/ConfirmUnbondViewModel.kt | 5 ++--- .../details/CollatorDetailsViewModel.kt | 5 ++--- .../details/ValidatorDetailsViewModel.kt | 5 ++--- .../confirm/CrossChainConfirmViewModel.kt | 5 ++--- .../send/confirm/ConfirmSendViewModel.kt | 5 ++--- .../detail/extrinsic/ExtrinsicDetailFragment.kt | 2 +- .../detail/extrinsic/ExtrinsicDetailViewModel.kt | 5 +++++ .../detail/reward/RewardDetailFragment.kt | 2 +- .../detail/reward/RewardDetailViewModel.kt | 5 +++++ .../transfer/TransactionDetailViewModel.kt | 6 ++++-- .../detail/transfer/TransferDetailFragment.kt | 2 +- .../runtime/multiNetwork/chain/model/Chain.kt | 16 ++++++++++++++++ 24 files changed, 70 insertions(+), 67 deletions(-) diff --git a/common/src/main/java/jp/co/soramitsu/common/data/network/BlockExplorerUrlBuilder.kt b/common/src/main/java/jp/co/soramitsu/common/data/network/BlockExplorerUrlBuilder.kt index b0203a0b3b..cedb3f3129 100644 --- a/common/src/main/java/jp/co/soramitsu/common/data/network/BlockExplorerUrlBuilder.kt +++ b/common/src/main/java/jp/co/soramitsu/common/data/network/BlockExplorerUrlBuilder.kt @@ -2,7 +2,7 @@ package jp.co.soramitsu.common.data.network class BlockExplorerUrlBuilder(private val baseUrl: String, private val types: List) { enum class Type { - EXTRINSIC, ACCOUNT, EVENT, TX, TRANSFER; + EXTRINSIC, ACCOUNT, EVENT, TX, TRANSFER, ADDRESS; val nameLowercase = name.lowercase() } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt index 38dee3abc3..c2f277d8bf 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/account/details/AccountDetailsViewModel.kt @@ -22,8 +22,6 @@ import jp.co.soramitsu.common.address.createAddressIcon import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.compose.component.ChangeBalanceViewState import jp.co.soramitsu.common.compose.component.WalletItemViewState -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder -import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.list.headers.TextHeader import jp.co.soramitsu.common.list.toListWithHeaders import jp.co.soramitsu.common.resources.ResourceManager @@ -36,19 +34,17 @@ import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.feature_account_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import kotlin.time.DurationUnit import kotlin.time.toDuration import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch private const val UPDATE_NAME_INTERVAL_SECONDS = 1L @@ -106,9 +102,9 @@ class AccountDetailsViewModel @Inject constructor( val openPlayMarket: LiveData> = _openPlayMarket private val enteredQueryFlow = MutableStateFlow("") - val accountNameFlow = MutableStateFlow("") + private val accountNameFlow = MutableStateFlow("") - val chainAccountProjections = combine( + private val chainAccountProjections = combine( interactor.getChainProjectionsFlow(walletId), enteredQueryFlow ) { groupedList, query -> @@ -253,7 +249,7 @@ class AccountDetailsViewModel @Inject constructor( if (item.hasAccount) { val chain = chainRegistry.getChain(item.chainId) val supportedExplorers = - chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, item.address) + chain.explorers.getSupportedAddressExplorers(item.address) externalAccountActions.showExternalActions(ExternalAccountActions.Payload(item.address, item.chainId, item.chainName, supportedExplorers, !chain.isEthereumChain diff --git a/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/presentation/contribute/confirm/ConfirmContributeViewModel.kt b/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/presentation/contribute/confirm/ConfirmContributeViewModel.kt index b65aeee4e8..34319432a9 100644 --- a/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/presentation/contribute/confirm/ConfirmContributeViewModel.kt +++ b/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/presentation/contribute/confirm/ConfirmContributeViewModel.kt @@ -12,7 +12,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCryptoDetail @@ -38,7 +37,7 @@ import jp.co.soramitsu.crowdloan.impl.presentation.contribute.select.parcel.getS import jp.co.soramitsu.crowdloan.impl.presentation.contribute.select.parcel.mapParachainMetadataFromParcel import jp.co.soramitsu.feature_crowdloan_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.wallet.api.data.mappers.mapAssetToAssetModel import jp.co.soramitsu.wallet.api.data.mappers.mapFeeToFeeModel import jp.co.soramitsu.wallet.api.domain.AssetUseCase @@ -157,7 +156,7 @@ class ConfirmContributeViewModel @Inject constructor( val accountAddress = selectedAddressModelFlow.first().address val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, accountAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(accountAddress) val payload = ExternalAccountActions.Payload(accountAddress, chainId, chain.name, supportedExplorers) externalAccountActions.showExternalActions(payload) } diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/ConfirmStakingViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/ConfirmStakingViewModel.kt index 00ac59f1fa..a5995b5a78 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/ConfirmStakingViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/ConfirmStakingViewModel.kt @@ -13,7 +13,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.AddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Retriable import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ClipboardManager @@ -25,7 +24,7 @@ import jp.co.soramitsu.common.validation.progressConsumer import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.ext.addressOf import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.api.domain.model.RewardDestination import jp.co.soramitsu.staking.api.domain.model.StakingState import jp.co.soramitsu.staking.api.domain.model.Validator @@ -201,7 +200,7 @@ class ConfirmStakingViewModel @Inject constructor( interactor.getSelectedAccountProjection()?.let { account -> val chainId = controllerAssetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, account.address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(account.address) val externalActionsPayload = ExternalAccountActions.Payload( value = account.address, chainId = chainId, @@ -218,7 +217,7 @@ class ConfirmStakingViewModel @Inject constructor( val payoutDestination = rewardDestinationLiveData.value as? RewardDestinationModel.Payout ?: return@launch val chainId = controllerAssetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, payoutDestination.destination.address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(payoutDestination.destination.address) val externalActionsPayload = ExternalAccountActions.Payload( value = payoutDestination.destination.address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt index ab07743f4b..5c9c5655ce 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt @@ -13,7 +13,6 @@ import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.base.TitleAndMessage -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCryptoDetail @@ -25,7 +24,7 @@ import jp.co.soramitsu.common.validation.ValidationSystem import jp.co.soramitsu.common.validation.progressConsumer import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.shared_utils.ss58.SS58Encoder.addressByte import jp.co.soramitsu.shared_utils.ss58.SS58Encoder.toAddress import jp.co.soramitsu.staking.api.data.SyntheticStakingType @@ -204,7 +203,7 @@ class ConfirmPayoutViewModel @Inject constructor( val address = addressProducer() ?: return@launch val chainId = tokenFlow.first().configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(address) val externalActionsPayload = ExternalAccountActions.Payload( value = address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/detail/PayoutDetailsViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/detail/PayoutDetailsViewModel.kt index dc79ce1e21..eee4bccea9 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/detail/PayoutDetailsViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/detail/PayoutDetailsViewModel.kt @@ -7,14 +7,13 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCryptoDetail import jp.co.soramitsu.common.utils.formatFiat import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.api.data.SyntheticStakingType import jp.co.soramitsu.staking.api.data.syntheticStakingType import jp.co.soramitsu.staking.impl.domain.StakingInteractor @@ -66,10 +65,7 @@ class PayoutDetailsViewModel @Inject constructor( fun validatorExternalActionClicked() = launch { val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers( - BlockExplorerUrlBuilder.Type.ACCOUNT, - payout.validatorInfo.address - ) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(payout.validatorInfo.address) val externalActionsPayload = ExternalAccountActions.Payload( value = payout.validatorInfo.address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmBondMoreViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmBondMoreViewModel.kt index 4291f43531..8990c21256 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmBondMoreViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmBondMoreViewModel.kt @@ -10,7 +10,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.flowOf @@ -22,7 +21,7 @@ import jp.co.soramitsu.common.validation.ValidationExecutor import jp.co.soramitsu.common.validation.progressConsumer import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.impl.domain.StakingInteractor import jp.co.soramitsu.staking.impl.domain.staking.bond.BondMoreInteractor import jp.co.soramitsu.staking.impl.domain.validations.bond.BondMoreValidationPayload @@ -108,7 +107,7 @@ class ConfirmBondMoreViewModel @Inject constructor( fun originAccountClicked() = launch { val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, payload.stashAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(payload.stashAddress) val externalActionsPayload = ExternalAccountActions.Payload( value = payload.stashAddress, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/confirm/ConfirmSetControllerViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/confirm/ConfirmSetControllerViewModel.kt index 17b9e92ef8..38b06c47d5 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/confirm/ConfirmSetControllerViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/confirm/ConfirmSetControllerViewModel.kt @@ -9,7 +9,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.flowOf @@ -17,7 +16,7 @@ import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.common.validation.ValidationExecutor import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.impl.domain.StakingInteractor import jp.co.soramitsu.staking.impl.domain.staking.controller.ControllerInteractor import jp.co.soramitsu.staking.impl.domain.validations.controller.SetControllerValidationPayload @@ -77,7 +76,7 @@ class ConfirmSetControllerViewModel @Inject constructor( viewModelScope.launch { val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, payload.stashAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(payload.stashAddress) val externalActionsPayload = ExternalAccountActions.Payload( value = payload.stashAddress, chainId = chainId, @@ -93,7 +92,7 @@ class ConfirmSetControllerViewModel @Inject constructor( viewModelScope.launch { val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, payload.controllerAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(payload.controllerAddress) val externalActionsPayload = ExternalAccountActions.Payload( value = payload.controllerAddress, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/set/SetControllerViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/set/SetControllerViewModel.kt index c0e76c8bba..0d9d5cc0a1 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/set/SetControllerViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/controller/set/SetControllerViewModel.kt @@ -13,7 +13,6 @@ import jp.co.soramitsu.common.address.AddressModel import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.data.network.AppLinksProvider -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.RetryPayload import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager @@ -25,7 +24,7 @@ import jp.co.soramitsu.common.utils.updateFrom import jp.co.soramitsu.common.validation.ValidationExecutor import jp.co.soramitsu.common.view.bottomSheet.list.dynamic.DynamicListBottomSheet.Payload import jp.co.soramitsu.feature_wallet_api.R -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.api.domain.model.StakingAccount import jp.co.soramitsu.staking.api.domain.model.StakingState import jp.co.soramitsu.staking.impl.domain.StakingInteractor @@ -122,7 +121,7 @@ class SetControllerViewModel @Inject constructor( } else { stakingInteractor.getChain(chainId) } - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, stashAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(stashAddress) val externalActionsPayload = ExternalAccountActions.Payload( value = stashAddress, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rebond/confirm/ConfirmRebondViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rebond/confirm/ConfirmRebondViewModel.kt index 864cfd4fbd..4c65563a4f 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rebond/confirm/ConfirmRebondViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rebond/confirm/ConfirmRebondViewModel.kt @@ -11,7 +11,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCryptoFull @@ -21,7 +20,7 @@ import jp.co.soramitsu.common.validation.ValidationExecutor import jp.co.soramitsu.common.validation.progressConsumer import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.impl.domain.StakingInteractor import jp.co.soramitsu.staking.impl.domain.staking.rebond.RebondInteractor import jp.co.soramitsu.staking.impl.domain.validations.rebond.RebondValidationPayload @@ -116,10 +115,7 @@ class ConfirmRebondViewModel @Inject constructor( val originAddressModel = originAddressModelLiveData.value ?: return@launch val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers( - BlockExplorerUrlBuilder.Type.ACCOUNT, - originAddressModel.address - ) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(originAddressModel.address) val externalActionsPayload = ExternalAccountActions.Payload( value = originAddressModel.address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/RedeemViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/RedeemViewModel.kt index 7b699a6aa3..ec851a13be 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/RedeemViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/RedeemViewModel.kt @@ -13,7 +13,6 @@ import jp.co.soramitsu.common.R import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatCryptoFull @@ -24,7 +23,7 @@ import jp.co.soramitsu.common.utils.requireValue import jp.co.soramitsu.common.validation.ValidationExecutor import jp.co.soramitsu.common.validation.progressConsumer import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.staking.api.domain.model.StakingState import jp.co.soramitsu.staking.impl.domain.StakingInteractor @@ -155,7 +154,7 @@ class RedeemViewModel @Inject constructor( val address = originAddressModelLiveData.value?.address ?: return@launch val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(address) val externalActionsPayload = ExternalAccountActions.Payload( value = address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rewardDestination/confirm/ConfirmRewardDestinationViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rewardDestination/confirm/ConfirmRewardDestinationViewModel.kt index c66242b632..1e18d274a6 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rewardDestination/confirm/ConfirmRewardDestinationViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/rewardDestination/confirm/ConfirmRewardDestinationViewModel.kt @@ -8,7 +8,6 @@ import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.AddressModel import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.inBackground @@ -35,13 +34,13 @@ import jp.co.soramitsu.staking.impl.scenarios.relaychain.StakingRelayChainScenar import jp.co.soramitsu.wallet.api.data.mappers.mapFeeToFeeModel import jp.co.soramitsu.wallet.api.presentation.mixin.fee.FeeStatus import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers @HiltViewModel class ConfirmRewardDestinationViewModel @Inject constructor( @@ -111,7 +110,7 @@ class ConfirmRewardDestinationViewModel @Inject constructor( private fun showAddressExternalActions(address: String) = launch { val chainId = controllerAssetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(address) val externalActionsPayload = ExternalAccountActions.Payload( value = address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmUnbondViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmUnbondViewModel.kt index c894b74bf4..441368466e 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmUnbondViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmUnbondViewModel.kt @@ -8,7 +8,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createAddressModel import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.mixin.api.Validatable import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.formatFiat @@ -19,7 +18,6 @@ import jp.co.soramitsu.common.validation.ValidationExecutor import jp.co.soramitsu.common.validation.progressConsumer import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers import jp.co.soramitsu.staking.impl.domain.StakingInteractor import jp.co.soramitsu.staking.impl.domain.staking.unbond.UnbondInteractor import jp.co.soramitsu.staking.impl.domain.validations.unbond.UnbondValidationPayload @@ -36,6 +34,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers @HiltViewModel class ConfirmUnbondViewModel @Inject constructor( @@ -108,7 +107,7 @@ class ConfirmUnbondViewModel @Inject constructor( val originAddressModel = originAddressModelLiveData.value ?: return@launch val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, originAddressModel.address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(originAddressModel.address) val externalActionsPayload = ExternalAccountActions.Payload( value = originAddressModel.address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/CollatorDetailsViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/CollatorDetailsViewModel.kt index 6af39bf7b5..bc741e02bb 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/CollatorDetailsViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/CollatorDetailsViewModel.kt @@ -11,7 +11,6 @@ import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.address.createEthereumAddressModel import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.data.network.AppLinksProvider -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.formatAsPercentage @@ -20,7 +19,7 @@ import jp.co.soramitsu.common.utils.formatFiat import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.staking.api.domain.model.CandidateInfoStatus import jp.co.soramitsu.staking.impl.domain.StakingInteractor @@ -174,7 +173,7 @@ class CollatorDetailsViewModel @Inject constructor( val address = collatorDetails.value?.address ?: return@launch val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(address) val externalActionsPayload = ExternalAccountActions.Payload( value = address, chainId = chainId, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/ValidatorDetailsViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/ValidatorDetailsViewModel.kt index 334e5cc61a..5d3f410cd4 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/ValidatorDetailsViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/details/ValidatorDetailsViewModel.kt @@ -10,7 +10,6 @@ import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions import jp.co.soramitsu.common.address.AddressIconGenerator import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.data.network.AppLinksProvider -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.flowOf @@ -20,7 +19,7 @@ import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.common.utils.sumByBigInteger import jp.co.soramitsu.feature_staking_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.staking.impl.domain.StakingInteractor import jp.co.soramitsu.staking.impl.domain.getSelectedChain import jp.co.soramitsu.staking.impl.presentation.StakingRouter @@ -142,7 +141,7 @@ class ValidatorDetailsViewModel @Inject constructor( val address = validatorDetails.value?.address ?: return@launch val chainId = assetFlow.first().token.configuration.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(address) val externalActionsPayload = ExternalAccountActions.Payload( value = address, chainId = chainId, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/confirm/CrossChainConfirmViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/confirm/CrossChainConfirmViewModel.kt index 624a4c482b..91c76f6ada 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/confirm/CrossChainConfirmViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/confirm/CrossChainConfirmViewModel.kt @@ -16,7 +16,6 @@ import jp.co.soramitsu.common.base.errors.ValidationException import jp.co.soramitsu.common.base.errors.ValidationWarning import jp.co.soramitsu.common.compose.component.ButtonViewState import jp.co.soramitsu.common.compose.component.TitleValueViewState -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.combine @@ -28,7 +27,7 @@ import jp.co.soramitsu.common.utils.requireValue import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.feature_wallet_impl.R -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.wallet.api.domain.TransferValidationResult import jp.co.soramitsu.wallet.api.domain.ValidateTransferUseCase import jp.co.soramitsu.wallet.api.domain.fromValidationResult @@ -233,7 +232,7 @@ class CrossChainConfirmViewModel @Inject constructor( override fun copyRecipientAddressClicked() { launch { val chain = destinationNetworkFlow.value ?: return@launch - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, transferDraft.recipientAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(transferDraft.recipientAddress) val externalActionsPayload = ExternalAccountActions.Payload( value = transferDraft.recipientAddress, chainId = chain.id, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/confirm/ConfirmSendViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/confirm/ConfirmSendViewModel.kt index effa7d9739..cec6ae12c0 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/confirm/ConfirmSendViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/confirm/ConfirmSendViewModel.kt @@ -18,7 +18,6 @@ import jp.co.soramitsu.common.base.errors.ValidationException import jp.co.soramitsu.common.base.errors.ValidationWarning import jp.co.soramitsu.common.compose.component.ButtonViewState import jp.co.soramitsu.common.compose.component.TitleValueViewState -import jp.co.soramitsu.common.data.network.BlockExplorerUrlBuilder import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.applyFiatRate @@ -38,7 +37,7 @@ import jp.co.soramitsu.polkaswap.api.models.Market import jp.co.soramitsu.polkaswap.api.models.WithDesired import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.bokoloCashTokenId -import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.wallet.api.domain.TransferValidationResult import jp.co.soramitsu.wallet.api.domain.ValidateTransferUseCase import jp.co.soramitsu.wallet.api.domain.fromValidationResult @@ -252,7 +251,7 @@ class ConfirmSendViewModel @Inject constructor( launch { val chainId = transferDraft.assetPayload.chainId val chain = chainRegistry.getChain(chainId) - val supportedExplorers = chain.explorers.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, transferDraft.recipientAddress) + val supportedExplorers = chain.explorers.getSupportedAddressExplorers(transferDraft.recipientAddress) val externalActionsPayload = ExternalAccountActions.Payload( value = transferDraft.recipientAddress, chainId = chainId, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailFragment.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailFragment.kt index f5032b8d29..267d2e26c9 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailFragment.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailFragment.kt @@ -74,7 +74,7 @@ class ExtrinsicDetailFragment : BaseFragment(R.layout. ) = showExternalActionsSheet( copyLabelRes = R.string.common_copy_address, value = address, - explorers = viewModel.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address), + explorers = viewModel.getSupportedAddressExplorers(address), externalViewCallback = viewModel::openUrl ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailViewModel.kt index 1701b41956..282d47c9cd 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/extrinsic/ExtrinsicDetailViewModel.kt @@ -21,6 +21,8 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers import kotlinx.coroutines.flow.flow import javax.inject.Inject +import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers private const val ICON_SIZE_DP = 32 @@ -55,6 +57,9 @@ class ExtrinsicDetailViewModel @Inject constructor( fun getSupportedExplorers(type: BlockExplorerUrlBuilder.Type, value: String) = chainExplorers.replayCache.firstOrNull()?.getSupportedExplorers(type, value).orEmpty() + fun getSupportedAddressExplorers(address: String): Map = + chainExplorers.replayCache.firstOrNull()?.getSupportedAddressExplorers(address).orEmpty() + private suspend fun getIcon(address: String) = addressIconGenerator.createAddressModel( address, ICON_SIZE_DP, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailFragment.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailFragment.kt index bfdc425969..a8bd83ee92 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailFragment.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailFragment.kt @@ -92,7 +92,7 @@ class RewardDetailFragment : BaseFragment(R.layout.fragme private fun showExternalAddressActions(address: String) = showExternalActionsSheet( copyLabelRes = R.string.common_copy_address, value = address, - explorers = viewModel.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address), + explorers = viewModel.getSupportedAddressExplorers(address), externalViewCallback = viewModel::openUrl ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailViewModel.kt index e65604cef9..0f65aa1ff9 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/reward/RewardDetailViewModel.kt @@ -21,6 +21,8 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers import kotlinx.coroutines.flow.flow import javax.inject.Inject +import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers private const val ICON_SIZE_DP = 32 @@ -61,6 +63,9 @@ class RewardDetailViewModel @Inject constructor( fun getSupportedExplorers(type: BlockExplorerUrlBuilder.Type, value: String) = chainExplorers.replayCache.firstOrNull()?.getSupportedExplorers(type, value).orEmpty() + fun getSupportedAddressExplorers(address: String): Map = + chainExplorers.replayCache.firstOrNull()?.getSupportedAddressExplorers(address).orEmpty() + private suspend fun getIcon(address: String) = addressIconGenerator.createAddressModel(address, ICON_SIZE_DP, addressDisplayUseCase(address)) fun openUrl(url: String) { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt index b2c5088383..87387837dd 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt @@ -18,6 +18,7 @@ import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain +import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedAddressExplorers import jp.co.soramitsu.runtime.multiNetwork.chain.model.getSupportedExplorers import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import jp.co.soramitsu.wallet.impl.presentation.AssetPayload @@ -70,8 +71,9 @@ class TransactionDetailViewModel @Inject constructor( } return chainExplorers.replayCache.firstOrNull()?.getSupportedExplorers(explorerUrlType, value).orEmpty() } - fun getSupportedExplorers(type: BlockExplorerUrlBuilder.Type, value: String) = - chainExplorers.replayCache.firstOrNull()?.getSupportedExplorers(type, value).orEmpty() + + fun getSupportedAddressExplorers(address: String): Map = + chainExplorers.replayCache.firstOrNull()?.getSupportedAddressExplorers(address).orEmpty() val retryAddressModelLiveData = if (operation.isIncome) senderAddressModelLiveData else recipientAddressModelLiveData diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt index 9dfcfc47ff..44bf2ab7b2 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransferDetailFragment.kt @@ -148,7 +148,7 @@ class TransferDetailFragment : BaseFragment(R.layout private fun showExternalAddressActions(address: String) = showExternalActionsSheet( copyLabelRes = R.string.common_copy_address, value = address, - explorers = viewModel.getSupportedExplorers(BlockExplorerUrlBuilder.Type.ACCOUNT, address), + explorers = viewModel.getSupportedAddressExplorers(address), externalViewCallback = viewModel::openUrl ) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt index 6aee03c36a..63e5b4913c 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt @@ -154,6 +154,22 @@ fun List.getSupportedExplorers(type: BlockExplorerUrlBuilder.Typ } }.toMap() +fun List.getSupportedAddressExplorers(address: String) = mapNotNull { + val type = when (it.type) { + Chain.Explorer.Type.ETHERSCAN, + Chain.Explorer.Type.OKLINK -> { + BlockExplorerUrlBuilder.Type.ADDRESS + } + else -> { + BlockExplorerUrlBuilder.Type.ACCOUNT + } + } + + BlockExplorerUrlBuilder(it.url, it.types).build(type, address)?.let { url -> + it.type to url + } +}.toMap() + @Deprecated("Use defaultChainSort() to get Polkadot at first place", ReplaceWith("defaultChainSort()")) fun ChainId.isPolkadotOrKusama() = this in listOf(polkadotChainId, kusamaChainId) From d426e123e160bb988e7c1c33ff1585378e815186 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 2 May 2024 13:05:20 +0500 Subject: [PATCH 031/100] FLW-4475 NFT. Entered address did not deleted --- .../common/compose/component/AddressInput.kt | 16 ++++++---- .../common/compose/component/Button.kt | 29 ++++++++++++------- .../common/compose/component/InputWithHint.kt | 1 - .../ChooseNFTRecipientPresenter.kt | 28 +++++++++++++++--- .../contract/ChooseNFTRecipientContract.kt | 2 +- .../setup/MinimumAmountValidation.kt | 2 +- 6 files changed, 54 insertions(+), 24 deletions(-) diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/AddressInput.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/AddressInput.kt index 522de8592a..374822160b 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/AddressInput.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/AddressInput.kt @@ -20,7 +20,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import jp.co.soramitsu.common.R -import jp.co.soramitsu.common.compose.theme.FearlessTheme import jp.co.soramitsu.common.compose.theme.black1 import jp.co.soramitsu.common.compose.theme.black2 import jp.co.soramitsu.common.compose.theme.colorAccentDark @@ -88,10 +87,7 @@ fun AddressInput( editable = state.editable, onInput = onInput, Hint = { - Row { - MarginHorizontal(margin = 6.dp) - B1(text = "Public address", color = black1) - } + B1(text = "Public address", color = black1) } ) } @@ -127,7 +123,15 @@ private fun AccountInputPreview() { input = "0xsjkdflsdgueroirgfosdifsd;fgoksd;fg;sd845tg849", image = R.drawable.ic_address_placeholder ) - FearlessTheme { + Column { AddressInput(state) + MarginVertical(margin = 6.dp) + AddressInput(state.copy(input = ""), onPaste = {}) + MarginVertical(margin = 6.dp) + Badge( + iconResId = R.drawable.ic_copy_16, + labelResId = R.string.chip_paste, + onClick = {} + ) } } diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/Button.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/Button.kt index 58d4115036..87fc72ae06 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/Button.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/Button.kt @@ -19,11 +19,14 @@ import androidx.compose.foundation.layout.width import androidx.compose.material.ButtonColors import androidx.compose.material.ButtonDefaults import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Icon +import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -37,7 +40,6 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import jp.co.soramitsu.common.R import jp.co.soramitsu.common.compose.theme.FearlessAppTheme -import jp.co.soramitsu.common.compose.theme.accentButtonColors import jp.co.soramitsu.common.compose.theme.accentDarkButtonColors import jp.co.soramitsu.common.compose.theme.accentDarkDisabledButtonColors import jp.co.soramitsu.common.compose.theme.colorAccent @@ -379,6 +381,7 @@ fun ColoredTextButton( } } +@OptIn(ExperimentalMaterialApi::class) @Composable fun ColoredButton( modifier: Modifier = Modifier, @@ -389,16 +392,20 @@ fun ColoredButton( onClick: () -> Unit, content: @Composable RowScope.() -> Unit ) { - TextButton( - modifier = modifier, - onClick = onClick, - shape = FearlessCorneredShape(cornerRadius = 4.dp, cornerCutLength = 6.dp), - colors = customButtonColors(backgroundColor), - border = border, - enabled = enabled, - contentPadding = contentPadding, - content = content - ) + CompositionLocalProvider( + LocalMinimumInteractiveComponentEnforcement provides false, + ) { + TextButton( + modifier = modifier, + onClick = onClick, + shape = FearlessCorneredShape(cornerRadius = 4.dp, cornerCutLength = 6.dp), + colors = customButtonColors(backgroundColor), + border = border, + enabled = enabled, + contentPadding = contentPadding, + content = content + ) + } } @Composable diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/InputWithHint.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/InputWithHint.kt index 84f25498ca..3211d07d41 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/InputWithHint.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/InputWithHint.kt @@ -80,7 +80,6 @@ private fun PreviewInputWithHint() { onInput = { }, Hint = { Row { - MarginHorizontal(margin = 6.dp) B1(text = "Public address".withNoFontPadding()) } } diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt index e9f6707e41..e40d892ed0 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt @@ -40,7 +40,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filterIsInstance @@ -56,6 +55,7 @@ import kotlinx.coroutines.launch import java.math.BigDecimal import java.math.BigInteger import javax.inject.Inject +import jp.co.soramitsu.nft.impl.navigation.NavAction class ChooseNFTRecipientPresenter @Inject constructor( private val addressIconGenerator: AddressIconGenerator, @@ -70,8 +70,9 @@ class ChooseNFTRecipientPresenter @Inject constructor( private val currentAccountAddressUseCase: CurrentAccountAddressUseCase, private val internalNFTRouter: InternalNFTRouter ) : ChooseNFTRecipientCallback { + private val navGraphRoutesFlow = internalNFTRouter.createNavGraphRoutesFlow().shareIn(coroutinesStore.ioScope, SharingStarted.Eagerly, 1) - private val tokenFlow = internalNFTRouter.createNavGraphRoutesFlow() + private val tokenFlow = navGraphRoutesFlow .filterIsInstance() .map { destinationArgs -> destinationArgs.token } .shareIn(coroutinesStore.uiScope, SharingStarted.Eagerly, 1) @@ -85,6 +86,21 @@ class ChooseNFTRecipientPresenter @Inject constructor( private val currentToken: NFT? get() = tokenFlow.replayCache.lastOrNull() + init { + subscribeClearInputOnBackClick() + } + + private fun subscribeClearInputOnBackClick() { + internalNFTRouter.createNavGraphActionsFlow() + .onEach { + val currentRoute = navGraphRoutesFlow.replayCache.lastOrNull() + if (it is NavAction.BackPressed && currentRoute is NFTNavGraphRoute.ChooseNFTRecipientScreen) { + clearInputAddress() + } + } + .launchIn(coroutinesStore.uiScope) + } + fun handleQRCodeResult(qrCodeContent: String) { val result = walletInteractor.tryReadAddressFromSoraFormat(qrCodeContent) ?: qrCodeContent @@ -145,7 +161,7 @@ class ChooseNFTRecipientPresenter @Inject constructor( input = addressInput, image = addressIcon, editable = false, - showClear = false + showClear = true ), buttonState = ButtonViewState( text = resourceManager.getString(R.string.common_preview), @@ -237,8 +253,12 @@ class ChooseNFTRecipientPresenter @Inject constructor( } override fun onAddressInputClear() { - selectedWalletIdFlow.value = null + clearInputAddress() + } + + private fun clearInputAddress() { addressInputFlow.value = "" + selectedWalletIdFlow.value = null } override fun onNextClick() { diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/contract/ChooseNFTRecipientContract.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/contract/ChooseNFTRecipientContract.kt index 560e39f457..5ec637af0e 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/contract/ChooseNFTRecipientContract.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/contract/ChooseNFTRecipientContract.kt @@ -23,7 +23,7 @@ data class ChooseNFTRecipientScreenState( "", R.drawable.ic_address_placeholder, editable = false, - showClear = false + showClear = true ), buttonState = ButtonViewState("", false), feeInfoState = FeeInfoViewState.default, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt index 65f7ef2826..b27938eb8e 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt @@ -18,7 +18,7 @@ class MinimumAmountValidation( val minimumBondInPlanks = stakingScenarioInteractor.getMinimumStake(assetConfiguration) val minStakeMultiplier: Double = if (assetConfiguration.chainId == polkadotChainId) { - StakingRelaychainScenarioViewModel.POLKADOT_STAKE_EXTRA_MULTIPLIER // 15% increase + StakingRelaychainScenarioViewModel.STAKE_EXTRA_MULTIPLIER // 15% increase } else { 1.0 } From 383c7981b969b188ec41e36ed5f7f94031529a77 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 2 May 2024 18:33:42 +0500 Subject: [PATCH 032/100] FLW-4480 There is wrong text on the alert on the Polkadot staking: update condition --- .../impl/data/repository/StakingConstantsRepository.kt | 2 +- .../domain/validations/setup/MinimumAmountValidation.kt | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/StakingConstantsRepository.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/StakingConstantsRepository.kt index 9986a24c04..20fdc7ca24 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/StakingConstantsRepository.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/StakingConstantsRepository.kt @@ -21,7 +21,7 @@ class StakingConstantsRepository( return try { val runtime = chainRegistry.getRuntime(chainId) - if(runtime.metadata.stakingOrNull()?.constantOrNull("MaxNominatorRewardedPerValidator") != null){ + if (runtime.metadata.stakingOrNull()?.constantOrNull("MaxNominatorRewardedPerValidator") != null) { return getNumberConstant(chainId, "MaxNominatorRewardedPerValidator").toInt() } else { // todo need research diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt index b27938eb8e..6223f6df0f 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/setup/MinimumAmountValidation.kt @@ -5,9 +5,12 @@ import jp.co.soramitsu.common.validation.DefaultFailureLevel import jp.co.soramitsu.common.validation.Validation import jp.co.soramitsu.common.validation.ValidationStatus import jp.co.soramitsu.runtime.multiNetwork.chain.model.polkadotChainId +import jp.co.soramitsu.staking.impl.domain.model.NetworkInfo import jp.co.soramitsu.staking.impl.presentation.staking.main.scenarios.StakingRelaychainScenarioViewModel import jp.co.soramitsu.staking.impl.scenarios.StakingScenarioInteractor import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map class MinimumAmountValidation( private val stakingScenarioInteractor: StakingScenarioInteractor @@ -16,8 +19,9 @@ class MinimumAmountValidation( override suspend fun validate(value: SetupStakingPayload): ValidationStatus { val assetConfiguration = value.asset.token.configuration + val networkInfo = stakingScenarioInteractor.observeNetworkInfoState().map { it as? NetworkInfo.RelayChain }.firstOrNull() val minimumBondInPlanks = stakingScenarioInteractor.getMinimumStake(assetConfiguration) - val minStakeMultiplier: Double = if (assetConfiguration.chainId == polkadotChainId) { + val minStakeMultiplier: Double = if (networkInfo?.shouldUseMinimumStakeMultiplier == true) { StakingRelaychainScenarioViewModel.STAKE_EXTRA_MULTIPLIER // 15% increase } else { 1.0 From a714cc7be76c2c7341f7e7d40b32f94e1fdc993c Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 2 May 2024 21:53:34 +0500 Subject: [PATCH 033/100] fix available text value when token has no fiat --- .../wallet/api/presentation/BaseEnterAmountViewModel.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseEnterAmountViewModel.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseEnterAmountViewModel.kt index ce67028afe..583035f143 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseEnterAmountViewModel.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseEnterAmountViewModel.kt @@ -76,7 +76,11 @@ open class BaseEnterAmountViewModel( private val amountInputViewState: Flow = enteredAmountFlow.map { amount -> val tokenBalance = availableAmountForOperation(asset).formatCrypto(asset.token.configuration.symbol) val tokenBalanceFiat = availableAmountForOperation(asset).applyFiatRate(asset.token.fiatRate)?.formatFiat(asset.token.fiatSymbol) - val balanceWithFiat = tokenBalance + tokenBalanceFiat?.let { " ($it)" } + val balanceWithFiat = if (tokenBalanceFiat == null) { + tokenBalance + } else { + "$tokenBalance ($tokenBalanceFiat)" + } val fiatAmount = amount.applyFiatRate(asset.token.fiatRate)?.formatFiat(asset.token.fiatSymbol) AmountInputViewState( From 8c03a93be9d0fdf5ba8bfdf8b09d99fad34d471c Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 26 Apr 2024 22:25:08 +0500 Subject: [PATCH 034/100] change middle ellipsized text realisation --- common/build.gradle | 2 ++ .../common/compose/component/Text.kt | 26 ++++++------------- gradle/libs.versions.toml | 2 ++ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 01556f9fb9..da98f04683 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -148,6 +148,8 @@ dependencies { implementation libs.xnetworking.fearless, withoutBasic implementation libs.xnetworking.sorawallet, withoutBasic + implementation libs.middle.ellipsis.text + implementation libs.bundles.compose debugImplementation libs.bundles.composeDebug diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/Text.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/Text.kt index 9e21a59dc9..f7eaefb207 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/Text.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/Text.kt @@ -1,21 +1,16 @@ package jp.co.soramitsu.common.compose.component -import android.text.TextUtils -import android.widget.TextView import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextLayoutResult import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.viewinterop.AndroidView -import androidx.core.content.res.ResourcesCompat -import jp.co.soramitsu.common.R +import io.github.mataku.middleellipsistext.MiddleEllipsisText import jp.co.soramitsu.common.compose.theme.bold import jp.co.soramitsu.common.compose.theme.customTypography @@ -66,20 +61,15 @@ fun B1( fun B1EllipsizeMiddle( modifier: Modifier = Modifier, text: String, - color: Color = Color.White + textAlign: TextAlign? = null, + color: Color = Color.Unspecified ) { - AndroidView( + MiddleEllipsisText( + textAlign = textAlign, + text = text, + style = MaterialTheme.customTypography.body1, modifier = modifier, - factory = { context -> - TextView(context).apply { - setTextColor(color.toArgb()) - typeface = ResourcesCompat.getFont(context, R.font.sora_regular) - textSize = 14f - maxLines = 1 - ellipsize = TextUtils.TruncateAt.MIDDLE - } - }, - update = { it.text = text } + color = color ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 45b3315df4..8a4ebce241 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,7 @@ kotlin = "1.9.23" kotlinxSerializationjson = "1.6.3" legacySupportV4 = "1.0.0" material = "1.11.0" +middleEllipsisTextVersion = "1.1.0" mockitoKotlin = "5.2.1" mockitoVersion = "5.10.0" mockitoInlineVersion = "5.2.0" @@ -88,6 +89,7 @@ lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.r lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "architectureComponentVersion" } lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "architectureComponentVersion" } logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttpVersion" } +middle-ellipsis-text = { module = "io.github.mataku:middle-ellipsis-text", version.ref = "middleEllipsisTextVersion" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoVersion" } mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoInlineVersion" } mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" } From 6ecc4c1dc971fc9ed354b523dc371338c6a973b4 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 8 May 2024 11:42:29 +0500 Subject: [PATCH 035/100] fix detekt --- .../presentation/chooserecipient/ChooseNFTRecipientPresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt index e40d892ed0..f44c3f3970 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/presentation/chooserecipient/ChooseNFTRecipientPresenter.kt @@ -23,6 +23,7 @@ import jp.co.soramitsu.nft.domain.NFTTransferInteractor import jp.co.soramitsu.nft.domain.models.NFT import jp.co.soramitsu.nft.impl.domain.usecase.transfer.ValidateNFTTransferUseCase import jp.co.soramitsu.nft.impl.navigation.InternalNFTRouter +import jp.co.soramitsu.nft.impl.navigation.NavAction import jp.co.soramitsu.nft.impl.presentation.CoroutinesStore import jp.co.soramitsu.nft.impl.presentation.chooserecipient.contract.ChooseNFTRecipientCallback import jp.co.soramitsu.nft.impl.presentation.chooserecipient.contract.ChooseNFTRecipientScreenState @@ -55,7 +56,6 @@ import kotlinx.coroutines.launch import java.math.BigDecimal import java.math.BigInteger import javax.inject.Inject -import jp.co.soramitsu.nft.impl.navigation.NavAction class ChooseNFTRecipientPresenter @Inject constructor( private val addressIconGenerator: AddressIconGenerator, From a3f10332731220cda2ae86b092d01d8a3f0bf51c Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 2 Apr 2024 12:30:20 +0700 Subject: [PATCH 036/100] wip --- .../app/root/domain/RootInteractor.kt | 2 +- .../jp/co/soramitsu/common/utils/FlowExt.kt | 4 +- .../jp/co/soramitsu/coredb/dao/AssetDao.kt | 7 + .../data/repository/AccountRepositoryImpl.kt | 81 +++++-- .../account/impl/di/AccountFeatureModule.kt | 9 +- .../wallet/api/data/cache/AssetCache.kt | 12 +- .../runtime/di/ChainRegistryModule.kt | 16 +- .../runtime/multiNetwork/ChainRegistry.kt | 197 ++++++++++-------- .../multiNetwork/chain/ChainSyncService.kt | 81 ++++--- .../runtime/RuntimeSyncService.kt | 26 ++- 10 files changed, 285 insertions(+), 150 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt index 033c4d2f2f..7a1c6530df 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt @@ -52,7 +52,7 @@ class RootInteractor( fun chainRegistrySyncUp() = walletRepository.chainRegistrySyncUp() - suspend fun fetchFeatureToggle() = withContext(Dispatchers.Default){ pendulumPreInstalledAccountsScenario.fetchFeatureToggle() } + suspend fun fetchFeatureToggle() = withContext(Dispatchers.Default) { pendulumPreInstalledAccountsScenario.fetchFeatureToggle() } suspend fun getPendingListOfSessionRequests(topic: String) = withContext(Dispatchers.Default){ Web3Wallet.getPendingListOfSessionRequests(topic) } } diff --git a/common/src/main/java/jp/co/soramitsu/common/utils/FlowExt.kt b/common/src/main/java/jp/co/soramitsu/common/utils/FlowExt.kt index 5549c10bae..5b8f5a257a 100644 --- a/common/src/main/java/jp/co/soramitsu/common/utils/FlowExt.kt +++ b/common/src/main/java/jp/co/soramitsu/common/utils/FlowExt.kt @@ -94,12 +94,12 @@ data class ListDiff( val all: List ) -fun Flow>.diffed(): Flow> { +fun Flow>.diffed(): Flow> { return zipWithPrevious().map { (previous, new) -> val addedOrModified = new - previous.orEmpty().toSet() val removed = if (previous != null && previous.size != new.size) previous - new.toSet() else emptyList() - ListDiff(removed = removed, addedOrModified = addedOrModified, all = new) + ListDiff(removed = removed, addedOrModified = addedOrModified, all = new.toList()) } } diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt index 06d5e32ea0..91c9419652 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt @@ -51,6 +51,7 @@ private const val RETRIEVE_ACCOUNT_ASSETS_QUERY = """ interface AssetReadOnlyCache { fun observeAssets(metaId: Long): Flow> + fun observeAllEnabledAssets(): Flow> suspend fun getAssets(metaId: Long): List fun observeAsset(metaId: Long, accountId: AccountId, chainId: String, assetId: String): Flow @@ -65,6 +66,9 @@ abstract class AssetDao : AssetReadOnlyCache { @Query(RETRIEVE_ACCOUNT_ASSETS_QUERY) abstract override fun observeAssets(metaId: Long): Flow> + @Query("SELECT * FROM assets WHERE enabled = 1") + abstract override fun observeAllEnabledAssets(): Flow> + @Query(RETRIEVE_ACCOUNT_ASSETS_QUERY) abstract override suspend fun getAssets(metaId: Long): List @@ -110,6 +114,9 @@ abstract class AssetDao : AssetReadOnlyCache { @Insert(onConflict = OnConflictStrategy.REPLACE) abstract suspend fun insertAsset(asset: AssetLocal) + @Insert(onConflict = OnConflictStrategy.ABORT) + abstract suspend fun insertAssets(assets: List) + @Update(entity = AssetLocal::class) abstract suspend fun updateAssets(item: List): Int diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt index 5028593f21..a7adb42651 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt @@ -30,13 +30,17 @@ import jp.co.soramitsu.core.crypto.mapEncryptionToCryptoType import jp.co.soramitsu.core.model.Language import jp.co.soramitsu.core.model.SecuritySource import jp.co.soramitsu.core.models.CryptoType +import jp.co.soramitsu.core.models.accountIdOf import jp.co.soramitsu.coredb.dao.AccountDao +import jp.co.soramitsu.coredb.dao.AssetDao import jp.co.soramitsu.coredb.dao.MetaAccountDao import jp.co.soramitsu.coredb.model.AccountLocal +import jp.co.soramitsu.coredb.model.AssetLocal import jp.co.soramitsu.coredb.model.chain.ChainAccountLocal import jp.co.soramitsu.coredb.model.chain.FavoriteChainLocal import jp.co.soramitsu.coredb.model.chain.MetaAccountLocal import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry +import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polkadotChainId @@ -57,8 +61,12 @@ import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.shared_utils.scale.EncodableStruct import jp.co.soramitsu.shared_utils.ss58.SS58Encoder.addressByte import jp.co.soramitsu.shared_utils.ss58.SS58Encoder.toAccountId +import jp.co.soramitsu.wallet.api.data.cache.AssetCache +import jp.co.soramitsu.wallet.impl.domain.model.Asset +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -66,6 +74,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.bouncycastle.util.encoders.Hex @@ -77,11 +86,14 @@ class AccountRepositoryImpl( private val jsonSeedDecoder: JsonSeedDecoder, private val jsonSeedEncoder: JsonSeedEncoder, private val languagesHolder: LanguagesHolder, - private val chainRegistry: ChainRegistry + private val chainRegistry: ChainRegistry, + private val chainsRepository: ChainsRepository, + private val assetDao: AssetDao, + private val dispatcher: CoroutineDispatcher = Dispatchers.Default ) : AccountRepository { override fun getEncryptionTypes(): List { - return CryptoType.values().toList() + return CryptoType.entries } override suspend fun selectAccount(metaAccountId: Long) { @@ -138,7 +150,7 @@ class AccountRepositoryImpl( override fun allMetaAccountsFlow(): StateFlow> { return accountDataSource.observeAllMetaAccounts() - .flowOn(Dispatchers.IO) + .flowOn(dispatcher) .stateIn(GlobalScope, SharingStarted.Eagerly, emptyList()) } @@ -232,7 +244,7 @@ class AccountRepositoryImpl( } override suspend fun getMyAccounts(query: String, chainId: String): Set { -// return withContext(Dispatchers.Default) { +// return withContext(dispatcher) { // accountDao.getAccounts(query, networkType) // .map { mapAccountLocalToAccount(it) } // .toSet() @@ -294,7 +306,7 @@ class AccountRepositoryImpl( ethSeed: String?, googleBackupAddress: String? ) { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val substrateSeedBytes = Hex.decode(seed.removePrefix("0x")) val ethSeedBytes = ethSeed?.let { Hex.decode(it.removePrefix("0x")) } @@ -336,8 +348,12 @@ class AccountRepositoryImpl( ) val metaAccountId = insertAccount(metaAccount) - storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) - selectAccount(metaAccountId) + + coroutineScope { + launch { fillAccountAssets(metaAccountId, metaAccount) } + launch { storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) } + launch { selectAccount(metaAccountId) } + }.join() } } @@ -349,7 +365,7 @@ class AccountRepositoryImpl( substrateDerivationPath: String, selectedEncryptionType: CryptoType ) { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val seedBytes = Hex.decode(seed.removePrefix("0x")) val ethereumBased = chainRegistry.getChain(chainId).isEthereumBased @@ -417,7 +433,7 @@ class AccountRepositoryImpl( ethJson: String?, googleBackupAddress: String? ) { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val substrateImportData = jsonSeedDecoder.decode(json, password) val ethImportData = ethJson?.let { jsonSeedDecoder.decode(ethJson, password) } @@ -449,9 +465,12 @@ class AccountRepositoryImpl( ) val metaAccountId = insertAccount(metaAccount) - storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) - selectAccount(metaAccountId) + coroutineScope { + launch { fillAccountAssets(metaAccountId, metaAccount) } + launch { storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) } + launch { selectAccount(metaAccountId) } + }.join() } } @@ -462,7 +481,7 @@ class AccountRepositoryImpl( json: String, password: String ) { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val importData = jsonSeedDecoder.decode(json, password) val ethereumBased = chainRegistry.getChain(chainId).isEthereumBased @@ -515,7 +534,7 @@ class AccountRepositoryImpl( } override suspend fun generateMnemonic(): List { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val generationResult = MnemonicCreator.randomMnemonic(Mnemonic.Length.TWELVE) generationResult.wordList @@ -539,7 +558,7 @@ class AccountRepositoryImpl( } override suspend fun processAccountJson(json: String): ImportJsonData { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val importAccountMeta = jsonSeedDecoder.extractImportMetaData(json) with(importAccountMeta) { @@ -575,7 +594,7 @@ class AccountRepositoryImpl( } override suspend fun generateRestoreJson(metaId: Long, chainId: ChainId, password: String) = - withContext(Dispatchers.Default) { + withContext(dispatcher) { val chain = chainRegistry.getChain(chainId) val metaAccount = getMetaAccount(metaId) @@ -674,7 +693,7 @@ class AccountRepositoryImpl( isBackedUp: Boolean, googleBackupAddress: String? ): Long { - return withContext(Dispatchers.Default) { + return withContext(dispatcher) { val substrateDerivationPathOrNull = substrateDerivationPath.nullIfEmpty() val decodedDerivationPath = substrateDerivationPathOrNull?.let { SubstrateJunctionDecoder.decode(it) @@ -733,7 +752,10 @@ class AccountRepositoryImpl( ) val metaAccountId = insertAccount(metaAccount) - storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) + coroutineScope { + launch { fillAccountAssets(metaAccountId, metaAccount) } + launch { storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) } + }.join() metaAccountId } @@ -842,6 +864,31 @@ class AccountRepositoryImpl( throw AccountAlreadyExistsException() } + private suspend fun fillAccountAssets(metaAccountId: Long, metaAccount: MetaAccountLocal) { + val chains = chainsRepository.getChains() + val assets = chains.map { chain -> + chain.assets.map { + AssetLocal( + id = it.id, + chainId = chain.id, + accountId = metaAccount.substrateAccountId, + metaId = metaAccountId, + tokenPriceId = it.priceId, + freeInPlanks = null, + reservedInPlanks = null, + miscFrozenInPlanks = null, + feeFrozenInPlanks = null, + bondedInPlanks = null, + redeemableInPlanks = null, + unbondingInPlanks = null, + enabled = chain.rank != null && it.isUtility + ) + + } + }.flatten() + assetDao.insertAssets(assets) + } + private suspend fun insertChainAccount( chainAccount: ChainAccountLocal ) = try { diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt index 330e80ab2d..b654e8411b 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt @@ -51,6 +51,7 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.shared_utils.encrypt.json.JsonSeedDecoder import jp.co.soramitsu.shared_utils.encrypt.json.JsonSeedEncoder +import jp.co.soramitsu.wallet.api.data.cache.AssetCache @InstallIn(SingletonComponent::class) @Module @@ -79,7 +80,9 @@ class AccountFeatureModule { jsonSeedDecoder: JsonSeedDecoder, jsonSeedEncoder: JsonSeedEncoder, languagesHolder: LanguagesHolder, - chainRegistry: ChainRegistry + chainRegistry: ChainRegistry, + chainsRepository: ChainsRepository, + assetDao: AssetDao ): AccountRepository { return AccountRepositoryImpl( accountDataSource, @@ -89,7 +92,9 @@ class AccountFeatureModule { jsonSeedDecoder, jsonSeedEncoder, languagesHolder, - chainRegistry + chainRegistry, + chainsRepository, + assetDao ) } diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt index 888abc2acb..267ff5c1a6 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt @@ -13,6 +13,7 @@ import jp.co.soramitsu.coredb.model.AssetLocal import jp.co.soramitsu.coredb.model.AssetUpdateItem import jp.co.soramitsu.coredb.model.TokenPriceLocal import jp.co.soramitsu.shared_utils.runtime.AccountId +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -23,7 +24,8 @@ class AssetCache( private val accountRepository: AccountRepository, private val assetDao: AssetDao, private val updatesMixin: UpdatesMixin, - private val selectedFiat: SelectedFiat + private val selectedFiat: SelectedFiat, + private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) : AssetReadOnlyCache by assetDao, UpdatesProviderUi by updatesMixin { @@ -34,7 +36,7 @@ class AssetCache( accountId: AccountId, chainAsset: Asset, builder: (local: AssetLocal) -> AssetLocal - ) = withContext(Dispatchers.IO) { + ) = withContext(dispatcher) { val chainId = chainAsset.chainId val assetId = chainAsset.id val shouldUseChainlinkForRates = selectedFiat.isUsd() && chainAsset.priceProvider?.id != null @@ -91,7 +93,7 @@ class AssetCache( accountId: AccountId, chainAsset: Asset, builder: (local: AssetLocal) -> AssetLocal - ) = withContext(Dispatchers.IO) { + ) = withContext(dispatcher) { val applicableMetaAccount = accountRepository.findMetaAccount(accountId) applicableMetaAccount?.let { @@ -101,14 +103,14 @@ class AssetCache( suspend fun updateTokensPrice( update: List - ) = withContext(Dispatchers.IO) { + ) = withContext(dispatcher) { tokenPriceDao.insertTokensPrice(update) } suspend fun updateTokenPrice( priceId: String, builder: (local: TokenPriceLocal) -> TokenPriceLocal - ) = withContext(Dispatchers.IO) { + ) = withContext(dispatcher) { val tokenPriceLocal = tokenPriceDao.getTokenPrice(priceId) ?: TokenPriceLocal.createEmpty(priceId) val newToken = builder.invoke(tokenPriceLocal) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt index a64ab61a8a..64b64a7522 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt @@ -14,7 +14,10 @@ import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.core.network.JsonFactory import jp.co.soramitsu.core.runtime.ChainConnection import jp.co.soramitsu.core.runtime.RuntimeFactory +import jp.co.soramitsu.coredb.dao.AssetDao +import jp.co.soramitsu.coredb.dao.AssetReadOnlyCache import jp.co.soramitsu.coredb.dao.ChainDao +import jp.co.soramitsu.coredb.dao.MetaAccountDao import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.ChainSyncService import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository @@ -44,8 +47,10 @@ class ChainRegistryModule { @Singleton fun provideChainSyncService( dao: ChainDao, - chainFetcher: ChainFetcher - ) = ChainSyncService(dao, chainFetcher) + chainFetcher: ChainFetcher, + metaAccountDao: MetaAccountDao, + assetDao: AssetDao, + ) = ChainSyncService(dao, chainFetcher, metaAccountDao, assetDao) @Provides @Singleton @@ -140,6 +145,7 @@ class ChainRegistryModule { ) = EthereumConnectionPool(networkStateMixin) + @Provides @Singleton fun provideChainRegistry( @@ -151,7 +157,8 @@ class ChainRegistryModule { runtimeSyncService: RuntimeSyncService, updatesMixin: UpdatesMixin, networkStateMixin: NetworkStateMixin, - ethereumConnectionPool: EthereumConnectionPool + ethereumConnectionPool: EthereumConnectionPool, + assetReadOnlyCache: AssetDao ): ChainRegistry = ChainRegistry( runtimeProviderPool, chainConnectionPool, @@ -161,7 +168,8 @@ class ChainRegistryModule { runtimeSyncService, updatesMixin, networkStateMixin, - ethereumConnectionPool + ethereumConnectionPool, + assetReadOnlyCache ) @Provides diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 9464aeeb3c..4060da3ed8 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -17,6 +17,7 @@ import jp.co.soramitsu.core.models.IChain import jp.co.soramitsu.core.runtime.ChainConnection import jp.co.soramitsu.core.runtime.IChainRegistry import jp.co.soramitsu.core.utils.utilityAsset +import jp.co.soramitsu.coredb.dao.AssetReadOnlyCache import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.coredb.model.chain.ChainNodeLocal import jp.co.soramitsu.runtime.multiNetwork.chain.ChainSyncService @@ -32,18 +33,26 @@ import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProviderPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeSubscriptionPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeSyncService import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch -import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext data class ChainService( @@ -60,101 +69,117 @@ class ChainRegistry @Inject constructor( private val runtimeSyncService: RuntimeSyncService, private val updatesMixin: UpdatesMixin, private val networkStateMixin: NetworkStateMixin, - private val ethereumConnectionPool: EthereumConnectionPool -) : IChainRegistry, CoroutineScope by CoroutineScope(Dispatchers.Default), - UpdatesProviderUi by updatesMixin { + private val ethereumConnectionPool: EthereumConnectionPool, + private val assetsCache: AssetReadOnlyCache, + private val dispatcher: CoroutineDispatcher = Dispatchers.Default +) : IChainRegistry, UpdatesProviderUi by updatesMixin { + + val scope = CoroutineScope(dispatcher + SupervisorJob()) val syncedChains = MutableSharedFlow>() val currentChains = syncedChains .filter { it.isNotEmpty() } .distinctUntilChanged() - .shareIn(this, SharingStarted.Eagerly, replay = 1) + .shareIn(scope, SharingStarted.Eagerly, replay = 1) val chainsById = currentChains.map { chains -> chains.associateBy { it.id } } .inBackground() - .shareIn(this, SharingStarted.Eagerly, replay = 1) + .shareIn(scope, SharingStarted.Eagerly, replay = 1) + + private val enabledAssetsFlow = assetsCache.observeAllEnabledAssets() + .onStart { emit(emptyList()) } + + private val chainsToSync = chainDao.joinChainInfoFlow() + .mapList(::mapChainLocalToChain) + .combine(enabledAssetsFlow) { chains, enabledAssets -> + val popularChains = chains.filter { it.rank != null } + val enabledChains = + enabledAssets.mapNotNull { asset -> chains.find { chain -> chain.id == asset.chainId } } + val chainsWithCrowdloans = chains.filter { it.hasCrowdloans } + val chainsWithStaking = chains.filter { + it.assets.any { asset -> asset.staking == Asset.StakingType.PARACHAIN || asset.staking == Asset.StakingType.RELAYCHAIN || asset.supportStakingPool } + } + (popularChains + enabledChains + chainsWithCrowdloans + chainsWithStaking).toSet() + .filter { /*it.disabled*/ it.nodes.isNotEmpty() } + } + .diffed() + .filter { it.addedOrModified.isNotEmpty() || it.removed.isNotEmpty() } + .flowOn(dispatcher) init { syncUp() } fun syncUp() { - launch { - runCatching { - chainSyncService.syncUp() - runtimeSyncService.syncTypes() - } - chainDao.joinChainInfoFlow() - .mapList(::mapChainLocalToChain) - .diffed() - .filter { it.addedOrModified.isNotEmpty() || it.removed.isNotEmpty() } - .collect { (removed, addedOrModified, all) -> - val s = supervisorScope { - runCatching { - removed.forEach { - val chainId = it.id - if (it.isEthereumChain) { - ethereumConnectionPool.stop(chainId) - return@forEach + scope.launch { + coroutineScope { + launch { chainSyncService.syncUp() } + launch { runtimeSyncService.syncTypes() } + }.join() + + chainsToSync + .onEach { (removed, addedOrModified, all) -> + coroutineScope { + val removedDeferred = removed.map { + async { removeChain(it) } + } + + updatesMixin.startChainsSyncUp(addedOrModified.filter { it.nodes.isNotEmpty() } + .map { it.id }) + + val syncDeferred = addedOrModified.map { chain -> + async { + runCatching { + setupChain(chain) + }.onFailure { + networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) + Log.e( + "ChainRegistry", + "error while sync in chain registry $it" + ) + }.onSuccess { + networkStateMixin.notifyChainSyncSuccess( + chain.id + ) } - runtimeProviderPool.removeRuntimeProvider(chainId) - runtimeSubscriptionPool.removeSubscription(chainId) - runtimeSyncService.unregisterChain(chainId) - connectionPool.removeConnection(chainId) } - updatesMixin.startChainsSyncUp(addedOrModified.filter { it.nodes.isNotEmpty() } - .map { it.id }) - addedOrModified - .filter { /*it.disabled*/ it.nodes.isNotEmpty() } - .map { chain -> - launch chainLaunch@{ - if (chain.isEthereumChain) { - runCatching { - ethereumConnectionPool.setupConnection( - chain, - onSelectedNodeChange = { chainId, newNodeUrl -> - notifyNodeSwitched(NodeId(chainId to newNodeUrl)) - }) - } - return@chainLaunch - } - - runCatching { - val connection = connectionPool.setupConnection( - chain, - onSelectedNodeChange = { chainId, newNodeUrl -> - notifyNodeSwitched(NodeId(chainId to newNodeUrl)) - } - ) - val runtimeProvider = runtimeProviderPool.setupRuntimeProvider(chain) - runtimeSubscriptionPool.setupRuntimeSubscription( - chain, - connection, - runtimeProvider - ) - runtimeSyncService.registerChain(chain) - }.onFailure { networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) } - .onSuccess { - networkStateMixin.notifyChainSyncSuccess( - chain.id - ) - } - } - } } - }.onFailure { - Log.e( - "ChainRegistry", - "error while sync in chain registry $it" - ) - }.requireValue() - joinAll(*s.toTypedArray()) + + (removedDeferred + syncDeferred).awaitAll() + } this@ChainRegistry.syncedChains.emit(all) + } + .launchIn(scope) } } + private fun removeChain(chain: Chain) { + val chainId = chain.id + if (chain.isEthereumChain) { + ethereumConnectionPool.stop(chainId) + return + } + runtimeProviderPool.removeRuntimeProvider(chainId) + runtimeSubscriptionPool.removeSubscription(chainId) + runtimeSyncService.unregisterChain(chainId) + connectionPool.removeConnection(chainId) + } + + private fun setupChain(chain: Chain) { + if (chain.isEthereumChain) { + ethereumConnectionPool.setupConnection(chain, ::notifyNodeSwitched) + return + } + + val connection = connectionPool.setupConnection(chain, ::notifyNodeSwitched) + + runtimeSubscriptionPool.setupRuntimeSubscription(chain, connection) + runtimeSyncService.registerChain(chain) + runtimeProviderPool.setupRuntimeProvider(chain) + } + override fun getConnection(chainId: String) = connectionPool.getConnection(chainId) @Deprecated( @@ -166,7 +191,8 @@ class ChainRegistry @Inject constructor( } suspend fun getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { - return kotlin.runCatching { getRuntimeProvider(chainId).getOrNullWithTimeout() }.getOrNull() + return kotlin.runCatching { getRuntimeProvider(chainId).getOrNullWithTimeout() } + .getOrNull() } fun getConnectionOrNull(chainId: String) = connectionPool.getConnectionOrNull(chainId) @@ -196,18 +222,18 @@ class ChainRegistry @Inject constructor( .mapList(::mapNodeLocalToNode) suspend fun switchNode(id: NodeId) { - withContext(Dispatchers.Default) { + withContext(dispatcher) { val chain = getChain(id.chainId) if (!chain.isEthereumChain) { connectionPool.getConnection(id.chainId).socketService.switchUrl(id.nodeUrl) - notifyNodeSwitched(id) + notifyNodeSwitched(id.chainId, id.nodeUrl) } } } - private fun notifyNodeSwitched(id: NodeId) { - launch(Dispatchers.IO) { - chainDao.selectNode(id.chainId, id.nodeUrl) + private fun notifyNodeSwitched(chainId: ChainId, nodeUrl: String) { + scope.launch { + chainDao.selectNode(chainId, nodeUrl) } } @@ -224,7 +250,8 @@ class ChainRegistry @Inject constructor( suspend fun deleteNode(id: NodeId) = chainDao.deleteNode(id.chainId, id.nodeUrl) - suspend fun getNode(id: NodeId) = mapNodeLocalToNode(chainDao.getNode(id.chainId, id.nodeUrl)) + suspend fun getNode(id: NodeId) = + mapNodeLocalToNode(chainDao.getNode(id.chainId, id.nodeUrl)) suspend fun updateNode(id: NodeId, name: String, url: String) = chainDao.updateNode(id.chainId, id.nodeUrl, name, url) @@ -238,7 +265,10 @@ suspend fun ChainRegistry.getChain(chainId: ChainId): Chain { return getChain(chainId) } -suspend fun ChainRegistry.chainWithAsset(chainId: ChainId, assetId: String): Pair { +suspend fun ChainRegistry.chainWithAsset( + chainId: ChainId, + assetId: String +): Pair { val chain = chainsById.first().getValue(chainId) return chain to chain.assetsById.getValue(assetId) @@ -263,7 +293,8 @@ suspend fun ChainRegistry.getRuntimeCatching(chainId: ChainId): Result - val localVersion = localMapping[remoteChain.id] + val newOrUpdated = remoteChains.mapNotNull { remoteChain -> + val localVersion = localMapping[remoteChain.id] - when { - localVersion == null -> remoteChain // new - localVersion != remoteChain -> remoteChain // updated - else -> null // same - } - }.map(::mapChainToChainLocal) + when { + localVersion == null -> remoteChain // new + localVersion != remoteChain -> remoteChain // updated + else -> null // same + } + }.map(::mapChainToChainLocal) - val removed = localChainsJoinedInfo.filter { it.chain.id !in remoteMapping } - .map(JoinedChainInfo::chain) + coroutineScope { + launch { + val removed = localChainsJoinedInfo.filter { it.chain.id !in remoteMapping } + .map(JoinedChainInfo::chain) + dao.update(removed, newOrUpdated) + } + launch { + val metaAccounts = metaAccountDao.getMetaAccounts() + if(metaAccounts.isEmpty()) return@launch + val newAssets = + newOrUpdated.filter { it.chain.id !in localMapping.keys }.map { it.assets } + .flatten() - dao.update(removed, newOrUpdated) + val newLocalAssets = metaAccounts.map { metaAccount -> + newAssets.map { + AssetLocal( + accountId = metaAccount.substrateAccountId, + id = it.id, + chainId = it.chainId, + metaId = metaAccount.id, + tokenPriceId = it.priceId, + enabled = remoteMapping[it.chainId]?.rank != null && it.isUtility == true + ) + } + }.flatten() + assetsDao.insertAssets(newLocalAssets) + } + }.join() + } } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt index 322d03d7f6..2f42d14874 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.jsonObject @@ -139,17 +140,20 @@ class RuntimeSyncService( ) } - suspend fun syncTypes() { - val types = typesFetcher.getTypes(BuildConfig.TYPES_URL) - val defaultTypes = typesFetcher.getTypes(BuildConfig.DEFAULT_V13_TYPES_URL) - val array = Json.decodeFromString(types) - val chainIdToTypes = - array.mapNotNull { element -> - val chainId = - element.jsonObject["chainId"]?.jsonPrimitive?.content ?: return@mapNotNull null - ChainTypesLocal(chainId, element.toString()) - }.toMutableList().apply { add(ChainTypesLocal("default", defaultTypes)) } - chainDao.insertTypes(chainIdToTypes) + suspend fun syncTypes(): Result = withContext(Dispatchers.Default) { + runCatching { + val types = typesFetcher.getTypes(BuildConfig.TYPES_URL) + val defaultTypes = typesFetcher.getTypes(BuildConfig.DEFAULT_V13_TYPES_URL) + val array = Json.decodeFromString(types) + val chainIdToTypes = + array.mapNotNull { element -> + val chainId = + element.jsonObject["chainId"]?.jsonPrimitive?.content + ?: return@mapNotNull null + ChainTypesLocal(chainId, element.toString()) + }.toMutableList().apply { add(ChainTypesLocal("default", defaultTypes)) } + chainDao.insertTypes(chainIdToTypes) + } } private fun cancelExistingSync(chainId: String) { From 49c166d216783970a53b72709c1c35ab1e6a3c04 Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 5 Apr 2024 12:02:52 +0700 Subject: [PATCH 037/100] wip1 --- .../updaters/BalancesUpdateSystem.kt | 8 +++---- .../data/repository/WalletRepositoryImpl.kt | 21 +++++++++++++++++++ .../runtime/multiNetwork/ChainRegistry.kt | 16 +++++++++----- .../runtime/RuntimeVersionSubscription.kt | 19 ++++++++++++----- 4 files changed, 50 insertions(+), 14 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt index ec2c5354c5..0f00bd29dd 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt @@ -398,10 +398,6 @@ class BalancesUpdateSystem( } } - override fun start(): Flow { - return combine(subscribeFlow(), singleUpdateFlow()) { sideEffect, _ -> sideEffect } - } - private fun logError(chain: Chain, error: Throwable) { Log.e( "BalancesUpdateSystem", @@ -463,6 +459,10 @@ class BalancesUpdateSystem( source = OperationLocal.Source.BLOCKCHAIN ) } + + override fun start(): Flow { + return combine(subscribeFlow(), singleUpdateFlow()) { sideEffect, _ -> sideEffect } + } } // Request with id = 0 helps to indicate balances subscriptions in logs diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index a9129005cd..2179c619d4 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -120,6 +120,27 @@ class WalletRepositoryImpl( private val coingeckoCache = mutableMapOf>>() override fun assetsFlow(meta: MetaAccount): Flow> { + return combine( + chainsRepository.chainsByIdFlow(), + assetCache.observeAssets(meta.id) + ){ chainsById, assetsLocal -> + val chainAccounts = meta.chainAccounts.values.toList() + val updatedAssets = assetsLocal.mapNotNull { asset -> + mapAssetLocalToAsset(chainsById, asset)?.let { + val hasChainAccount = + asset.asset.chainId in chainAccounts.mapNotNull { it.chain?.id } + AssetWithStatus( + asset = it, + hasAccount = !it.accountId.contentEquals(emptyAccountIdValue), + hasChainAccount = hasChainAccount + ) + } + } + updatedAssets + } + } + + fun assetsFlow1(meta: MetaAccount): Flow> { return combine( chainsRepository.chainsByIdFlow(), assetCache.observeAssets(meta.id) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 4060da3ed8..8c0f176e64 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -41,6 +41,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -52,6 +53,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -76,7 +78,7 @@ class ChainRegistry @Inject constructor( val scope = CoroutineScope(dispatcher + SupervisorJob()) - val syncedChains = MutableSharedFlow>() + val syncedChains = MutableStateFlow>(emptyList()) val currentChains = syncedChains .filter { it.isNotEmpty() } @@ -121,9 +123,9 @@ class ChainRegistry @Inject constructor( chainsToSync .onEach { (removed, addedOrModified, all) -> coroutineScope { - val removedDeferred = removed.map { - async { removeChain(it) } - } +// val removedDeferred = removed.map { +// async { removeChain(it) } +// } updatesMixin.startChainsSyncUp(addedOrModified.filter { it.nodes.isNotEmpty() } .map { it.id }) @@ -146,7 +148,11 @@ class ChainRegistry @Inject constructor( } } - (removedDeferred + syncDeferred).awaitAll() + syncDeferred.awaitAll() + } + if(this@ChainRegistry.syncedChains.value.isEmpty()) { + // initial sync is done + } this@ChainRegistry.syncedChains.emit(all) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt index fe8761a851..c7852077ed 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt @@ -23,9 +23,11 @@ import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.SubscribeStateRu import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.runtimeVersionChange import jp.co.soramitsu.shared_utils.wsrpc.state.SocketStateMachine import jp.co.soramitsu.shared_utils.wsrpc.subscriptionFlow +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.first @@ -39,15 +41,17 @@ class RuntimeVersionSubscription( connection: ChainConnection, private val chainDao: ChainDao, private val runtimeSyncService: RuntimeSyncService, - runtimeProvider: RuntimeProvider -) : CoroutineScope by CoroutineScope(Dispatchers.IO + SupervisorJob()) { - + runtimeProvider: RuntimeProvider, + dispatcher: CoroutineDispatcher = Dispatchers.Default +) { + private val scope = CoroutineScope(dispatcher + SupervisorJob()) init { runCatching { ChainsStateTracker.updateState(chainId) { it.copy(runtimeVersion = ChainState.Status.Started) } - launch { + + scope.launch { // await connection - connection.state.first { it is SocketStateMachine.State.Connected } + connection.isConnected.first() connection.socketService.subscriptionFlow(SubscribeRuntimeVersionRequest) .map { it.runtimeVersionChange().specVersion } .catch { @@ -74,6 +78,7 @@ class RuntimeVersionSubscription( ) runtimeSyncService.applyRuntimeVersion(chainId) + ChainsStateTracker.updateState(chainId) { it.copy(runtimeVersion = ChainState.Status.Completed) } } .catch { error -> @@ -115,4 +120,8 @@ class RuntimeVersionSubscription( mapper = pojo().nonNull() ).specVersion }.getOrNull() + + fun cancel() { + scope.coroutineContext.cancel() + } } \ No newline at end of file From 449c6efff6014e99d53a5ea346877d98a3d6cd41 Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 12 Apr 2024 23:33:13 +0700 Subject: [PATCH 038/100] wip --- .../app/root/di/RootFeatureModule.kt | 7 +- .../app/root/domain/RootInteractor.kt | 4 +- .../common/compose/component/FeeInfo.kt | 2 + .../network/runtime/binding/AccountInfo.kt | 67 ++++- .../jp/co/soramitsu/coredb/dao/AssetDao.kt | 2 +- .../co/soramitsu/coredb/dao/MetaAccountDao.kt | 7 + .../soramitsu/coredb/migrations/Migrations.kt | 6 + .../coredb/migrations/V2Migration.kt | 3 +- .../co/soramitsu/coredb/model/AssetLocal.kt | 6 +- .../coredb/model/chain/MetaAccountLocal.kt | 4 +- .../account/api/domain/model/MetaAccount.kt | 11 +- feature-account-impl/build.gradle | 1 + .../account/impl/data/mappers/Mappers.kt | 6 +- .../data/repository/AccountRepositoryImpl.kt | 59 ++--- .../account/impl/domain/BalancesUtils.kt | 223 ++++++++++++++++ .../account/impl/domain/WalletSyncService.kt | 238 ++++++++++++++++++ .../confirm/ConfirmMnemonicViewModel.kt | 5 +- .../impl/domain/NFTTransferInteractorImpl.kt | 2 +- .../wallet/api/data/cache/AssetCacheExt.kt | 2 +- .../blockchain/EthereumRemoteSource.kt | 21 +- .../updaters/BalancesUpdateSystem.kt | 150 +---------- .../network/model/SubstrateBalancesUtils.kt | 105 -------- .../wallet/impl/di/WalletFeatureModule.kt | 24 +- .../balance/assetActions/buy/BuyMixin.kt | 4 +- .../assetActions/buy/BuyMixinProvider.kt | 4 +- .../balance/detail/BalanceDetailViewModel.kt | 4 +- .../balance/list/BalanceListViewModel.kt | 3 +- .../presentation/receive/ReceiveViewModel.kt | 6 +- runtime/build.gradle | 2 +- .../runtime/di/ChainRegistryModule.kt | 6 +- .../co/soramitsu/runtime/di/RuntimeModule.kt | 10 +- .../runtime/multiNetwork/ChainRegistry.kt | 89 ++++--- .../multiNetwork/chain/ChainSyncService.kt | 10 +- .../connection/EthereumConnectionPool.kt | 45 ++-- .../runtime/RuntimeProviderPool.kt | 47 ++-- .../storage/source/BaseStorageSource.kt | 2 +- 36 files changed, 768 insertions(+), 419 deletions(-) create mode 100644 feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt create mode 100644 feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt delete mode 100644 feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/model/SubstrateBalancesUtils.kt diff --git a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt index b25ac10926..ea22ee5b61 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt @@ -6,6 +6,7 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Named import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario +import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.core.updater.UpdateSystem @@ -20,13 +21,15 @@ class RootFeatureModule { walletRepository: WalletRepository, @Named("BalancesUpdateSystem") walletUpdateSystem: UpdateSystem, pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, - preferences: Preferences + preferences: Preferences, + walletSyncService: WalletSyncService ): RootInteractor { return RootInteractor( walletUpdateSystem, walletRepository, pendulumPreInstalledAccountsScenario, - preferences + preferences, + walletSyncService ) } } diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt index 7a1c6530df..0a6c832fae 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt @@ -2,6 +2,7 @@ package jp.co.soramitsu.app.root.domain import com.walletconnect.web3.wallet.client.Web3Wallet import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario +import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.data.storage.appConfig import jp.co.soramitsu.common.domain.model.AppConfig @@ -21,7 +22,8 @@ class RootInteractor( private val updateSystem: UpdateSystem, private val walletRepository: WalletRepository, private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, - private val preferences: Preferences + private val preferences: Preferences, + private val walletSyncService: WalletSyncService ) { fun runBalancesUpdate(): Flow = updateSystem.start().inBackground() diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt index 2908bc6f15..1be2b488c9 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt @@ -1,5 +1,6 @@ package jp.co.soramitsu.common.compose.component +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -69,6 +70,7 @@ fun FeeInfo(state: FeeInfoViewState, modifier: Modifier = Modifier, tooltipClick } } Column(modifier = Modifier.weight(1f)) { + Log.d("&&&", "state.feeAmount = ${state.feeAmount}") state.feeAmount?.let { CapsTitle(text = it, textAlign = TextAlign.End, modifier = Modifier.align(Alignment.End)) } ?: Shimmer( diff --git a/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/AccountInfo.kt b/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/AccountInfo.kt index a97af3a062..4817fe4c42 100644 --- a/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/AccountInfo.kt +++ b/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/AccountInfo.kt @@ -15,6 +15,50 @@ import jp.co.soramitsu.shared_utils.runtime.metadata.storage interface AssetBalanceData +data class AssetBalance( + val freeInPlanks: BigInteger? = null, + val reservedInPlanks: BigInteger? = null, + val miscFrozenInPlanks: BigInteger? = null, + val feeFrozenInPlanks: BigInteger? = null, + val bondedInPlanks: BigInteger? = null, + val redeemableInPlanks: BigInteger? = null, + val unbondingInPlanks: BigInteger? = null, + val status: String? = null +) + +fun AssetBalanceData?.toAssetBalance(): AssetBalance? { + return when (this) { + null, is EmptyBalance -> AssetBalance(freeInPlanks = BigInteger.ZERO) + is AccountInfo -> { + AssetBalance( + freeInPlanks = data.free, + reservedInPlanks = data.reserved, + miscFrozenInPlanks = data.miscFrozen, + feeFrozenInPlanks = data.feeFrozen + ) + } + is AssetsAccountInfo -> { + AssetBalance( + freeInPlanks = balance + ) + } + is OrmlTokensAccountData -> { + AssetBalance( + freeInPlanks = free, + reservedInPlanks = reserved, + miscFrozenInPlanks = frozen + ) + } + is SimpleBalanceData -> { + AssetBalance( + freeInPlanks = balance + ) + } + + else -> null + } +} + object EmptyBalance : AssetBalanceData class AccountData( val free: BigInteger, @@ -96,7 +140,8 @@ class DataPoint( fun bindAccountData(dynamicInstance: Struct.Instance?) = AccountData( free = (dynamicInstance?.get("free") as? BigInteger).orZero(), reserved = (dynamicInstance?.get("reserved") as? BigInteger).orZero(), - miscFrozen = (dynamicInstance?.get("miscFrozen") as? BigInteger) ?: (dynamicInstance?.get("frozen") as? BigInteger).orZero(), + miscFrozen = (dynamicInstance?.get("miscFrozen") as? BigInteger) + ?: (dynamicInstance?.get("frozen") as? BigInteger).orZero(), feeFrozen = (dynamicInstance?.get("feeFrozen") as? BigInteger).orZero() ) @@ -134,14 +179,15 @@ fun bindEquilibriumAssetRates(scale: String?, runtime: RuntimeSnapshot): EqOracl val type = runtime.metadata.module(Modules.ORACLE).storage("PricePoints").returnType() val dynamicInstance = type.fromHexOrNull(runtime, scale).cast() - val dataPoints = dynamicInstance.getList("dataPoints").filterIsInstance().map { dataPointStruct -> - DataPoint( - price = bindNumber(dataPointStruct["price"]), - accountId = bindAccountId(dataPointStruct["accountId"]), - blockNumber = bindNumber(dataPointStruct["blockNumber"]), - timestamp = bindNumber(dataPointStruct["timestamp"]) - ) - } + val dataPoints = dynamicInstance.getList("dataPoints").filterIsInstance() + .map { dataPointStruct -> + DataPoint( + price = bindNumber(dataPointStruct["price"]), + accountId = bindAccountId(dataPointStruct["accountId"]), + blockNumber = bindNumber(dataPointStruct["blockNumber"]), + timestamp = bindNumber(dataPointStruct["timestamp"]) + ) + } return EqOraclePricePoint( blockNumber = bindNumber(dynamicInstance["blockNumber"]), timestamp = bindNumber(dynamicInstance["timestamp"]), @@ -156,7 +202,8 @@ fun bindEquilibriumAccountData(dynamicInstance: Struct.Instance?): EqAccountData val balances = balanceList?.mapNotNull { (it.getOrNull(0) as? BigInteger)?.let { eqAssetId -> val balanceEnum: DictEnum.Entry? = it.getOrNull(1).cast() - val balanceValue = if (balanceEnum?.name == "Positive") balanceEnum.value else BigInteger.ZERO + val balanceValue = + if (balanceEnum?.name == "Positive") balanceEnum.value else BigInteger.ZERO eqAssetId to balanceValue } }?.toMap().orEmpty() diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt index 91c9419652..5569956c50 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt @@ -114,7 +114,7 @@ abstract class AssetDao : AssetReadOnlyCache { @Insert(onConflict = OnConflictStrategy.REPLACE) abstract suspend fun insertAsset(asset: AssetLocal) - @Insert(onConflict = OnConflictStrategy.ABORT) + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract suspend fun insertAssets(assets: List) @Update(entity = AssetLocal::class) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt index 5c121c7f60..7d895fa809 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt @@ -57,6 +57,9 @@ interface MetaAccountDao { @Query("UPDATE meta_accounts SET isSelected = (id = :metaId)") suspend fun selectMetaAccount(metaId: Long) + @Query("UPDATE meta_accounts SET initialized = 1 WHERE id in (:ids)") + suspend fun markAccountsInitialized(ids: List) + @Update(entity = MetaAccountLocal::class) suspend fun updatePositions(updates: List) @@ -118,4 +121,8 @@ interface MetaAccountDao { @Query("SELECT * FROM favorite_chains WHERE metaId = :metaId") fun observeFavoriteChains(metaId: Long): Flow> + + @Query("SELECT * FROM meta_accounts WHERE initialized = 0") + @Transaction + fun observeNotInitializedMetaAccounts(): Flow> } diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt index 3990515e7a..dcbff13857 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt @@ -3,6 +3,12 @@ package jp.co.soramitsu.coredb.migrations import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase +val Migration_65_66 = object : Migration(65, 66) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE meta_accounts ADD COLUMN `initialized` INTEGER NOT NULL DEFAULT 0") + } +} + val Migration_64_65 = object : Migration(64, 65) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("DELETE FROM storage") diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/V2Migration.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/V2Migration.kt index 063ec2b093..2821e929ea 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/V2Migration.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/V2Migration.kt @@ -88,7 +88,8 @@ class V2Migration( isSelected = isSelected, position = index, isBackedUp = false, - googleBackupAddress = null + googleBackupAddress = null, + initialized = false ) val metaId = insertMetaAccount(metaAccount, database) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt index 51c1a5192b..33fce1cc5b 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt @@ -45,13 +45,15 @@ data class AssetLocal( assetId: String, chainId: String, metaId: Long, - priceId: String? + priceId: String?, + enabled: Boolean? = null ) = AssetLocal( id = assetId, chainId = chainId, accountId = accountId, metaId = metaId, - tokenPriceId = priceId + tokenPriceId = priceId, + enabled = enabled ) } diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt index 621d73fe82..096e188724 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt @@ -6,6 +6,7 @@ import androidx.room.ForeignKey import androidx.room.Index import androidx.room.PrimaryKey import androidx.room.Relation +import jp.co.soramitsu.core.models.Chain import jp.co.soramitsu.core.models.CryptoType @Entity( @@ -25,7 +26,8 @@ class MetaAccountLocal( val isSelected: Boolean, val position: Int, val isBackedUp: Boolean, - val googleBackupAddress: String? + val googleBackupAddress: String?, + val initialized: Boolean ) { companion object Table { diff --git a/feature-account-api/src/main/java/jp/co/soramitsu/account/api/domain/model/MetaAccount.kt b/feature-account-api/src/main/java/jp/co/soramitsu/account/api/domain/model/MetaAccount.kt index e372d12c6a..1ce6290e4f 100644 --- a/feature-account-api/src/main/java/jp/co/soramitsu/account/api/domain/model/MetaAccount.kt +++ b/feature-account-api/src/main/java/jp/co/soramitsu/account/api/domain/model/MetaAccount.kt @@ -23,6 +23,7 @@ interface LightMetaAccount { val isSelected: Boolean val name: String val isBackedUp: Boolean + val initialized: Boolean } fun LightMetaAccount( @@ -34,7 +35,8 @@ fun LightMetaAccount( ethereumPublicKey: ByteArray?, isSelected: Boolean, name: String, - isBackedUp: Boolean + isBackedUp: Boolean, + initialized: Boolean ) = object : LightMetaAccount { override val id: Long = id override val substratePublicKey: ByteArray = substratePublicKey @@ -45,6 +47,7 @@ fun LightMetaAccount( override val isSelected: Boolean = isSelected override val name: String = name override val isBackedUp: Boolean = isBackedUp + override val initialized: Boolean = initialized } data class MetaAccount( @@ -59,7 +62,8 @@ data class MetaAccount( override val isSelected: Boolean, override val isBackedUp: Boolean, val googleBackupAddress: String?, - override val name: String + override val name: String, + override val initialized: Boolean ) : LightMetaAccount { class ChainAccount( @@ -98,6 +102,7 @@ data class MetaAccount( } else if (other.ethereumPublicKey != null) return false if (isSelected != other.isSelected) return false if (name != other.name) return false + if (initialized != other.initialized) return false return true } @@ -113,6 +118,7 @@ data class MetaAccount( result = 31 * result + (ethereumPublicKey?.contentHashCode() ?: 0) result = 31 * result + isSelected.hashCode() result = 31 * result + name.hashCode() + result = 31 * result + initialized.hashCode() return result } } @@ -156,3 +162,4 @@ fun MetaAccount.accountId(chain: Chain): ByteArray? { else -> substrateAccountId } } + diff --git a/feature-account-impl/build.gradle b/feature-account-impl/build.gradle index c5494c8da9..6ea2148632 100644 --- a/feature-account-impl/build.gradle +++ b/feature-account-impl/build.gradle @@ -94,4 +94,5 @@ dependencies { api libs.sharedFeaturesCoreDep implementation libs.sharedFeaturesBackupDep + implementation libs.web3jDep } \ No newline at end of file diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/mappers/Mappers.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/mappers/Mappers.kt index 9df626d097..13cd096a4d 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/mappers/Mappers.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/mappers/Mappers.kt @@ -67,7 +67,8 @@ fun mapMetaAccountLocalToLightMetaAccount( ethereumPublicKey = ethereumPublicKey, isSelected = isSelected, name = name, - isBackedUp = isBackedUp + isBackedUp = isBackedUp, + initialized = initialized, ) } @@ -112,7 +113,8 @@ fun mapMetaAccountLocalToMetaAccount( isSelected = isSelected, name = name, isBackedUp = isBackedUp, - googleBackupAddress = googleBackupAddress + googleBackupAddress = googleBackupAddress, + initialized = initialized, ) } return metaAccount diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt index a7adb42651..049fe982e2 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt @@ -12,6 +12,7 @@ import jp.co.soramitsu.account.api.domain.model.MetaAccountOrdering import jp.co.soramitsu.account.api.domain.model.address import jp.co.soramitsu.account.api.domain.model.cryptoType import jp.co.soramitsu.account.api.domain.model.hasChainAccount +import jp.co.soramitsu.account.impl.data.mappers.mapMetaAccountLocalToMetaAccount import jp.co.soramitsu.account.impl.data.repository.datasource.AccountDataSource import jp.co.soramitsu.backup.domain.models.BackupAccountType import jp.co.soramitsu.common.data.Keypair @@ -344,13 +345,13 @@ class AccountRepositoryImpl( ethereumPublicKey = ethereumKeypair?.publicKey, ethereumAddress = ethereumKeypair?.publicKey?.ethereumAddressFromPublicKey(), isBackedUp = true, - googleBackupAddress = googleBackupAddress + googleBackupAddress = googleBackupAddress, + initialized = false ) val metaAccountId = insertAccount(metaAccount) coroutineScope { - launch { fillAccountAssets(metaAccountId, metaAccount) } launch { storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) } launch { selectAccount(metaAccountId) } }.join() @@ -368,7 +369,7 @@ class AccountRepositoryImpl( return withContext(dispatcher) { val seedBytes = Hex.decode(seed.removePrefix("0x")) - val ethereumBased = chainRegistry.getChain(chainId).isEthereumBased + val ethereumBased = chainsRepository.getChain(chainId).isEthereumBased val keyPair = when { ethereumBased -> EthereumKeypairFactory.createWithPrivateKey(seedBytes) else -> { @@ -461,13 +462,13 @@ class AccountRepositoryImpl( ethereumAddress = ethereumKeypair?.publicKey?.ethereumAddressFromPublicKey(), ethereumPublicKey = ethereumKeypair?.publicKey, isBackedUp = true, - googleBackupAddress = googleBackupAddress + googleBackupAddress = googleBackupAddress, + initialized = false ) val metaAccountId = insertAccount(metaAccount) coroutineScope { - launch { fillAccountAssets(metaAccountId, metaAccount) } launch { storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) } launch { selectAccount(metaAccountId) } }.join() @@ -484,7 +485,7 @@ class AccountRepositoryImpl( return withContext(dispatcher) { val importData = jsonSeedDecoder.decode(json, password) - val ethereumBased = chainRegistry.getChain(chainId).isEthereumBased + val ethereumBased = chainsRepository.getChain(chainId).isEthereumBased val keyPair = when { ethereumBased -> EthereumKeypairFactory.createWithPrivateKey(importData.keypair.privateKey) else -> importData.keypair @@ -595,7 +596,7 @@ class AccountRepositoryImpl( override suspend fun generateRestoreJson(metaId: Long, chainId: ChainId, password: String) = withContext(dispatcher) { - val chain = chainRegistry.getChain(chainId) + val chain = chainsRepository.getChain(chainId) val metaAccount = getMetaAccount(metaId) val hasChainAccount = metaAccount.hasChainAccount(chainId) @@ -748,15 +749,12 @@ class AccountRepositoryImpl( isSelected = true, position = position, isBackedUp = isBackedUp, - googleBackupAddress = googleBackupAddress + googleBackupAddress = googleBackupAddress, + initialized = false, ) val metaAccountId = insertAccount(metaAccount) - coroutineScope { - launch { fillAccountAssets(metaAccountId, metaAccount) } - launch { storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) } - }.join() - + storeV2.putMetaAccountSecrets(metaAccountId, secretsV2) metaAccountId } } @@ -796,7 +794,7 @@ class AccountRepositoryImpl( junctions = decodedEthereumDerivationPath.junctions ) - val ethereumBased = chainRegistry.getChain(chainId).isEthereumBased + val ethereumBased = chainsRepository.getChain(chainId).isEthereumBased val keyPair = when { ethereumBased -> ethereumKeypair else -> keys @@ -864,31 +862,6 @@ class AccountRepositoryImpl( throw AccountAlreadyExistsException() } - private suspend fun fillAccountAssets(metaAccountId: Long, metaAccount: MetaAccountLocal) { - val chains = chainsRepository.getChains() - val assets = chains.map { chain -> - chain.assets.map { - AssetLocal( - id = it.id, - chainId = chain.id, - accountId = metaAccount.substrateAccountId, - metaId = metaAccountId, - tokenPriceId = it.priceId, - freeInPlanks = null, - reservedInPlanks = null, - miscFrozenInPlanks = null, - feeFrozenInPlanks = null, - bondedInPlanks = null, - redeemableInPlanks = null, - unbondingInPlanks = null, - enabled = chain.rank != null && it.isUtility - ) - - } - }.flatten() - assetDao.insertAssets(assets) - } - private suspend fun insertChainAccount( chainAccount: ChainAccountLocal ) = try { @@ -899,7 +872,7 @@ class AccountRepositoryImpl( override fun polkadotAddressForSelectedAccountFlow(): Flow { return selectedMetaAccountFlow().map { - val chain = chainRegistry.getChain(polkadotChainId) + val chain = chainsRepository.getChain(polkadotChainId) it.address(chain) ?: "" } } @@ -927,13 +900,13 @@ class AccountRepositoryImpl( override suspend fun googleBackupAddressForWallet(walletId: Long): String { val wallet = getMetaAccount(walletId) - val chain = chainRegistry.getChain(westendChainId) + val chain = chainsRepository.getChain(westendChainId) return wallet.googleBackupAddress ?: wallet.address(chain) ?: "" } override fun googleAddressAllWalletsFlow(): Flow> { return allMetaAccountsFlow().map { allMetaAccounts -> - val westendChain = chainRegistry.getChain(westendChainId) + val westendChain = chainsRepository.getChain(westendChainId) allMetaAccounts.mapNotNull { it.googleBackupAddress ?: it.address(westendChain) } @@ -941,7 +914,7 @@ class AccountRepositoryImpl( } override suspend fun getChain(chainId: ChainId): Chain { - return chainRegistry.getChain(chainId) + return chainsRepository.getChain(chainId) } override suspend fun updateFavoriteChain(metaAccountId: Long, chainId: ChainId, isFavorite: Boolean) { diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt new file mode 100644 index 0000000000..1cd840c8a0 --- /dev/null +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt @@ -0,0 +1,223 @@ +package jp.co.soramitsu.account.impl.domain + +import android.util.Log +import java.math.BigInteger +import jp.co.soramitsu.account.api.domain.model.MetaAccount +import jp.co.soramitsu.account.api.domain.model.accountId +import jp.co.soramitsu.common.data.network.runtime.binding.AssetBalanceData +import jp.co.soramitsu.common.data.network.runtime.binding.EmptyBalance +import jp.co.soramitsu.common.utils.Modules +import jp.co.soramitsu.common.utils.system +import jp.co.soramitsu.common.utils.tokens +import jp.co.soramitsu.core.models.Asset +import jp.co.soramitsu.core.models.ChainAssetType +import jp.co.soramitsu.core.utils.utilityAsset +import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain +import jp.co.soramitsu.shared_utils.runtime.AccountId +import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot +import jp.co.soramitsu.shared_utils.runtime.metadata.module +import jp.co.soramitsu.shared_utils.runtime.metadata.storage +import jp.co.soramitsu.shared_utils.runtime.metadata.storageKey +import jp.co.soramitsu.wallet.api.data.cache.bindAccountInfoOrDefault +import jp.co.soramitsu.wallet.api.data.cache.bindAssetsAccountData +import jp.co.soramitsu.wallet.api.data.cache.bindEquilibriumAccountData +import jp.co.soramitsu.wallet.api.data.cache.bindOrmlTokensAccountDataOrDefault +import kotlinx.coroutines.withContext +import org.web3j.abi.FunctionEncoder +import org.web3j.abi.datatypes.Address +import org.web3j.abi.datatypes.Function +import org.web3j.protocol.core.DefaultBlockParameterName +import org.web3j.protocol.core.Ethereum +import org.web3j.protocol.core.methods.request.Transaction +import org.web3j.utils.Numeric + +fun buildStorageKeys( + chain: Chain, + metaAccount: MetaAccount, + runtime: RuntimeSnapshot +): Result> { + val accountId = metaAccount.accountId(chain) + ?: return Result.failure(RuntimeException("Can't get account id for meta account ${metaAccount.name}, chain: ${chain.name}")) + + return Result.success(buildStorageKeys(chain, runtime, metaAccount.id, accountId)) +} + +fun buildStorageKeys( + chain: Chain, + runtime: RuntimeSnapshot, + metaAccountId: Long, + accountId: ByteArray +): List { + if (chain.utilityAsset != null && chain.utilityAsset?.typeExtra == ChainAssetType.Equilibrium) { + val equilibriumStorageKeys = listOf( + constructBalanceKey( + runtime, + requireNotNull(chain.utilityAsset), + accountId + ).let { + StorageKeyWithMetadata( + requireNotNull(chain.utilityAsset), + metaAccountId, + accountId, + it + ) + }) + return equilibriumStorageKeys + } + + val storageKeys = chain.assets.map { asset -> + StorageKeyWithMetadata(asset, metaAccountId, accountId, + constructBalanceKey(runtime, asset, accountId) + ) + } + return storageKeys +} + +data class StorageKeyWithMetadata( + val asset: Asset, + val metaAccountId: Long, + val accountId: AccountId, + val key: String? +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as StorageKeyWithMetadata + + if (asset != other.asset) return false + if (metaAccountId != other.metaAccountId) return false + if (!accountId.contentEquals(other.accountId)) return false + + return true + } + + override fun hashCode(): Int { + var result = asset.hashCode() + result = 31 * result + metaAccountId.hashCode() + result = 31 * result + accountId.contentHashCode() + return result + } + + override fun toString(): String { + return "StorageKeyWithMetadata(asset=${asset.name}, metaAccountId=$metaAccountId, key='$key')" + } +} + + +fun constructBalanceKey( + runtime: RuntimeSnapshot, + asset: Asset, + accountId: ByteArray +): String? { + val keyConstructionResult = runCatching { + val currency = + asset.currency ?: return@runCatching runtime.metadata.system().storage("Account") + .storageKey(runtime, accountId) + when (asset.typeExtra) { + null, ChainAssetType.Normal, + ChainAssetType.Equilibrium, + ChainAssetType.SoraUtilityAsset -> runtime.metadata.system().storage("Account") + .storageKey(runtime, accountId) + + ChainAssetType.OrmlChain, + ChainAssetType.OrmlAsset, + ChainAssetType.VToken, + ChainAssetType.VSToken, + ChainAssetType.Stable, + ChainAssetType.ForeignAsset, + ChainAssetType.StableAssetPoolToken, + ChainAssetType.SoraAsset, + ChainAssetType.AssetId, + ChainAssetType.Token2, + ChainAssetType.Xcm, + ChainAssetType.LiquidCrowdloan -> runtime.metadata.tokens().storage("Accounts") + .storageKey(runtime, accountId, currency) + + ChainAssetType.Assets -> runtime.metadata.module(Modules.ASSETS).storage("Account") + .storageKey(runtime, currency, accountId) + + ChainAssetType.Unknown -> error("Not supported type for token ${asset.symbol} in ${asset.chainName}") + } + } + return keyConstructionResult + .onFailure { + Log.d( + "BalancesUpdateSystem", + "Failed to construct storage key for asset ${asset.symbol} (${asset.id}) $it " + ) + } + .getOrNull() +} + +fun handleBalanceResponse( + runtime: RuntimeSnapshot, + asset: Asset, + scale: String? +): Result { + return runCatching { + when (asset.typeExtra) { + null, + ChainAssetType.Normal, + ChainAssetType.SoraUtilityAsset -> { + bindAccountInfoOrDefault(scale, runtime) + } + + ChainAssetType.OrmlChain, + ChainAssetType.OrmlAsset, + ChainAssetType.ForeignAsset, + ChainAssetType.StableAssetPoolToken, + ChainAssetType.LiquidCrowdloan, + ChainAssetType.VToken, + ChainAssetType.SoraAsset, + ChainAssetType.VSToken, + ChainAssetType.AssetId, + ChainAssetType.Token2, + ChainAssetType.Xcm, + ChainAssetType.Stable -> { + bindOrmlTokensAccountDataOrDefault(scale, runtime) + } + + ChainAssetType.Equilibrium -> { + bindEquilibriumAccountData(scale, runtime) ?: EmptyBalance + } + + ChainAssetType.Assets -> { + bindAssetsAccountData(scale, runtime) ?: EmptyBalance + } + + ChainAssetType.Unknown -> EmptyBalance + } + } +} + +suspend fun Ethereum.fetchEthBalance(asset: Asset, address: String): BigInteger { + return if (asset.isUtility) { + withContext(kotlinx.coroutines.Dispatchers.IO) { + ethGetBalance( + address, + DefaultBlockParameterName.LATEST + ).send().balance + } + } else { + val erc20GetBalanceFunction = Function( + "balanceOf", + listOf(Address(address)), + emptyList() + ) + + val erc20BalanceWei = withContext(kotlinx.coroutines.Dispatchers.IO) { + ethCall( + Transaction.createEthCallTransaction( + null, + asset.id, + FunctionEncoder.encode(erc20GetBalanceFunction) + ), + DefaultBlockParameterName.LATEST + ).send().value + } + + Numeric.decodeQuantity(erc20BalanceWei) + } +} + diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt new file mode 100644 index 0000000000..435d289f4e --- /dev/null +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -0,0 +1,238 @@ +package jp.co.soramitsu.account.impl.domain + +import android.util.Log +import java.math.BigInteger +import jp.co.soramitsu.account.api.domain.model.accountId +import jp.co.soramitsu.account.impl.data.mappers.mapMetaAccountLocalToMetaAccount +import jp.co.soramitsu.common.data.network.runtime.binding.AssetBalance +import jp.co.soramitsu.common.data.network.runtime.binding.toAssetBalance +import jp.co.soramitsu.common.utils.isNotZero +import jp.co.soramitsu.common.utils.orZero +import jp.co.soramitsu.core.models.ChainAssetType +import jp.co.soramitsu.core.utils.utilityAsset +import jp.co.soramitsu.coredb.dao.AssetDao +import jp.co.soramitsu.coredb.dao.MetaAccountDao +import jp.co.soramitsu.coredb.model.AssetLocal +import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry +import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository +import jp.co.soramitsu.runtime.multiNetwork.connection.EvmConnectionStatus +import jp.co.soramitsu.runtime.storage.source.RemoteStorageSource +import jp.co.soramitsu.shared_utils.extensions.toHexString +import jp.co.soramitsu.wallet.api.data.cache.bindEquilibriumAccountData +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.cancel +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.job +import kotlinx.coroutines.launch + +class WalletSyncService( + private val metaAccountDao: MetaAccountDao, + private val chainsRepository: ChainsRepository, + private val chainRegistry: ChainRegistry, + private val remoteStorageSource: RemoteStorageSource, + private val assetDao: AssetDao, + dispatcher: CoroutineDispatcher = Dispatchers.Default +) { + private val scope = CoroutineScope(dispatcher + SupervisorJob() + CoroutineExceptionHandler { coroutineContext, throwable -> Log.d("&&&", "WalletSyncService scope error: $throwable") }) + + init { + metaAccountDao.observeNotInitializedMetaAccounts().onEach { Log.d("&&&", "start sync ${it.size} accounts: ${it.map { it.metaAccount.name }}") }.filter { it.isNotEmpty() } + .onEach { localMetaAccounts -> + chainRegistry.configsSyncDeferred.join() + + val chains = chainsRepository.getChains() + val ethereumChains = chains.filter { it.isEthereumChain } + val substrateChains = chains.filter { !it.isEthereumChain } + + val metaAccounts = + localMetaAccounts.map { accountInfo -> + mapMetaAccountLocalToMetaAccount( + chains.associateBy { it.id }, + accountInfo + ) + } + + coroutineScope { + + launch { + ethereumChains.forEach { chain -> + launch { + val assetsDeferred = async { + if (chainRegistry.checkChainSyncedUp(chain).not()) { + chainRegistry.setupChain(chain) + } + val connection = chainRegistry.awaitEthereumConnection(chain.id) + // await connecting to the node + connection.statusFlow.first { it is EvmConnectionStatus.Connected } +// this@launch.cancel() + metaAccounts.mapNotNull { metaAccount -> + val accountId = + metaAccount.accountId(chain) ?: return@mapNotNull null + chain.assets.map { chainAsset -> + val balance = kotlin.runCatching { + connection.web3j!!.fetchEthBalance( + chainAsset, + accountId.toHexString(true) + ) + }.getOrNull() + + AssetLocal( + id = chainAsset.id, + chainId = chain.id, + accountId = accountId, + metaId = metaAccount.id, + tokenPriceId = chainAsset.priceId, + freeInPlanks = balance, + reservedInPlanks = BigInteger.ZERO, + miscFrozenInPlanks = BigInteger.ZERO, + feeFrozenInPlanks = BigInteger.ZERO, + bondedInPlanks = BigInteger.ZERO, + redeemableInPlanks = BigInteger.ZERO, + unbondingInPlanks = BigInteger.ZERO, + enabled = balance.isNotZero() || chain.rank != null && chainAsset.isUtility + ) + } + }.flatten() + + + } + val localAssets = assetsDeferred.await() + assetDao.insertAssets(localAssets) + Log.d("&&&&", "chain ${chain.name} sync completed") + } + } + }.invokeOnCompletion { Log.d("&&&&", "EVM CHAINS sync completed") } + launch { + substrateChains.onEach { chain -> + launch { + val assetsDeferred = async { + if (chainRegistry.checkChainSyncedUp(chain).not()) { + chainRegistry.setupChain(chain) + } + val emptyAssets: MutableList = mutableListOf() + // awaiting runtime snapshot + val runtime = chainRegistry.awaitRuntimeProvider(chain.id).get() + + val allAccountsStorageKeys = + metaAccounts.mapNotNull { metaAccount -> + val accountId = + metaAccount.accountId(chain) + ?: return@mapNotNull null + buildStorageKeys( + chain, + runtime, + metaAccount.id, + accountId + ) + }.flatten().associateBy { it.key } + + val keysToQuery = + allAccountsStorageKeys.mapNotNull { (storageKey, metadata) -> + // if storage key build is failed - we put the empty assets + if (storageKey == null) { + // filling all the equilibrium assets + if (chain.utilityAsset != null && chain.utilityAsset?.typeExtra == ChainAssetType.Equilibrium) { + chain.assets.map { + emptyAssets.add( + AssetLocal.createEmpty( + accountId = metadata.accountId, + assetId = it.id, + chainId = it.chainId, + metaId = metadata.metaAccountId, + priceId = it.priceId, + enabled = false + ) + ) + } + } else { + emptyAssets.add( + AssetLocal.createEmpty( + accountId = metadata.accountId, + assetId = metadata.asset.id, + chainId = metadata.asset.chainId, + metaId = metadata.metaAccountId, + priceId = metadata.asset.priceId, + enabled = false + ) + ) + } + } + storageKey + }.toList() + + val storageKeyToResult = remoteStorageSource.queryKeys( + keysToQuery, + chain.id, + null + ) + + val equilibriumBalanceMap = + if (chain.utilityAsset != null && chain.utilityAsset!!.typeExtra == ChainAssetType.Equilibrium) { + val result = + storageKeyToResult.values.firstNotNullOfOrNull { it } + val balanceData = + bindEquilibriumAccountData(result, runtime) + balanceData?.data?.balances.orEmpty() + } else { + null + } + + allAccountsStorageKeys.map { (storageKey, metadata) -> + val hexRaw = + storageKeyToResult.getOrDefault(storageKey, null) + + val assetBalance = if (equilibriumBalanceMap != null) { + val balance = + metadata.asset.currencyId?.toBigInteger()?.let { + equilibriumBalanceMap.getOrDefault(it, null) + .orZero() + }.orZero() + AssetBalance(freeInPlanks = balance) + } else { + handleBalanceResponse( + runtime, + metadata.asset, + hexRaw + ).getOrNull().toAssetBalance() + + } ?: AssetBalance() + + AssetLocal( + id = metadata.asset.id, + chainId = chain.id, + accountId = metadata.accountId, + metaId = metadata.metaAccountId, + tokenPriceId = metadata.asset.priceId, + freeInPlanks = assetBalance.freeInPlanks, + reservedInPlanks = assetBalance.reservedInPlanks, + miscFrozenInPlanks = assetBalance.miscFrozenInPlanks, + feeFrozenInPlanks = assetBalance.feeFrozenInPlanks, + bondedInPlanks = assetBalance.bondedInPlanks, + redeemableInPlanks = assetBalance.redeemableInPlanks, + unbondingInPlanks = assetBalance.unbondingInPlanks, + enabled = assetBalance.freeInPlanks.isNotZero() || chain.rank != null && metadata.asset.isUtility + ) + } + emptyAssets + } + val localAssets = assetsDeferred.await() + assetDao.insertAssets(localAssets) + Log.d("&&&&", "chain ${chain.name} sync completed") + } + } + }.invokeOnCompletion { Log.d("&&&&", "SUBSTRATE CHAINS sync completed") } + this + }.coroutineContext.job.join() + Log.d("&&&", "marking accounts initialized ${metaAccounts.map { it.name }}") + metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) + }.launchIn(scope) + } +} \ No newline at end of file diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt index 03abfc1031..1ebc88d50d 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt @@ -139,10 +139,9 @@ class ConfirmMnemonicViewModel @Inject constructor( val result = interactor.createAccount(accountName, mnemonicString, cryptoType, substrateDerivationPath, ethereumDerivationPath, isBackedUp) if (result.isSuccess) { +// setupNewAccountAssetsVisibility() + continueBasedOnCodeStatus() - kotlinx.coroutines.MainScope().launch { - setupNewAccountAssetsVisibility() - } } else { showError(result.requireException()) } diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt index 8700c5da16..004824d4bc 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt @@ -33,7 +33,7 @@ class NFTTransferInteractorImpl( ) : NFTTransferInteractor { private fun getWeb3Connection(chainId: ChainId): EthereumChainConnection { - return ethereumConnectionPool.get(chainId) ?: error( + return ethereumConnectionPool.getOrNull(chainId) ?: error( """ EthereumConnection to chain with id - $chainId - could not have been established. """.trimIndent() diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCacheExt.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCacheExt.kt index a68fa98d34..88727460df 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCacheExt.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCacheExt.kt @@ -111,7 +111,7 @@ fun bindOrmlTokensAccountDataOrDefault(hex: String?, runtime: RuntimeSnapshot): } fun bindEquilibriumAccountData(hex: String?, runtime: RuntimeSnapshot): EqAccountInfo? { - return hex?.let { bindEquilibriumAccountInfo(it, runtime) } + return hex?.let { runCatching { bindEquilibriumAccountInfo(it, runtime) }.getOrNull() } } fun bindAssetsAccountData(hex: String?, runtime: RuntimeSnapshot): AssetsAccountInfo? { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/EthereumRemoteSource.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/EthereumRemoteSource.kt index 829416cd80..3e7650249d 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/EthereumRemoteSource.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/EthereumRemoteSource.kt @@ -1,5 +1,6 @@ package jp.co.soramitsu.wallet.impl.data.network.blockchain +import android.util.Log import java.math.BigInteger import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.domain.model.address @@ -19,8 +20,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.withContext @@ -56,10 +59,10 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio chain: Chain, accountId: AccountId ): Result { - val connection = ethereumConnectionPool.get(chain.id) + val connection = ethereumConnectionPool.await(chain.id) return kotlin.runCatching { - connection?.web3j!!.fetchEthBalance( + connection.web3j!!.fetchEthBalance( chainAsset, accountId.toHexString(true) ) @@ -72,7 +75,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio privateKey: String ): Result = withContext(Dispatchers.IO) { - val connection = ethereumConnectionPool.get(chain.id) + val connection = ethereumConnectionPool.await(chain.id) ?: return@withContext Result.failure("There is no connection created for chain ${chain.name}, ${chain.id}") val web3 = connection.web3j ?: return@withContext Result.failure("There is no connection established for chain ${chain.name}, ${chain.id}") @@ -99,7 +102,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio chain: Chain, account: MetaAccount ): Result>>> { - val connection = ethereumConnectionPool.get(chain.id) + val connection = ethereumConnectionPool.await(chain.id) val web3 = connection?.web3j ?: return Result.failure("There is no connection created for chain ${chain.name}, ${chain.id}") @@ -134,7 +137,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio asset: Asset, address: String ): BigInteger { - val connection = ethereumConnectionPool.get(asset.chainId) + val connection = ethereumConnectionPool.await(asset.chainId) val web3 = connection?.web3j ?: throw RuntimeException("There is no connection created for chain ${asset.chainId}") @@ -145,7 +148,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio chainId: ChainId, receiverAddress: String ): BigInteger? { - val connection = ethereumConnectionPool.get(chainId) + val connection = ethereumConnectionPool.await(chainId) val web3 = connection?.web3j ?: throw RuntimeException("There is no connection created for chain ${chainId}") @@ -222,7 +225,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio } fun listenGas(transfer: Transfer, chain: Chain): Flow { - val connection = requireNotNull(ethereumConnectionPool.get(chain.id)) + val connection = requireNotNull(ethereumConnectionPool.getOrNull(chain.id)) val web3j = requireNotNull(connection.web3j) val wsService = requireNotNull(connection.service) val transactionBuilder = EthereumTransactionBuilder(connection) @@ -268,7 +271,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio raw: RawTransaction, privateKey: String ): Result = withContext(Dispatchers.IO) { - val connection = ethereumConnectionPool.get(chainId) + val connection = ethereumConnectionPool.await(chainId) ?: return@withContext Result.failure("There is no connection created for chain with id = $chainId") val web3 = connection.web3j ?: return@withContext Result.failure("There is no connection established for chain with id = $chainId") @@ -287,7 +290,7 @@ class EthereumRemoteSource(private val ethereumConnectionPool: EthereumConnectio raw: RawTransaction, privateKey: String ): Result = withContext(Dispatchers.IO) { - val connection = ethereumConnectionPool.get(chainId) + val connection = ethereumConnectionPool.await(chainId) ?: return@withContext Result.failure("There is no connection created for chain with id = $chainId") val web3 = connection.web3j diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt index 0f00bd29dd..6ea7da4969 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt @@ -9,6 +9,8 @@ import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.account.api.domain.model.address import jp.co.soramitsu.account.impl.data.mappers.mapMetaAccountLocalToMetaAccount +import jp.co.soramitsu.account.impl.domain.buildStorageKeys +import jp.co.soramitsu.account.impl.domain.handleBalanceResponse import jp.co.soramitsu.common.data.network.rpc.BulkRetriever import jp.co.soramitsu.common.data.network.runtime.binding.ExtrinsicStatusEvent import jp.co.soramitsu.common.data.network.runtime.binding.SimpleBalanceData @@ -17,7 +19,6 @@ import jp.co.soramitsu.common.utils.failure import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.common.utils.requireException import jp.co.soramitsu.common.utils.requireValue -import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.core.models.ChainAssetType import jp.co.soramitsu.core.updater.UpdateSystem import jp.co.soramitsu.core.updater.Updater @@ -33,7 +34,6 @@ import jp.co.soramitsu.runtime.multiNetwork.getSocketOrNull import jp.co.soramitsu.runtime.multiNetwork.toSyncIssue import jp.co.soramitsu.runtime.network.subscriptionFlowCatching import jp.co.soramitsu.shared_utils.runtime.AccountId -import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.RuntimeRequest import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.storage.storageChange import jp.co.soramitsu.wallet.api.data.cache.AssetCache @@ -43,8 +43,6 @@ import jp.co.soramitsu.wallet.impl.data.mappers.mapOperationStatusToOperationLoc import jp.co.soramitsu.wallet.impl.data.network.blockchain.EthereumRemoteSource import jp.co.soramitsu.wallet.impl.data.network.blockchain.SubstrateRemoteSource import jp.co.soramitsu.wallet.impl.data.network.blockchain.bindings.TransferExtrinsic -import jp.co.soramitsu.wallet.impl.data.network.model.constructBalanceKey -import jp.co.soramitsu.wallet.impl.data.network.model.handleBalanceResponse import jp.co.soramitsu.wallet.impl.domain.model.Operation import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope @@ -55,6 +53,7 @@ import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest @@ -72,9 +71,7 @@ private const val RUNTIME_AWAITING_TIMEOUT = 10_000L @SuppressLint("LogNotTimber") class BalancesUpdateSystem( private val chainRegistry: ChainRegistry, - private val accountRepository: AccountRepository, private val metaAccountDao: MetaAccountDao, - private val bulkRetriever: BulkRetriever, private val assetCache: AssetCache, private val substrateSource: SubstrateRemoteSource, private val operationDao: OperationDao, @@ -166,7 +163,7 @@ class BalancesUpdateSystem( .getOrNull() ?: return@flatMapLatest flowOf(Result.failure(RuntimeException("Error getting socket for chain ${chain.name}"))) - val request = SubscribeBalanceRequest(storageKeys.map { it.key }) + val request = SubscribeBalanceRequest(storageKeys.mapNotNull { it.key }) combine(socketService.subscriptionFlowCatching(request)) { subscriptionsChangeResults -> subscriptionsChangeResults.forEach { subscriptionChangeResult -> @@ -233,143 +230,6 @@ class BalancesUpdateSystem( return chainUpdateFlow } - private val ethBalancesFlow = combine(chainRegistry.syncedChains, - accountRepository.allMetaAccountsFlow().filterNot { it.isEmpty() }) { chains, accounts -> - val filtered = chains.filter { it.isEthereumChain } - coroutineScope { - filtered.forEach { chain -> - launch { - fetchEthereumBalances(chain, accounts) - } - } - } - } - - private val substrateBalancesFlow = combine(chainRegistry.syncedChains, - accountRepository.allMetaAccountsFlow().filterNot { it.isEmpty() }) { chains, accounts -> - coroutineScope { - val filtered = chains.filterNot { it.isEthereumChain } - filtered.forEach { chain -> - launch { - runCatching { - val runtime = - runCatching { chainRegistry.getRuntimeOrNull(chain.id) }.getOrNull() - ?: return@launch - val socketService = - runCatching { chainRegistry.getSocket(chain.id) }.getOrNull() - ?: return@launch - val storageKeys = - accounts.mapNotNull { metaAccount -> - buildStorageKeys(chain, metaAccount, runtime) - .onFailure { } - .getOrNull()?.toList() - }.flatten() - val queryResults = withContext(Dispatchers.IO) { - bulkRetriever.queryKeys( - socketService, - storageKeys.map { it.key } - ) - } - - storageKeys.map { keyWithMetadata -> - val hexRaw = - queryResults[keyWithMetadata.key] - - val balanceData = handleBalanceResponse( - runtime, - keyWithMetadata.asset, - hexRaw - ).onFailure { logError(chain, it) } - - assetCache.updateAsset( - keyWithMetadata.metaAccountId, - keyWithMetadata.accountId, - keyWithMetadata.asset, - balanceData.getOrNull() - ) - } - } - .onFailure { - logError(chain, it) - return@launch - } - } - } - } - } - - private fun singleUpdateFlow(): Flow { - return combine( - ethBalancesFlow, - substrateBalancesFlow - ) { _, _ -> - }.onStart { emit(Unit) }.flowOn(Dispatchers.Default) - } - - private fun buildStorageKeys( - chain: Chain, - metaAccount: MetaAccount, - runtime: RuntimeSnapshot - ): Result> { - val accountId = metaAccount.accountId(chain) - ?: return Result.failure(RuntimeException("Can't get account id for meta account ${metaAccount.name}, chain: ${chain.name}")) - - if (chain.utilityAsset != null && chain.utilityAsset?.typeExtra == ChainAssetType.Equilibrium) { - val equilibriumStorageKeys = listOf( - constructBalanceKey( - runtime, - requireNotNull(chain.utilityAsset), - accountId - )?.let { - StorageKeyWithMetadata( - requireNotNull(chain.utilityAsset), - metaAccount.id, - accountId, - it - ) - }) - return Result.success(equilibriumStorageKeys.filterNotNull()) - } - - val storageKeys = chain.assets.map { asset -> - constructBalanceKey(runtime, asset, accountId)?.let { - StorageKeyWithMetadata(asset, metaAccount.id, accountId, it) - } - } - return Result.success(storageKeys.filterNotNull()) - } - - data class StorageKeyWithMetadata( - val asset: Asset, - val metaAccountId: Long, - val accountId: AccountId, - val key: String - ) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as StorageKeyWithMetadata - - if (asset != other.asset) return false - if (metaAccountId != other.metaAccountId) return false - if (!accountId.contentEquals(other.accountId)) return false - - return true - } - - override fun hashCode(): Int { - var result = asset.hashCode() - result = 31 * result + metaAccountId.hashCode() - result = 31 * result + accountId.contentHashCode() - return result - } - - override fun toString(): String { - return "StorageKeyWithMetadata(asset=${asset.name}, metaAccountId=$metaAccountId, key='$key')" - } - } - private suspend fun fetchEthereumBalances( chain: Chain, accounts: List @@ -461,7 +321,7 @@ class BalancesUpdateSystem( } override fun start(): Flow { - return combine(subscribeFlow(), singleUpdateFlow()) { sideEffect, _ -> sideEffect } + return emptyFlow()//subscribeFlow() } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/model/SubstrateBalancesUtils.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/model/SubstrateBalancesUtils.kt deleted file mode 100644 index ebc8e194f3..0000000000 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/model/SubstrateBalancesUtils.kt +++ /dev/null @@ -1,105 +0,0 @@ -package jp.co.soramitsu.wallet.impl.data.network.model - -import android.util.Log -import jp.co.soramitsu.common.data.network.runtime.binding.AssetBalanceData -import jp.co.soramitsu.common.data.network.runtime.binding.EmptyBalance -import jp.co.soramitsu.common.utils.Modules -import jp.co.soramitsu.common.utils.system -import jp.co.soramitsu.common.utils.tokens -import jp.co.soramitsu.core.models.Asset -import jp.co.soramitsu.core.models.ChainAssetType -import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot -import jp.co.soramitsu.shared_utils.runtime.metadata.module -import jp.co.soramitsu.shared_utils.runtime.metadata.storage -import jp.co.soramitsu.shared_utils.runtime.metadata.storageKey -import jp.co.soramitsu.wallet.api.data.cache.bindAccountInfoOrDefault -import jp.co.soramitsu.wallet.api.data.cache.bindAssetsAccountData -import jp.co.soramitsu.wallet.api.data.cache.bindEquilibriumAccountData -import jp.co.soramitsu.wallet.api.data.cache.bindOrmlTokensAccountDataOrDefault - - -fun constructBalanceKey( - runtime: RuntimeSnapshot, - asset: Asset, - accountId: ByteArray -): String? { - val keyConstructionResult = runCatching { - val currency = - asset.currency ?: return@runCatching runtime.metadata.system().storage("Account") - .storageKey(runtime, accountId) - when (asset.typeExtra) { - null, ChainAssetType.Normal, - ChainAssetType.Equilibrium, - ChainAssetType.SoraUtilityAsset -> runtime.metadata.system().storage("Account") - .storageKey(runtime, accountId) - - ChainAssetType.OrmlChain, - ChainAssetType.OrmlAsset, - ChainAssetType.VToken, - ChainAssetType.VSToken, - ChainAssetType.Stable, - ChainAssetType.ForeignAsset, - ChainAssetType.StableAssetPoolToken, - ChainAssetType.SoraAsset, - ChainAssetType.AssetId, - ChainAssetType.Token2, - ChainAssetType.Xcm, - ChainAssetType.LiquidCrowdloan -> runtime.metadata.tokens().storage("Accounts") - .storageKey(runtime, accountId, currency) - - ChainAssetType.Assets -> runtime.metadata.module(Modules.ASSETS).storage("Account") - .storageKey(runtime, currency, accountId) - - ChainAssetType.Unknown -> error("Not supported type for token ${asset.symbol} in ${asset.chainName}") - } - } - return keyConstructionResult - .onFailure { - Log.d( - "BalancesUpdateSystem", - "Failed to construct storage key for asset ${asset.symbol} (${asset.id}) $it " - ) - } - .getOrNull() -} - -fun handleBalanceResponse( - runtime: RuntimeSnapshot, - asset: Asset, - scale: String? -): Result { - return runCatching { - when (asset.typeExtra) { - null, - ChainAssetType.Normal, - ChainAssetType.SoraUtilityAsset -> { - bindAccountInfoOrDefault(scale, runtime) - } - - ChainAssetType.OrmlChain, - ChainAssetType.OrmlAsset, - ChainAssetType.ForeignAsset, - ChainAssetType.StableAssetPoolToken, - ChainAssetType.LiquidCrowdloan, - ChainAssetType.VToken, - ChainAssetType.SoraAsset, - ChainAssetType.VSToken, - ChainAssetType.AssetId, - ChainAssetType.Token2, - ChainAssetType.Xcm, - ChainAssetType.Stable -> { - bindOrmlTokensAccountDataOrDefault(scale, runtime) - } - - ChainAssetType.Equilibrium -> { - bindEquilibriumAccountData(scale, runtime) ?: EmptyBalance - } - - ChainAssetType.Assets -> { - bindAssetsAccountData(scale, runtime) ?: EmptyBalance - } - - ChainAssetType.Unknown -> EmptyBalance - } - } -} diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt index e7a2876a73..244ff4f3fc 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt @@ -12,6 +12,7 @@ import javax.inject.Named import javax.inject.Singleton import jp.co.soramitsu.account.api.domain.interfaces.AccountInteractor import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository +import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.account.impl.presentation.account.mixin.api.AccountListingMixin import jp.co.soramitsu.account.impl.presentation.account.mixin.impl.AccountListingProvider import jp.co.soramitsu.common.address.AddressIconGenerator @@ -45,6 +46,7 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumConnectionPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeFilesCache +import jp.co.soramitsu.runtime.storage.source.RemoteStorageSource import jp.co.soramitsu.runtime.storage.source.StorageDataSource import jp.co.soramitsu.wallet.api.data.cache.AssetCache import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase @@ -203,6 +205,24 @@ class WalletFeatureModule { remoteStorageSource ) + @Provides + @Singleton + fun provideWalletSyncService( + metaAccountDao: MetaAccountDao, + chainsRepository: ChainsRepository, + chainRegistry: ChainRegistry, + remoteStorageSource: RemoteStorageSource, + assetDao: AssetDao, + ): WalletSyncService { + return WalletSyncService( + metaAccountDao, + chainsRepository, + chainRegistry, + remoteStorageSource, + assetDao + ) + } + @Provides @Singleton fun provideHistoryRepository( @@ -355,9 +375,7 @@ class WalletFeatureModule { @Named("BalancesUpdateSystem") fun provideFeatureUpdaters( chainRegistry: ChainRegistry, - accountRepository: AccountRepository, metaAccountDao: MetaAccountDao, - bulkRetriever: BulkRetriever, assetCache: AssetCache, substrateSource: SubstrateRemoteSource, operationDao: OperationDao, @@ -365,9 +383,7 @@ class WalletFeatureModule { ethereumRemoteSource: EthereumRemoteSource ): UpdateSystem = BalancesUpdateSystem( chainRegistry, - accountRepository, metaAccountDao, - bulkRetriever, assetCache, substrateSource, operationDao, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixin.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixin.kt index 194997c5b0..b589b9049f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixin.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixin.kt @@ -39,9 +39,9 @@ interface BuyMixin { override val integrateWithBuyProviderEvent: MutableLiveData> - fun buyClicked(chainId: ChainId, chainAssetId: String, accountAddress: String) + suspend fun buyClicked(chainId: ChainId, chainAssetId: String, accountAddress: String) - fun isBuyEnabled(chainId: ChainId, chainAssetId: String): Boolean + suspend fun isBuyEnabled(chainId: ChainId, chainAssetId: String): Boolean } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixinProvider.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixinProvider.kt index 97d37249fb..b20f29f92e 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixinProvider.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetActions/buy/BuyMixinProvider.kt @@ -19,7 +19,7 @@ class BuyMixinProvider( override val integrateWithBuyProviderEvent = MutableLiveData>() - override fun isBuyEnabled(chainId: ChainId, chainAssetId: String) = + override suspend fun isBuyEnabled(chainId: ChainId, chainAssetId: String) = when (val asset = chainRegistry.getAsset(chainId, chainAssetId)) { null -> false else -> buyTokenRegistry.availableProviders(asset).isNotEmpty() @@ -39,7 +39,7 @@ class BuyMixinProvider( integrateWithBuyProviderEvent.value = Event(payload) } - override fun buyClicked(chainId: ChainId, chainAssetId: String, accountAddress: String) { + override suspend fun buyClicked(chainId: ChainId, chainAssetId: String, accountAddress: String) { val asset = chainRegistry.getAsset(chainId, chainAssetId) val availableProviders = asset?.let { buyTokenRegistry.availableProviders(it) } ?: emptyList() diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt index 1af8864e2c..ab0f29c879 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/detail/BalanceDetailViewModel.kt @@ -150,7 +150,7 @@ class BalanceDetailViewModel @Inject constructor( } } - private fun isBuyEnabled(): Boolean { + private suspend fun isBuyEnabled(): Boolean { return buyMixin.isBuyEnabled( assetPayload.value.chainId, assetPayload.value.chainAssetId @@ -379,7 +379,7 @@ class BalanceDetailViewModel @Inject constructor( return actionItems } - private fun getDisabledItems(): List { + private suspend fun getDisabledItems(): List { return if (!isBuyEnabled()) { listOf(ActionItemType.BUY) } else { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 1229419dd3..81af9d60ae 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -263,7 +263,8 @@ class BalanceListViewModel @Inject constructor( } assetStates - }.onStart { emit(buildInitialAssetsList().toMutableList()) }.inBackground().share() + }//.onStart { emit(buildInitialAssetsList().toMutableList()) } + .inBackground().share() @OptIn(FlowPreview::class) private fun createNFTCollectionScreenViewsFlow(): Flow, ScreenLayout>> { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/receive/ReceiveViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/receive/ReceiveViewModel.kt index ce11237f8d..a45babd204 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/receive/ReceiveViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/receive/ReceiveViewModel.kt @@ -73,8 +73,6 @@ class ReceiveViewModel @Inject constructor( private val assetPayload = savedStateHandle.get(ReceiveFragment.KEY_ASSET_PAYLOAD)!! - private val assetSymbolToShow = chainRegistry.getAsset(assetPayload.chainId, assetPayload.chainAssetId)?.symbol - private val accountFlow = interactor.selectedAccountFlow(assetPayload.chainId) private val assetFlow = chainAssetsManager.assetFlow.onStart { emit(interactor.getCurrentAsset(assetPayload.chainId, assetPayload.chainAssetId)) @@ -165,11 +163,13 @@ class ReceiveViewModel @Inject constructor( soraMainChainId, soraTestChainId ) + val assetSymbol = chainRegistry.getAsset(assetPayload.chainId, assetPayload.chainAssetId)?.symbol + LoadingState.Loaded( ReceiveScreenViewState( account = account, qrCode = qrCode, - assetSymbol = assetSymbolToShow.orEmpty().uppercase(), + assetSymbol = assetSymbol.orEmpty().uppercase(), multiToggleButtonState = receiveTypeState, amountInputViewState = amountInputViewState, requestAllowed = allowRequest diff --git a/runtime/build.gradle b/runtime/build.gradle index 8d58334593..9bc69bea68 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -18,7 +18,7 @@ android { buildTypes { debug { - buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/develop-free/chains/v9/chains_dev.json\"" + buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/master/chains/v9/chains.json\"" } release { diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt index 64b64a7522..ec161e04e8 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt @@ -158,7 +158,8 @@ class ChainRegistryModule { updatesMixin: UpdatesMixin, networkStateMixin: NetworkStateMixin, ethereumConnectionPool: EthereumConnectionPool, - assetReadOnlyCache: AssetDao + assetReadOnlyCache: AssetDao, + chainsRepository: ChainsRepository ): ChainRegistry = ChainRegistry( runtimeProviderPool, chainConnectionPool, @@ -169,7 +170,8 @@ class ChainRegistryModule { updatesMixin, networkStateMixin, ethereumConnectionPool, - assetReadOnlyCache + assetReadOnlyCache, + chainsRepository ) @Provides diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/di/RuntimeModule.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/di/RuntimeModule.kt index ce3414cdf3..02ad3e28e9 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/di/RuntimeModule.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/di/RuntimeModule.kt @@ -54,12 +54,18 @@ class RuntimeModule { ): StorageDataSource = LocalStorageSource(chainRegistry, storageCache) @Provides - @Named(REMOTE_STORAGE_SOURCE) @Singleton fun provideRemoteStorageSource( chainRegistry: ChainRegistry, bulkRetriever: BulkRetriever - ): StorageDataSource = RemoteStorageSource(chainRegistry, bulkRetriever) + ): RemoteStorageSource = RemoteStorageSource(chainRegistry, bulkRetriever) + + @Provides + @Named(REMOTE_STORAGE_SOURCE) + @Singleton + fun provideRemoteStorageDataSource( + remoteStorageSource: RemoteStorageSource + ): StorageDataSource = remoteStorageSource @Provides @Singleton diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 8c0f176e64..6ab351b1a4 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -21,18 +21,21 @@ import jp.co.soramitsu.coredb.dao.AssetReadOnlyCache import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.coredb.model.chain.ChainNodeLocal import jp.co.soramitsu.runtime.multiNetwork.chain.ChainSyncService +import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.chain.mapChainLocalToChain import jp.co.soramitsu.runtime.multiNetwork.chain.mapNodeLocalToNode import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.NodeId import jp.co.soramitsu.runtime.multiNetwork.connection.ConnectionPool +import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumChainConnection import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumConnectionPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProvider import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProviderPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeSubscriptionPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeSyncService import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot +import jp.co.soramitsu.shared_utils.wsrpc.state.SocketStateMachine import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -40,7 +43,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -53,7 +55,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -73,6 +74,7 @@ class ChainRegistry @Inject constructor( private val networkStateMixin: NetworkStateMixin, private val ethereumConnectionPool: EthereumConnectionPool, private val assetsCache: AssetReadOnlyCache, + private val chainsRepository: ChainsRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.Default ) : IChainRegistry, UpdatesProviderUi by updatesMixin { @@ -109,23 +111,25 @@ class ChainRegistry @Inject constructor( .filter { it.addedOrModified.isNotEmpty() || it.removed.isNotEmpty() } .flowOn(dispatcher) + val configsSyncDeferred = scope.async { + launch { chainSyncService.syncUp() } + launch { runtimeSyncService.syncTypes() } + } + init { syncUp() } fun syncUp() { scope.launch { - coroutineScope { - launch { chainSyncService.syncUp() } - launch { runtimeSyncService.syncTypes() } - }.join() + configsSyncDeferred.join() chainsToSync .onEach { (removed, addedOrModified, all) -> coroutineScope { -// val removedDeferred = removed.map { -// async { removeChain(it) } -// } + val removedDeferred = removed.map { + async { connectionPool.getConnectionOrNull(it.id)?.socketService?.pause() } + } updatesMixin.startChainsSyncUp(addedOrModified.filter { it.nodes.isNotEmpty() } .map { it.id }) @@ -148,11 +152,7 @@ class ChainRegistry @Inject constructor( } } - syncDeferred.awaitAll() - } - if(this@ChainRegistry.syncedChains.value.isEmpty()) { - // initial sync is done - + (removedDeferred + syncDeferred).awaitAll() } this@ChainRegistry.syncedChains.emit(all) @@ -161,7 +161,7 @@ class ChainRegistry @Inject constructor( } } - private fun removeChain(chain: Chain) { + fun stopChain(chain: Chain) { val chainId = chain.id if (chain.isEthereumChain) { ethereumConnectionPool.stop(chainId) @@ -173,19 +173,39 @@ class ChainRegistry @Inject constructor( connectionPool.removeConnection(chainId) } - private fun setupChain(chain: Chain) { + suspend fun setupChain(chain: Chain) { if (chain.isEthereumChain) { ethereumConnectionPool.setupConnection(chain, ::notifyNodeSwitched) return } - val connection = connectionPool.setupConnection(chain, ::notifyNodeSwitched) + val connection = connectionPool.getConnectionOrNull(chain.id)?.let { + if (it.state.value is SocketStateMachine.State.Paused) { + it.socketService.resume() + } + it + } ?: connectionPool.setupConnection(chain, ::notifyNodeSwitched) + + if (runtimeProviderPool.getRuntimeProviderOrNull(chain.id)?.getOrNull() != null) return + + if (connection.state.value is SocketStateMachine.State.Disconnected) { + connection.socketService.start(chain.nodes.first().url) + } runtimeSubscriptionPool.setupRuntimeSubscription(chain, connection) runtimeSyncService.registerChain(chain) runtimeProviderPool.setupRuntimeProvider(chain) } + suspend fun checkChainSyncedUp(chain: Chain): Boolean { + if (chain.isEthereumChain) { + return ethereumConnectionPool.getOrNull(chain.id) != null + } + val runtime = runtimeProviderPool.getRuntimeProviderOrNull(chain.id)?.getOrNull() + + return connectionPool.getConnectionOrNull(chain.id) != null && runtime != null + } + override fun getConnection(chainId: String) = connectionPool.getConnection(chainId) @Deprecated( @@ -193,35 +213,36 @@ class ChainRegistry @Inject constructor( ReplaceWith("getRuntimeOrNull(chainId)") ) override suspend fun getRuntime(chainId: ChainId): RuntimeSnapshot { - return getRuntimeProvider(chainId).get() + return awaitRuntimeProvider(chainId).get() } suspend fun getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { - return kotlin.runCatching { getRuntimeProvider(chainId).getOrNullWithTimeout() } - .getOrNull() + return getRuntimeProviderOrNull(chainId)?.getOrNull() } fun getConnectionOrNull(chainId: String) = connectionPool.getConnectionOrNull(chainId) - suspend fun getRuntimeProvider(chainId: String): RuntimeProvider { - return runtimeProviderPool.getRuntimeProvider(chainId) + suspend fun awaitRuntimeProvider(chainId: String): RuntimeProvider { + return runtimeProviderPool.awaitRuntimeProvider(chainId) } suspend fun getRuntimeProviderOrNull(chainId: String): RuntimeProvider? { return runtimeProviderPool.getRuntimeProviderOrNull(chainId) } - fun getAsset(chainId: ChainId, chainAssetId: String) = - chainsById.replayCache.lastOrNull()?.get(chainId)?.assets?.firstOrNull { - it.id == chainAssetId - } + fun getEthereumConnection(chainId: String) = ethereumConnectionPool.getOrNull(chainId) + suspend fun awaitEthereumConnection(chainId: String) = ethereumConnectionPool.await(chainId) + + suspend fun getAsset(chainId: ChainId, chainAssetId: String): Asset? { + return getChain(chainId).assetsById[chainAssetId] + } override suspend fun getChain(chainId: ChainId): Chain { - return chainsById.first().getValue(chainId) + return chainsRepository.getChain(chainId) } override suspend fun getChains(): List { - return chainsById.first().values.toList() + return chainsRepository.getChains() } fun nodesFlow(chainId: String) = chainDao.nodesFlow(chainId) @@ -267,21 +288,17 @@ class ChainRegistry @Inject constructor( } } -suspend fun ChainRegistry.getChain(chainId: ChainId): Chain { - return getChain(chainId) -} - suspend fun ChainRegistry.chainWithAsset( chainId: ChainId, assetId: String ): Pair { - val chain = chainsById.first().getValue(chainId) + val chain = getChain(chainId) return chain to chain.assetsById.getValue(assetId) } suspend fun ChainRegistry.getRuntime(chainId: ChainId): RuntimeSnapshot { - return getRuntimeProvider(chainId).get() + return awaitRuntimeProvider(chainId).get() } suspend fun ChainRegistry.getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { @@ -289,7 +306,7 @@ suspend fun ChainRegistry.getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { } suspend fun ChainRegistry.getRuntimeCatching(chainId: ChainId): Result { - val providerResult = kotlin.runCatching { getRuntimeProvider(chainId) } + val providerResult = kotlin.runCatching { awaitRuntimeProvider(chainId) } return if (providerResult.isFailure) { Result.failure(providerResult.requireException()) @@ -304,7 +321,7 @@ fun ChainRegistry.getSocketOrNull(chainId: ChainId) = suspend fun ChainRegistry.getService(chainId: ChainId): ChainService { return ChainService( - runtimeProvider = getRuntimeProvider(chainId), + runtimeProvider = awaitRuntimeProvider(chainId), connection = getConnection(chainId) ) } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt index cab36eaf18..d52aa1a90e 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt @@ -60,9 +60,15 @@ class ChainSyncService( .flatten() val newLocalAssets = metaAccounts.map { metaAccount -> - newAssets.map { + newAssets.mapNotNull { + val chain = remoteMapping[it.chainId] + val accountId = if(chain?.isEthereumBased == true) { + metaAccount.ethereumAddress + } else { + metaAccount.substrateAccountId + } ?: return@mapNotNull null AssetLocal( - accountId = metaAccount.substrateAccountId, + accountId = accountId, id = it.id, chainId = it.chainId, metaId = metaAccount.id, diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt index 6130a8cfd1..9d6c015dda 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt @@ -4,6 +4,7 @@ import android.util.Log import java.net.URI import java.util.concurrent.ConcurrentHashMap import jp.co.soramitsu.common.BuildConfig +import jp.co.soramitsu.common.data.network.runtime.binding.cast import jp.co.soramitsu.common.mixin.api.NetworkStateMixin import jp.co.soramitsu.common.utils.cycle import jp.co.soramitsu.core.models.ChainNode @@ -16,13 +17,16 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.goerliChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polygonChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polygonTestnetChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.sepoliaChainId +import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProvider import jp.co.soramitsu.runtime.multiNetwork.toSyncIssue import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -38,31 +42,38 @@ private const val EVM_CONNECTION_TAG = "EVM Connection" class EthereumConnectionPool( private val networkStateMixin: NetworkStateMixin, ) { - private val pool = ConcurrentHashMap() + private val poolStateFlow = + MutableStateFlow>(mutableMapOf()) + suspend fun await(chainId: String): EthereumChainConnection { + return poolStateFlow.map { it.getOrDefault(chainId, null) }.first { it != null }.cast() + } - fun setupConnection( - chain: Chain, - onSelectedNodeChange: (chainId: ChainId, newNodeUrl: String) -> Unit - ) { - pool[chain.id] = EthereumChainConnection( - chain, - onSelectedNodeChange = onSelectedNodeChange - ) { networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) } + fun getOrNull(chainId: String): EthereumChainConnection? { + return poolStateFlow.value.getOrDefault(chainId, null) } - fun get(chainId: String): EthereumChainConnection? { - val connection = pool.getOrDefault(chainId, null) - if (connection != null && connection.statusFlow.value !is EvmConnectionStatus.Connected) { - connection.statusFlow.update { null } + fun setupConnection(chain: Chain, onSelectedNodeChange: (chainId: ChainId, newNodeUrl: String) -> Unit): EthereumChainConnection { + if (poolStateFlow.value.containsKey(chain.id)) { + return poolStateFlow.value.getValue(chain.id) + } else { + poolStateFlow.update { prev -> + prev.also { + it[chain.id] = EthereumChainConnection( + chain, + onSelectedNodeChange = onSelectedNodeChange + ) { networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) } + } + } + return poolStateFlow.value.getValue(chain.id) } - return connection } fun stop(chainId: String) { - pool.getOrDefault(chainId, null)?.let { - it.web3j?.shutdown() - pool.remove(chainId) + poolStateFlow.update { prev -> + prev.also { + it.remove(chainId)?.apply { web3j?.shutdown() } + } } } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt index 05f8d0a691..4c1e8ce3bc 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt @@ -1,10 +1,14 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime -import java.util.concurrent.ConcurrentHashMap +import jp.co.soramitsu.common.data.network.runtime.binding.cast import jp.co.soramitsu.common.mixin.api.NetworkStateMixin import jp.co.soramitsu.core.runtime.RuntimeFactory import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update class RuntimeProviderPool( private val runtimeFactory: RuntimeFactory, @@ -14,30 +18,43 @@ class RuntimeProviderPool( private val networkStateMixin: NetworkStateMixin ) { - private val pool = ConcurrentHashMap() + private val poolStateFlow = + MutableStateFlow>(mutableMapOf()) - fun getRuntimeProvider(chainId: String): RuntimeProvider { - return pool.getValue(chainId) + suspend fun awaitRuntimeProvider(chainId: String): RuntimeProvider { + return poolStateFlow.map { it.getOrDefault(chainId, null) }.first { it != null }.cast() } fun getRuntimeProviderOrNull(chainId: String): RuntimeProvider? { - return pool.getOrDefault(chainId, null) + return poolStateFlow.value.getOrDefault(chainId, null) } fun setupRuntimeProvider(chain: Chain): RuntimeProvider { - return pool.getOrPut(chain.id) { - RuntimeProvider( - runtimeFactory, - runtimeSyncService, - runtimeFilesCache, - chainDao, - networkStateMixin, - chain - ) + if (poolStateFlow.value.containsKey(chain.id)) { + return poolStateFlow.value.getValue(chain.id) + } else { + poolStateFlow.update { prev -> + prev.also { + it[chain.id] = RuntimeProvider( + runtimeFactory, + runtimeSyncService, + runtimeFilesCache, + chainDao, + networkStateMixin, + chain + ) + } + } + return poolStateFlow.value.getValue(chain.id) } } fun removeRuntimeProvider(chainId: String) { - pool.remove(chainId)?.apply { finish() } + poolStateFlow.update { prev -> + prev.also { + it.remove(chainId)?.apply { finish() } + } + } + } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/BaseStorageSource.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/BaseStorageSource.kt index 372aa75608..791975a4cc 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/BaseStorageSource.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/BaseStorageSource.kt @@ -19,7 +19,7 @@ abstract class BaseStorageSource( protected abstract suspend fun query(key: String, chainId: String, at: BlockHash?): String? - protected abstract suspend fun queryKeys(keys: List, chainId: String, at: BlockHash?): Map + abstract suspend fun queryKeys(keys: List, chainId: String, at: BlockHash?): Map protected abstract suspend fun observe(key: String, chainId: String): Flow From 9f1a32e317c9935a895ceaab676d4d2dbc1b3ba2 Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 18 Apr 2024 17:21:16 +0700 Subject: [PATCH 039/100] wip --- .../co/soramitsu/coredb/dao/MetaAccountDao.kt | 6 +- .../co/soramitsu/coredb/model/AssetLocal.kt | 14 +- .../account/impl/domain/BalancesUtils.kt | 108 +++-- .../account/impl/domain/WalletSyncService.kt | 400 ++++++++++-------- 4 files changed, 309 insertions(+), 219 deletions(-) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt index 7d895fa809..057638e82f 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt @@ -57,9 +57,6 @@ interface MetaAccountDao { @Query("UPDATE meta_accounts SET isSelected = (id = :metaId)") suspend fun selectMetaAccount(metaId: Long) - @Query("UPDATE meta_accounts SET initialized = 1 WHERE id in (:ids)") - suspend fun markAccountsInitialized(ids: List) - @Update(entity = MetaAccountLocal::class) suspend fun updatePositions(updates: List) @@ -122,6 +119,9 @@ interface MetaAccountDao { @Query("SELECT * FROM favorite_chains WHERE metaId = :metaId") fun observeFavoriteChains(metaId: Long): Flow> + @Query("UPDATE meta_accounts SET initialized = 1 WHERE id in (:ids)") + suspend fun markAccountsInitialized(ids: List) :Int + @Query("SELECT * FROM meta_accounts WHERE initialized = 0") @Transaction fun observeNotInitializedMetaAccounts(): Flow> diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt index 33fce1cc5b..ff65b42829 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt @@ -8,6 +8,12 @@ import jp.co.soramitsu.coredb.model.chain.ChainLocal import jp.co.soramitsu.shared_utils.runtime.AccountId import java.math.BigInteger +/*** This table is used for storing assets in database. + * freeInPlanks - has three states: + * null - loading is in progress + * -1 - error + * 0 or positive number - free amount + */ @Entity( tableName = "assets", primaryKeys = ["id", "chainId", "accountId", "metaId"], @@ -42,17 +48,17 @@ data class AssetLocal( companion object { fun createEmpty( accountId: AccountId, - assetId: String, + id: String, chainId: String, metaId: Long, - priceId: String?, + tokenPriceId: String?, enabled: Boolean? = null ) = AssetLocal( - id = assetId, + id = id, chainId = chainId, accountId = accountId, metaId = metaId, - tokenPriceId = priceId, + tokenPriceId = tokenPriceId, enabled = enabled ) } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt index 1cd840c8a0..49f49866bc 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt @@ -23,6 +23,7 @@ import jp.co.soramitsu.wallet.api.data.cache.bindAssetsAccountData import jp.co.soramitsu.wallet.api.data.cache.bindEquilibriumAccountData import jp.co.soramitsu.wallet.api.data.cache.bindOrmlTokensAccountDataOrDefault import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout import org.web3j.abi.FunctionEncoder import org.web3j.abi.datatypes.Address import org.web3j.abi.datatypes.Function @@ -44,33 +45,53 @@ fun buildStorageKeys( fun buildStorageKeys( chain: Chain, - runtime: RuntimeSnapshot, + runtime: RuntimeSnapshot?, metaAccountId: Long, accountId: ByteArray ): List { if (chain.utilityAsset != null && chain.utilityAsset?.typeExtra == ChainAssetType.Equilibrium) { - val equilibriumStorageKeys = listOf( - constructBalanceKey( + return listOf(buildEquilibriumStorageKeys(chain, runtime, metaAccountId, accountId)) + } + + return buildSubstrateStorageKeys(chain, runtime, metaAccountId, accountId) +} + +fun buildSubstrateStorageKeys(chain: Chain, + runtime: RuntimeSnapshot?, + metaAccountId: Long, + accountId: ByteArray): List{ + return chain.assets.map { asset -> + StorageKeyWithMetadata( + asset, metaAccountId, accountId, + runtime?.let { constructBalanceKey(it, asset, accountId) } + ) + } +} + +fun buildEquilibriumStorageKeys( + chain: Chain, + runtime: RuntimeSnapshot?, + metaAccountId: Long, + accountId: ByteArray +): StorageKeyWithMetadata { + val metadata = StorageKeyWithMetadata( + requireNotNull(chain.utilityAsset), + metaAccountId, + accountId, + null + ) + + return if (runtime == null) { + metadata + } else { + metadata.copy( + key = constructBalanceKey( runtime, requireNotNull(chain.utilityAsset), accountId - ).let { - StorageKeyWithMetadata( - requireNotNull(chain.utilityAsset), - metaAccountId, - accountId, - it - ) - }) - return equilibriumStorageKeys - } - - val storageKeys = chain.assets.map { asset -> - StorageKeyWithMetadata(asset, metaAccountId, accountId, - constructBalanceKey(runtime, asset, accountId) + ) ) } - return storageKeys } data class StorageKeyWithMetadata( @@ -192,32 +213,33 @@ fun handleBalanceResponse( } suspend fun Ethereum.fetchEthBalance(asset: Asset, address: String): BigInteger { - return if (asset.isUtility) { - withContext(kotlinx.coroutines.Dispatchers.IO) { - ethGetBalance( - address, - DefaultBlockParameterName.LATEST - ).send().balance - } - } else { - val erc20GetBalanceFunction = Function( - "balanceOf", - listOf(Address(address)), - emptyList() - ) + return withTimeout(3000L) { + if (asset.isUtility) { + withContext(kotlinx.coroutines.Dispatchers.IO) { + ethGetBalance( + address, + DefaultBlockParameterName.LATEST + ).send().balance + } + } else { + val erc20GetBalanceFunction = Function( + "balanceOf", + listOf(Address(address)), + emptyList() + ) - val erc20BalanceWei = withContext(kotlinx.coroutines.Dispatchers.IO) { - ethCall( - Transaction.createEthCallTransaction( - null, - asset.id, - FunctionEncoder.encode(erc20GetBalanceFunction) - ), - DefaultBlockParameterName.LATEST - ).send().value - } + val erc20BalanceWei = withContext(kotlinx.coroutines.Dispatchers.IO) { + ethCall( + Transaction.createEthCallTransaction( + null, + asset.id, + FunctionEncoder.encode(erc20GetBalanceFunction) + ), + DefaultBlockParameterName.LATEST + ).send().value + } - Numeric.decodeQuantity(erc20BalanceWei) + Numeric.decodeQuantity(erc20BalanceWei) + } } } - diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index 435d289f4e..ba0e8e7fbd 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -2,6 +2,7 @@ package jp.co.soramitsu.account.impl.domain import android.util.Log import java.math.BigInteger +import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.account.impl.data.mappers.mapMetaAccountLocalToMetaAccount import jp.co.soramitsu.common.data.network.runtime.binding.AssetBalance @@ -15,24 +16,27 @@ import jp.co.soramitsu.coredb.dao.MetaAccountDao import jp.co.soramitsu.coredb.model.AssetLocal import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository +import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.connection.EvmConnectionStatus import jp.co.soramitsu.runtime.storage.source.RemoteStorageSource import jp.co.soramitsu.shared_utils.extensions.toHexString +import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.wallet.api.data.cache.bindEquilibriumAccountData import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async -import kotlinx.coroutines.cancel -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.job import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withTimeoutOrNull class WalletSyncService( private val metaAccountDao: MetaAccountDao, @@ -40,199 +44,257 @@ class WalletSyncService( private val chainRegistry: ChainRegistry, private val remoteStorageSource: RemoteStorageSource, private val assetDao: AssetDao, - dispatcher: CoroutineDispatcher = Dispatchers.Default + dispatcher: CoroutineDispatcher = Dispatchers.Default, ) { - private val scope = CoroutineScope(dispatcher + SupervisorJob() + CoroutineExceptionHandler { coroutineContext, throwable -> Log.d("&&&", "WalletSyncService scope error: $throwable") }) + companion object { + private const val CHAIN_SYNC_TIMEOUT_MILLIS: Long = 15_000L + } + + private val scope = + CoroutineScope(dispatcher + SupervisorJob() + CoroutineExceptionHandler { _, throwable -> + Log.d( + "&&&", + "WalletSyncService scope error: $throwable" + ) + }) + + private var syncJob: Job? = null init { - metaAccountDao.observeNotInitializedMetaAccounts().onEach { Log.d("&&&", "start sync ${it.size} accounts: ${it.map { it.metaAccount.name }}") }.filter { it.isNotEmpty() } + metaAccountDao.observeNotInitializedMetaAccounts().filter { it.isNotEmpty() } .onEach { localMetaAccounts -> - chainRegistry.configsSyncDeferred.join() + syncJob?.cancel() + syncJob = scope.launch { + chainRegistry.configsSyncDeferred.join() - val chains = chainsRepository.getChains() - val ethereumChains = chains.filter { it.isEthereumChain } - val substrateChains = chains.filter { !it.isEthereumChain } + val chains = chainsRepository.getChains() + val ethereumChains = chains.filter { it.isEthereumChain } + val substrateChains = chains.filter { !it.isEthereumChain } - val metaAccounts = - localMetaAccounts.map { accountInfo -> - mapMetaAccountLocalToMetaAccount( - chains.associateBy { it.id }, - accountInfo - ) - } - coroutineScope { + val metaAccounts = + localMetaAccounts.map { accountInfo -> + mapMetaAccountLocalToMetaAccount( + chains.associateBy { it.id }, + accountInfo + ) + } - launch { - ethereumChains.forEach { chain -> - launch { - val assetsDeferred = async { - if (chainRegistry.checkChainSyncedUp(chain).not()) { - chainRegistry.setupChain(chain) - } - val connection = chainRegistry.awaitEthereumConnection(chain.id) - // await connecting to the node - connection.statusFlow.first { it is EvmConnectionStatus.Connected } -// this@launch.cancel() - metaAccounts.mapNotNull { metaAccount -> - val accountId = - metaAccount.accountId(chain) ?: return@mapNotNull null - chain.assets.map { chainAsset -> - val balance = kotlin.runCatching { - connection.web3j!!.fetchEthBalance( - chainAsset, - accountId.toHexString(true) - ) - }.getOrNull() - - AssetLocal( - id = chainAsset.id, - chainId = chain.id, - accountId = accountId, - metaId = metaAccount.id, - tokenPriceId = chainAsset.priceId, - freeInPlanks = balance, - reservedInPlanks = BigInteger.ZERO, - miscFrozenInPlanks = BigInteger.ZERO, - feeFrozenInPlanks = BigInteger.ZERO, - bondedInPlanks = BigInteger.ZERO, - redeemableInPlanks = BigInteger.ZERO, - unbondingInPlanks = BigInteger.ZERO, - enabled = balance.isNotZero() || chain.rank != null && chainAsset.isUtility - ) + supervisorScope { + + launch { + ethereumChains.forEach { chain -> + launch { + val assetsDeferred = async { + if (chainRegistry.checkChainSyncedUp(chain).not()) { + chainRegistry.setupChain(chain) } - }.flatten() + val connection = + withTimeoutOrNull(CHAIN_SYNC_TIMEOUT_MILLIS) { + val connection = + chainRegistry.awaitEthereumConnection(chain.id) + // await connecting to the node + connection.statusFlow.first { it is EvmConnectionStatus.Connected } + connection + } + + metaAccounts.mapNotNull { metaAccount -> + val accountId = + metaAccount.accountId(chain) + ?: return@mapNotNull null + chain.assets.map { chainAsset -> + val balance = kotlin.runCatching { + connection?.web3j?.fetchEthBalance( + chainAsset, + accountId.toHexString(true) + ) + }.getOrNull() + AssetLocal( + id = chainAsset.id, + chainId = chain.id, + accountId = accountId, + metaId = metaAccount.id, + tokenPriceId = chainAsset.priceId, + freeInPlanks = balance, + reservedInPlanks = BigInteger.ZERO, + miscFrozenInPlanks = BigInteger.ZERO, + feeFrozenInPlanks = BigInteger.ZERO, + bondedInPlanks = BigInteger.ZERO, + redeemableInPlanks = BigInteger.ZERO, + unbondingInPlanks = BigInteger.ZERO, + enabled = balance.isNotZero() || chain.rank != null && chainAsset.isUtility + ) + } + }.flatten() + } + val localAssets = assetsDeferred.await() + assetDao.insertAssets(localAssets) } - val localAssets = assetsDeferred.await() - assetDao.insertAssets(localAssets) - Log.d("&&&&", "chain ${chain.name} sync completed") } } - }.invokeOnCompletion { Log.d("&&&&", "EVM CHAINS sync completed") } - launch { - substrateChains.onEach { chain -> - launch { - val assetsDeferred = async { - if (chainRegistry.checkChainSyncedUp(chain).not()) { - chainRegistry.setupChain(chain) - } - val emptyAssets: MutableList = mutableListOf() - // awaiting runtime snapshot - val runtime = chainRegistry.awaitRuntimeProvider(chain.id).get() + launch { + substrateChains.onEach { chain -> + launch { + val assetsDeferred = async { + val emptyAssets: MutableList = mutableListOf() + val runtime = withTimeoutOrNull(CHAIN_SYNC_TIMEOUT_MILLIS) { + if (chainRegistry.checkChainSyncedUp(chain).not()) { + chainRegistry.setupChain(chain) + } - val allAccountsStorageKeys = - metaAccounts.mapNotNull { metaAccount -> - val accountId = - metaAccount.accountId(chain) - ?: return@mapNotNull null - buildStorageKeys( - chain, - runtime, - metaAccount.id, - accountId - ) - }.flatten().associateBy { it.key } - - val keysToQuery = - allAccountsStorageKeys.mapNotNull { (storageKey, metadata) -> - // if storage key build is failed - we put the empty assets - if (storageKey == null) { - // filling all the equilibrium assets - if (chain.utilityAsset != null && chain.utilityAsset?.typeExtra == ChainAssetType.Equilibrium) { - chain.assets.map { + // awaiting runtime snapshot + chainRegistry.awaitRuntimeProvider(chain.id).get() + } + + val isEquilibriumTypeChain = + chain.utilityAsset != null && chain.utilityAsset!!.typeExtra == ChainAssetType.Equilibrium + + if (isEquilibriumTypeChain) { + buildEquilibriumAssets(metaAccounts, chain, runtime) + } else { + val allAccountsStorageKeys = + metaAccounts.mapNotNull { metaAccount -> + val accountId = + metaAccount.accountId(chain) + ?: return@mapNotNull null + buildSubstrateStorageKeys( + chain, + runtime, + metaAccount.id, + accountId + ) + }.flatten().associateBy { it.key } + + val keysToQuery = + allAccountsStorageKeys.mapNotNull { (storageKey, metadata) -> + // if storage key build is failed - we put the empty assets + if (storageKey == null) { emptyAssets.add( - AssetLocal.createEmpty( + AssetLocal( accountId = metadata.accountId, - assetId = it.id, - chainId = it.chainId, + id = metadata.asset.id, + chainId = metadata.asset.chainId, metaId = metadata.metaAccountId, - priceId = it.priceId, - enabled = false + tokenPriceId = metadata.asset.priceId, + enabled = false, + freeInPlanks = BigInteger.valueOf(-1) ) ) } - } else { - emptyAssets.add( - AssetLocal.createEmpty( - accountId = metadata.accountId, - assetId = metadata.asset.id, - chainId = metadata.asset.chainId, - metaId = metadata.metaAccountId, - priceId = metadata.asset.priceId, - enabled = false - ) + storageKey + }.toList() + + val storageKeyToResult = remoteStorageSource.queryKeys( + keysToQuery, + chain.id, + null + ) + + allAccountsStorageKeys.map { (storageKey, metadata) -> + val hexRaw = + storageKeyToResult.getOrDefault( + storageKey, + null ) - } - } - storageKey - }.toList() - - val storageKeyToResult = remoteStorageSource.queryKeys( - keysToQuery, - chain.id, - null - ) - - val equilibriumBalanceMap = - if (chain.utilityAsset != null && chain.utilityAsset!!.typeExtra == ChainAssetType.Equilibrium) { - val result = - storageKeyToResult.values.firstNotNullOfOrNull { it } - val balanceData = - bindEquilibriumAccountData(result, runtime) - balanceData?.data?.balances.orEmpty() - } else { - null - } - allAccountsStorageKeys.map { (storageKey, metadata) -> - val hexRaw = - storageKeyToResult.getOrDefault(storageKey, null) - - val assetBalance = if (equilibriumBalanceMap != null) { - val balance = - metadata.asset.currencyId?.toBigInteger()?.let { - equilibriumBalanceMap.getOrDefault(it, null) - .orZero() - }.orZero() - AssetBalance(freeInPlanks = balance) - } else { - handleBalanceResponse( - runtime, - metadata.asset, - hexRaw - ).getOrNull().toAssetBalance() - - } ?: AssetBalance() - - AssetLocal( - id = metadata.asset.id, - chainId = chain.id, - accountId = metadata.accountId, - metaId = metadata.metaAccountId, - tokenPriceId = metadata.asset.priceId, - freeInPlanks = assetBalance.freeInPlanks, - reservedInPlanks = assetBalance.reservedInPlanks, - miscFrozenInPlanks = assetBalance.miscFrozenInPlanks, - feeFrozenInPlanks = assetBalance.feeFrozenInPlanks, - bondedInPlanks = assetBalance.bondedInPlanks, - redeemableInPlanks = assetBalance.redeemableInPlanks, - unbondingInPlanks = assetBalance.unbondingInPlanks, - enabled = assetBalance.freeInPlanks.isNotZero() || chain.rank != null && metadata.asset.isUtility - ) - } + emptyAssets + val assetBalance = + runtime?.let { + handleBalanceResponse( + it, + metadata.asset, + hexRaw + ).getOrNull().toAssetBalance() + } ?: AssetBalance() + + AssetLocal( + id = metadata.asset.id, + chainId = chain.id, + accountId = metadata.accountId, + metaId = metadata.metaAccountId, + tokenPriceId = metadata.asset.priceId, + freeInPlanks = assetBalance.freeInPlanks, + reservedInPlanks = assetBalance.reservedInPlanks, + miscFrozenInPlanks = assetBalance.miscFrozenInPlanks, + feeFrozenInPlanks = assetBalance.feeFrozenInPlanks, + bondedInPlanks = assetBalance.bondedInPlanks, + redeemableInPlanks = assetBalance.redeemableInPlanks, + unbondingInPlanks = assetBalance.unbondingInPlanks, + enabled = assetBalance.freeInPlanks.isNotZero() || chain.rank != null && metadata.asset.isUtility + ) + } + emptyAssets + } + } + val localAssets = assetsDeferred.await() + assetDao.insertAssets(localAssets) } - val localAssets = assetsDeferred.await() - assetDao.insertAssets(localAssets) - Log.d("&&&&", "chain ${chain.name} sync completed") } } - }.invokeOnCompletion { Log.d("&&&&", "SUBSTRATE CHAINS sync completed") } - this - }.coroutineContext.job.join() - Log.d("&&&", "marking accounts initialized ${metaAccounts.map { it.name }}") - metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) + this + }.coroutineContext.job.join() + metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) + } }.launchIn(scope) } + + private suspend fun buildEquilibriumAssets( + metaAccounts: List, + chain: Chain, + runtime: RuntimeSnapshot? + ): List { + val emptyAssets: MutableList = mutableListOf() + + val allAccountsStorageKeys = metaAccounts.mapNotNull { metaAccount -> + val accountId = metaAccount.accountId(chain) ?: return@mapNotNull null + buildEquilibriumStorageKeys(chain, runtime, metaAccount.id, accountId) + }.associateBy { it.key } + + val keysToQuery = + allAccountsStorageKeys.mapNotNull { (storageKey, metadata) -> + // if storage key build is failed - we put the empty assets + if (storageKey == null) { + // filling all the equilibrium assets + val empty = chain.assets.map { + AssetLocal( + accountId = metadata.accountId, + id = it.id, + chainId = it.chainId, + metaId = metadata.metaAccountId, + tokenPriceId = it.priceId, + enabled = false, + freeInPlanks = BigInteger.valueOf(-1) + ) + } + emptyAssets.addAll(empty) + } + storageKey + }.toList() + + val storageKeyToResult = remoteStorageSource.queryKeys(keysToQuery, chain.id, null) + + return storageKeyToResult.mapNotNull { (storageKey, hexRaw) -> + val metadata = allAccountsStorageKeys[storageKey] ?: return@mapNotNull null + + val balanceData = runtime?.let { bindEquilibriumAccountData(hexRaw, it) } + val equilibriumAssetsBalanceMap = balanceData?.data?.balances.orEmpty() + + chain.assets.map { asset -> + val balance = + asset.currencyId?.toBigInteger()?.let { + equilibriumAssetsBalanceMap.getOrDefault(it, null) + .orZero() + }.orZero() + AssetLocal( + accountId = metadata.accountId, + id = asset.id, + chainId = asset.chainId, + metaId = metadata.metaAccountId, + tokenPriceId = asset.priceId, + enabled = false, + freeInPlanks = balance + ) + } + }.flatten() + emptyAssets + } } \ No newline at end of file From 0432d74878428767b65af8d57212fe2b4ae62c38 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 22 Apr 2024 11:53:11 +0700 Subject: [PATCH 040/100] wip --- .../account/impl/domain/BalancesUtils.kt | 3 ++ .../account/impl/domain/WalletSyncService.kt | 27 ++++++---- .../balance/list/BalanceListViewModel.kt | 7 ++- .../presentation/balance/list/WalletScreen.kt | 9 ++++ .../presentation/balance/list/WalletState.kt | 7 +++ .../impl/presentation/common/NetworkIssue.kt | 53 +++++++++++++++++++ .../multiNetwork/connection/ConnectionPool.kt | 2 +- 7 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt index 49f49866bc..b258d0b213 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt @@ -61,6 +61,9 @@ fun buildSubstrateStorageKeys(chain: Chain, metaAccountId: Long, accountId: ByteArray): List{ return chain.assets.map { asset -> + if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ + chain.hashCode() + } StorageKeyWithMetadata( asset, metaAccountId, accountId, runtime?.let { constructBalanceKey(it, asset, accountId) } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index ba0e8e7fbd..eb171c4044 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -81,7 +81,6 @@ class WalletSyncService( } supervisorScope { - launch { ethereumChains.forEach { chain -> launch { @@ -154,6 +153,9 @@ class WalletSyncService( if (isEquilibriumTypeChain) { buildEquilibriumAssets(metaAccounts, chain, runtime) } else { + if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ + Log.d("&&&", "hydra dx chain assets: ${chain.assets}") + } val allAccountsStorageKeys = metaAccounts.mapNotNull { metaAccount -> val accountId = @@ -165,12 +167,14 @@ class WalletSyncService( metaAccount.id, accountId ) - }.flatten().associateBy { it.key } - + }.flatten() + if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ + Log.d("&&&", "hydra dx storage keys: ${allAccountsStorageKeys}") + } val keysToQuery = - allAccountsStorageKeys.mapNotNull { (storageKey, metadata) -> + allAccountsStorageKeys.mapNotNull { metadata -> // if storage key build is failed - we put the empty assets - if (storageKey == null) { + if (metadata.key == null) { emptyAssets.add( AssetLocal( accountId = metadata.accountId, @@ -183,19 +187,24 @@ class WalletSyncService( ) ) } - storageKey + metadata.key }.toList() + if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ + Log.d("&&&", "hydra dx keysToQuery: ${keysToQuery}") + } val storageKeyToResult = remoteStorageSource.queryKeys( keysToQuery, chain.id, null ) - - allAccountsStorageKeys.map { (storageKey, metadata) -> + if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ + Log.d("&&&", "hydra dx storageKeyToResult: ${keysToQuery}") + } + allAccountsStorageKeys.map { metadata -> val hexRaw = storageKeyToResult.getOrDefault( - storageKey, + metadata.key, null ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 81af9d60ae..1c25ff7f4e 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -211,7 +211,7 @@ class BalanceListViewModel @Inject constructor( val shouldShowNetworkIssues = selectedChainId == null && (networkIssues.isNotEmpty() || assets.any { it.hasAccount.not() }) - showNetworkIssues.value = shouldShowNetworkIssues + showNetworkIssues.value = false val selectedAccountFavoriteChains = currentMetaAccountFlow.favoriteChains @@ -263,7 +263,7 @@ class BalanceListViewModel @Inject constructor( } assetStates - }//.onStart { emit(buildInitialAssetsList().toMutableList()) } + }.onStart { emit(buildInitialAssetsList().toMutableList()) } .inBackground().share() @OptIn(FlowPreview::class) @@ -368,6 +368,9 @@ class BalanceListViewModel @Inject constructor( ) { selectedChainId, selectorState, assetStates, filters, (pageViews, screenLayout) -> when (selectorState.currentSelection) { AssetType.Currencies -> { + if(selectedChainId != null && assetStates.isEmpty()){ + + } WalletAssetsState.Assets( assets = assetStates, isHideVisible = selectedChainId != null diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt index 2b87296f9c..39d11223f9 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt @@ -22,7 +22,9 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.SwipeableState import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -55,7 +57,9 @@ import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.AssetType import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.NFTScreen import jp.co.soramitsu.wallet.impl.presentation.common.AssetsList import jp.co.soramitsu.wallet.impl.presentation.common.AssetsListInterface +import jp.co.soramitsu.wallet.impl.presentation.common.NetworkIssue +@Stable interface WalletScreenInterface : AssetsListInterface { fun onAddressClick() fun onBalanceClicked() @@ -67,6 +71,7 @@ interface WalletScreenInterface : AssetsListInterface { fun assetTypeChanged(type: AssetType) fun onRefresh() fun onManageAssetClick() + fun onRetry() } @Composable @@ -135,6 +140,9 @@ fun WalletScreen( footer = footer ) } + is WalletAssetsState.NetworkIssue -> { + NetworkIssue(callback::onRetry) + } } } } @@ -285,6 +293,7 @@ private fun PreviewWalletScreen() { override fun onRefresh() {} override fun onManageAssetClick() {} + override fun onRetry() = Unit } val element = AssetListItemViewState( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt index ba450f83e5..1301cc4ec9 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt @@ -1,5 +1,7 @@ package jp.co.soramitsu.wallet.impl.presentation.balance.list +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable import jp.co.soramitsu.common.compose.component.AssetBalanceViewState import jp.co.soramitsu.common.compose.component.ChangeBalanceViewState import jp.co.soramitsu.common.compose.component.MultiToggleButtonState @@ -10,6 +12,7 @@ import jp.co.soramitsu.soracard.impl.presentation.SoraCardItemViewState import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.AssetType import jp.co.soramitsu.wallet.impl.presentation.common.AssetListState +@Stable data class WalletState( val assetsState: WalletAssetsState, val multiToggleButtonState: MultiToggleButtonState, @@ -34,12 +37,16 @@ data class WalletState( } } +@Stable sealed interface WalletAssetsState { data class Assets( override val assets: List, val isHideVisible: Boolean ): WalletAssetsState, AssetListState(assets) + @Immutable + data object NetworkIssue: WalletAssetsState + @JvmInline value class NftAssets( val collectionScreenModel: NFTCollectionsScreenModel diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt new file mode 100644 index 0000000000..19b2f024a0 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt @@ -0,0 +1,53 @@ +package jp.co.soramitsu.wallet.impl.presentation.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import jp.co.soramitsu.common.compose.component.B0 +import jp.co.soramitsu.common.compose.component.GradientIcon +import jp.co.soramitsu.common.compose.component.GrayButton +import jp.co.soramitsu.common.compose.component.H3 +import jp.co.soramitsu.common.compose.theme.alertYellow +import jp.co.soramitsu.common.compose.theme.white50 +import jp.co.soramitsu.feature_wallet_impl.R + +// compose component for network issues, contains warning icon, error message and try again button +@Composable +fun NetworkIssue(onRetry: () -> Unit) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + //Connection Error: Unable to connect to the network. Please try again. + GradientIcon( + iconRes = R.drawable.ic_alert_24, + color = alertYellow, + modifier = Modifier.align(Alignment.CenterHorizontally), + contentPadding = PaddingValues(bottom = 4.dp) + ) + + H3(text = "Connection Error") + B0( + text = "Unable to connect to the network. Please try again.", + color = white50 + ) + GrayButton(modifier = Modifier.width(200.dp).height(48.dp), text = "Try again", onClick = onRetry) + } +} + +@Preview +@Composable +fun NetworkIssuePreview() { + NetworkIssue { + + } +} \ No newline at end of file diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt index f7f00b7d17..d2c740ae52 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt @@ -152,7 +152,7 @@ class ConnectionPool @Inject constructor( is SocketStateMachine.State.Paused -> ChainState.ConnectionStatus.Paused(connectionState.url) is SocketStateMachine.State.WaitingForReconnect -> ChainState.ConnectionStatus.Connecting(connectionState.url) } - ChainsStateTracker.updateState(chain){ it.copy(connectionStatus = newState) } + ChainsStateTracker.updateState(chain) { it.copy(connectionStatus = newState) } }.launchIn(this) } From 2d751b4e4799fbca2e8fad017f15582525b4fbd0 Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 30 Apr 2024 12:23:47 +0700 Subject: [PATCH 041/100] wip network issues --- .../app/root/presentation/RootViewModel.kt | 6 +- .../common/di/modules/CommonModule.kt | 5 +- .../common/domain/NetworkStateService.kt | 41 +++++ .../common/domain/model/NetworkState.kt | 10 ++ .../common/mixin/api/NetworkStateMixin.kt | 28 ---- .../common/mixin/impl/NetworkStateProvider.kt | 61 -------- .../updaters/BalancesUpdateSystem.kt | 8 +- .../data/repository/WalletRepositoryImpl.kt | 72 --------- .../wallet/impl/di/WalletFeatureModule.kt | 6 +- .../impl/domain/WalletInteractorImpl.kt | 11 +- .../assetDetails/AssetDetailsViewModel.kt | 7 +- .../balance/list/BalanceListViewModel.kt | 19 ++- .../networkissues/NetworkIssuesViewModel.kt | 8 +- .../searchAssets/SearchAssetsViewModel.kt | 6 +- .../runtime/di/ChainRegistryModule.kt | 19 ++- .../runtime/multiNetwork/ChainRegistry.kt | 15 +- .../multiNetwork/connection/ConnectionPool.kt | 143 ++++++++---------- .../connection/EthereumConnectionPool.kt | 7 +- .../multiNetwork/runtime/RuntimeProvider.kt | 8 +- .../runtime/RuntimeProviderPool.kt | 7 +- .../runtime/RuntimeSyncService.kt | 2 +- .../runtime/RuntimeProviderTest.kt | 6 +- 22 files changed, 194 insertions(+), 301 deletions(-) create mode 100644 common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt create mode 100644 common/src/main/java/jp/co/soramitsu/common/domain/model/NetworkState.kt delete mode 100644 common/src/main/java/jp/co/soramitsu/common/mixin/api/NetworkStateMixin.kt delete mode 100644 common/src/main/java/jp/co/soramitsu/common/mixin/impl/NetworkStateProvider.kt diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index d1d8342836..3add3e4ccf 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -12,7 +12,7 @@ import javax.inject.Inject import jp.co.soramitsu.app.R import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.mixin.api.NetworkStateUi import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event @@ -36,8 +36,8 @@ class RootViewModel @Inject constructor( private val rootRouter: RootRouter, private val externalConnectionRequirementFlow: MutableStateFlow, private val resourceManager: ResourceManager, - private val networkStateMixin: NetworkStateMixin -) : BaseViewModel(), NetworkStateUi by networkStateMixin { + private val networkStateService: networkStateService +) : BaseViewModel(), NetworkStateUi by networkStateService { companion object { private const val IDLE_MINUTES: Long = 20 } diff --git a/common/src/main/java/jp/co/soramitsu/common/di/modules/CommonModule.kt b/common/src/main/java/jp/co/soramitsu/common/di/modules/CommonModule.kt index 4bfb3daa92..570256b38a 100644 --- a/common/src/main/java/jp/co/soramitsu/common/di/modules/CommonModule.kt +++ b/common/src/main/java/jp/co/soramitsu/common/di/modules/CommonModule.kt @@ -30,9 +30,8 @@ import jp.co.soramitsu.common.data.storage.encrypt.EncryptedPreferences import jp.co.soramitsu.common.data.storage.encrypt.EncryptedPreferencesImpl import jp.co.soramitsu.common.data.storage.encrypt.EncryptionUtil import jp.co.soramitsu.common.interfaces.FileProvider -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin import jp.co.soramitsu.common.mixin.api.UpdatesMixin -import jp.co.soramitsu.common.mixin.impl.NetworkStateProvider +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.mixin.impl.UpdatesProvider import jp.co.soramitsu.common.resources.ClipboardManager import jp.co.soramitsu.common.resources.ContextManager @@ -217,5 +216,5 @@ class CommonModule { @Provides @Singleton - fun provideNetworkStateMixin(): NetworkStateMixin = NetworkStateProvider() + fun provideNetworkStateMixin() = NetworkStateService() } diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt b/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt new file mode 100644 index 0000000000..fc20ecb535 --- /dev/null +++ b/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt @@ -0,0 +1,41 @@ +package jp.co.soramitsu.common.domain + +import jp.co.soramitsu.common.compose.component.NetworkIssueItemState +import jp.co.soramitsu.core.models.ChainId +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine + +class NetworkStateService { + + private val connectionPoolProblems = MutableStateFlow>(emptySet()) + private val chainsSyncProblems = MutableStateFlow>(emptySet()) + + private val _showConnectingBarFlow = MutableStateFlow(false) + val showConnectingBarFlow = _showConnectingBarFlow + + val networkIssuesFlow = combine( + connectionPoolProblems, + chainsSyncProblems + ) { connectionPoolProblems, chainsSyncProblems -> + connectionPoolProblems + chainsSyncProblems + } + + fun updateShowConnecting(isShow: Boolean) { + _showConnectingBarFlow.value = isShow + } + + fun updateNetworkIssues(list: List) { + connectionPoolProblems.value = list.toSet() + } + + fun notifyChainSyncProblem(issue: NetworkIssueItemState) { + val previousSet = chainsSyncProblems.value + val newSet = previousSet + issue + chainsSyncProblems.value = newSet + } + + fun notifyChainSyncSuccess(id: ChainId) { + val newSet = chainsSyncProblems.value.toMutableSet().apply { removeIf { it.chainId == id } } + chainsSyncProblems.value = newSet + } +} diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/model/NetworkState.kt b/common/src/main/java/jp/co/soramitsu/common/domain/model/NetworkState.kt new file mode 100644 index 0000000000..a154043b3a --- /dev/null +++ b/common/src/main/java/jp/co/soramitsu/common/domain/model/NetworkState.kt @@ -0,0 +1,10 @@ +package jp.co.soramitsu.common.domain.model + +data class NetworkIssue ( + val type: NetworkIssueType, + val chainId: String +) + +enum class NetworkIssueType { + Node, Network, Account +} \ No newline at end of file diff --git a/common/src/main/java/jp/co/soramitsu/common/mixin/api/NetworkStateMixin.kt b/common/src/main/java/jp/co/soramitsu/common/mixin/api/NetworkStateMixin.kt deleted file mode 100644 index 146594ff6b..0000000000 --- a/common/src/main/java/jp/co/soramitsu/common/mixin/api/NetworkStateMixin.kt +++ /dev/null @@ -1,28 +0,0 @@ -package jp.co.soramitsu.common.mixin.api - -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState -import jp.co.soramitsu.core.models.ChainId -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.StateFlow - -interface NetworkStateMixin : NetworkStateUi - -interface NetworkStateUi { - val showConnectingBarFlow: StateFlow - - val networkIssuesFlow: Flow> - - val chainConnectionsFlow: StateFlow> - - fun updateShowConnecting(isShow: Boolean) - - fun updateNetworkIssues(list: List) - - fun updateChainConnection(map: Map) - - fun notifyAssetsProblem(items: Set) - - fun isAssetHasProblems(assetId: String): Boolean - fun notifyChainSyncProblem(issue: NetworkIssueItemState) - fun notifyChainSyncSuccess(id: ChainId) -} diff --git a/common/src/main/java/jp/co/soramitsu/common/mixin/impl/NetworkStateProvider.kt b/common/src/main/java/jp/co/soramitsu/common/mixin/impl/NetworkStateProvider.kt deleted file mode 100644 index 57900e8607..0000000000 --- a/common/src/main/java/jp/co/soramitsu/common/mixin/impl/NetworkStateProvider.kt +++ /dev/null @@ -1,61 +0,0 @@ -package jp.co.soramitsu.common.mixin.impl - -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin -import jp.co.soramitsu.core.models.ChainId -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine - -class NetworkStateProvider : NetworkStateMixin { - - private val connectionPoolProblems = MutableStateFlow>(emptySet()) - private val chainsSyncProblems = MutableStateFlow>(emptySet()) - private val assetsUpdateProblems = MutableStateFlow>(emptySet()) - - private val _showConnectingBarFlow = MutableStateFlow(false) - override val showConnectingBarFlow = _showConnectingBarFlow - - override val networkIssuesFlow = combine( - connectionPoolProblems, - assetsUpdateProblems, - chainsSyncProblems - ) { connectionPoolProblems, assetsUpdateProblems, chainsSyncProblems -> - connectionPoolProblems + assetsUpdateProblems + chainsSyncProblems - } - - private val _chainConnectionsFlow = MutableStateFlow>(emptyMap()) - override val chainConnectionsFlow = _chainConnectionsFlow - - override fun updateShowConnecting(isShow: Boolean) { - _showConnectingBarFlow.value = isShow - } - - override fun updateNetworkIssues(list: List) { - connectionPoolProblems.value = list.toSet() - } - - override fun updateChainConnection(map: Map) { - _chainConnectionsFlow.value = map - } - - override fun notifyAssetsProblem(items: Set) { - assetsUpdateProblems.value = items - } - - override fun isAssetHasProblems(assetId: String): Boolean { - val hasConnectionPoolProblems = connectionPoolProblems.value.any { it.assetId == assetId } - val hasAssetUpdateProblems = assetsUpdateProblems.value.any { it.assetId == assetId } - return hasConnectionPoolProblems && hasAssetUpdateProblems - } - - override fun notifyChainSyncProblem(issue: NetworkIssueItemState) { - val previousSet = chainsSyncProblems.value - val newSet = previousSet + issue - chainsSyncProblems.value = newSet - } - - override fun notifyChainSyncSuccess(id: ChainId) { - val newSet = chainsSyncProblems.value.toMutableSet().apply { removeIf { it.chainId == id } } - chainsSyncProblems.value = newSet - } -} diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt index 6ea7da4969..bdf9101621 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt @@ -14,7 +14,7 @@ import jp.co.soramitsu.account.impl.domain.handleBalanceResponse import jp.co.soramitsu.common.data.network.rpc.BulkRetriever import jp.co.soramitsu.common.data.network.runtime.binding.ExtrinsicStatusEvent import jp.co.soramitsu.common.data.network.runtime.binding.SimpleBalanceData -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.utils.failure import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.common.utils.requireException @@ -75,7 +75,7 @@ class BalancesUpdateSystem( private val assetCache: AssetCache, private val substrateSource: SubstrateRemoteSource, private val operationDao: OperationDao, - private val networkStateMixin: NetworkStateMixin, + private val networkStateService: networkStateService, private val ethereumRemoteSource: EthereumRemoteSource ) : UpdateSystem { @@ -143,11 +143,11 @@ class BalancesUpdateSystem( ?.observeWithTimeout(RUNTIME_AWAITING_TIMEOUT) ?.flatMapLatest { runtimeResult -> if (runtimeResult.isFailure) { - networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) + networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) return@flatMapLatest flowOf(runtimeResult) } val runtime = runtimeResult.requireValue() - networkStateMixin.notifyChainSyncSuccess(chain.id) + networkStateService.notifyChainSyncSuccess(chain.id) val storageKeys = buildStorageKeys( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index 2179c619d4..e9d675df28 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -140,78 +140,6 @@ class WalletRepositoryImpl( } } - fun assetsFlow1(meta: MetaAccount): Flow> { - return combine( - chainsRepository.chainsByIdFlow(), - assetCache.observeAssets(meta.id) - ) { chainsById, assetsLocal -> - val chainAccounts = meta.chainAccounts.values.toList() - val updatedAssets = assetsLocal.mapNotNull { asset -> - mapAssetLocalToAsset(chainsById, asset)?.let { - val hasChainAccount = - asset.asset.chainId in chainAccounts.mapNotNull { it.chain?.id } - AssetWithStatus( - asset = it, - hasAccount = !it.accountId.contentEquals(emptyAccountIdValue), - hasChainAccount = hasChainAccount - ) - } - } - - val assetsByChain: List = chainsById.values - .flatMap { chain -> - chain.assets.map { - AssetWithStatus( - asset = createEmpty( - chainAsset = it, - metaId = meta.id, - accountId = meta.accountId(chain) ?: emptyAccountIdValue, - minSupportedVersion = chain.minSupportedVersion - ), - hasAccount = !chain.isEthereumBased || meta.ethereumPublicKey != null, - hasChainAccount = chain.id in chainAccounts.mapNotNull { it.chain?.id } - ) - } - } - - val assetsByUniqueAccounts = chainAccounts.mapNotNull { chainAccount -> - createEmpty(chainAccount)?.let { asset -> - AssetWithStatus( - asset = asset, - hasAccount = true, - hasChainAccount = false - ) - } - } - - val notUpdatedAssetsByUniqueAccounts = assetsByUniqueAccounts.filter { unique -> - !updatedAssets.any { - it.asset.token.configuration.chainToSymbol == unique.asset.token.configuration.chainToSymbol && - it.asset.accountId.contentEquals(unique.asset.accountId) - } - } - val notUpdatedAssets = assetsByChain.filter { - it.asset.token.configuration.chainToSymbol !in updatedAssets.map { it.asset.token.configuration.chainToSymbol } - } - - updatedAssets + notUpdatedAssetsByUniqueAccounts + notUpdatedAssets - } - } - - private fun buildNetworkIssues(items: List): Set { - return items.map { - val configuration = it.asset.token.configuration - NetworkIssueItemState( - iconUrl = configuration.iconUrl, - title = "${configuration.chainName} ${configuration.name}", - type = NetworkIssueType.Node, - chainId = configuration.chainId, - chainName = configuration.chainName, - assetId = configuration.id - ) - }.toSet() - } - override suspend fun getAssets(metaId: Long): List = withContext(Dispatchers.Default) { val chainsById = chainsRepository.getChainsById() val assetsLocal = assetCache.getAssets(metaId) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt index 244ff4f3fc..725fff0a89 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt @@ -25,7 +25,7 @@ import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.interfaces.FileProvider -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.QrBitmapDecoder @@ -379,7 +379,7 @@ class WalletFeatureModule { assetCache: AssetCache, substrateSource: SubstrateRemoteSource, operationDao: OperationDao, - networkStateMixin: NetworkStateMixin, + networkStateService: networkStateService, ethereumRemoteSource: EthereumRemoteSource ): UpdateSystem = BalancesUpdateSystem( chainRegistry, @@ -387,7 +387,7 @@ class WalletFeatureModule { assetCache, substrateSource, operationDao, - networkStateMixin, + networkStateService, ethereumRemoteSource ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index 51146acd87..66b6962e37 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -20,6 +20,7 @@ import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.interfaces.FileProvider import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.common.utils.Modules import jp.co.soramitsu.common.utils.mapList @@ -654,4 +655,12 @@ class WalletInteractorImpl( } ?: AssetSorting.FiatBalance } } -} + + override fun getChainInitializationStatus(chainId: ChainId): NetworkStateService { + + } + + override fun networkIssuesFlow(): Flow<> { + + } +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt index 8acd1aa07d..6e521925d6 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt @@ -18,8 +18,6 @@ import jp.co.soramitsu.common.compose.component.MainToolbarViewState import jp.co.soramitsu.common.compose.component.MultiToggleButtonState import jp.co.soramitsu.common.compose.component.NetworkIssueType import jp.co.soramitsu.common.compose.component.ToolbarHomeIconState -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin -import jp.co.soramitsu.common.mixin.api.NetworkStateUi import jp.co.soramitsu.common.presentation.LoadingState import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.applyFiatRate @@ -59,13 +57,12 @@ import kotlinx.coroutines.launch class AssetDetailsViewModel @Inject constructor( private val interactor: WalletInteractor, private val getAssetBalance: AssetBalanceUseCase, - private val networkStateMixin: NetworkStateMixin, private val walletRouter: WalletRouter, private val accountInteractor: AccountInteractor, private val resourceManager: ResourceManager, private val assetNotNeedAccount: AssetNotNeedAccountUseCase, savedStateHandle: SavedStateHandle, -) : BaseViewModel(), AssetDetailsCallback, NetworkStateUi by networkStateMixin { +) : BaseViewModel(), AssetDetailsCallback { companion object { private const val KEY_ALERT_RESULT = "notNeedAlertResult" @@ -190,6 +187,8 @@ class AssetDetailsViewModel @Inject constructor( }.launchIn(viewModelScope) } + private val networkIssuesFlow = interactor.networkNotReadyFlow() + private fun subscribeAssets() { val assetSortingFlow = interactor.observeAssetSorting() cachedPerChainBalanceWithAssetFlow diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 1c25ff7f4e..0e0d746f6d 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -40,7 +40,7 @@ import jp.co.soramitsu.common.data.network.coingecko.FiatCurrency import jp.co.soramitsu.common.domain.FiatCurrencies import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies import jp.co.soramitsu.common.domain.SelectedFiat -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.mixin.api.NetworkStateUi import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi @@ -128,7 +128,7 @@ class BalanceListViewModel @Inject constructor( private val selectedFiat: SelectedFiat, private val accountInteractor: AccountInteractor, private val updatesMixin: UpdatesMixin, - private val networkStateMixin: NetworkStateMixin, + private val networkStateService: networkStateService, private val resourceManager: ResourceManager, private val clipboardManager: ClipboardManager, private val currentAccountAddress: CurrentAccountAddressUseCase, @@ -136,7 +136,7 @@ class BalanceListViewModel @Inject constructor( private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, private val nftInteractor: NFTInteractor, private val walletConnectInteractor: WalletConnectInteractor -) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateMixin, +) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateService, WalletScreenInterface { private var awaitAssetsJob: Job? = null @@ -173,6 +173,7 @@ class BalanceListViewModel @Inject constructor( }.inBackground() private val selectedChainId = MutableStateFlow(null) + private val selectedChainItemFlow = combine(selectedChainId, chainsFlow) { selectedChainId, chains -> selectedChainId?.let { @@ -180,6 +181,10 @@ class BalanceListViewModel @Inject constructor( } } + private val networkIssueStateFlow = selectedChainId.map { + it?.let { } + } + private val currentMetaAccountFlow = interactor.selectedLightMetaAccountFlow() private val assetTypeSelectorState = MutableStateFlow( @@ -368,7 +373,7 @@ class BalanceListViewModel @Inject constructor( ) { selectedChainId, selectorState, assetStates, filters, (pageViews, screenLayout) -> when (selectorState.currentSelection) { AssetType.Currencies -> { - if(selectedChainId != null && assetStates.isEmpty()){ + if(selectedChainId != null && assetStates.isEmpty()) { } WalletAssetsState.Assets( @@ -407,7 +412,7 @@ class BalanceListViewModel @Inject constructor( } private fun observeNetworkState() { - networkStateMixin.showConnectingBarFlow + networkStateService.showConnectingBarFlow .onEach { hasConnectionProblems -> if (!hasConnectionProblems) { refresh() @@ -592,6 +597,10 @@ class BalanceListViewModel @Inject constructor( router.openManageAssets() } + override fun onRetry() { + + } + private fun refresh() { sync() } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt index 2716e848b8..ad8e3fb280 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt @@ -10,7 +10,7 @@ import jp.co.soramitsu.common.AlertViewState import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.NetworkIssueType -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.mixin.api.NetworkStateUi import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi @@ -33,10 +33,10 @@ class NetworkIssuesViewModel @Inject constructor( private val walletInteractor: WalletInteractor, private val accountInteractor: AccountInteractor, private val updatesMixin: UpdatesMixin, - private val networkStateMixin: NetworkStateMixin, + private val networkStateService: networkStateService, private val resourceManager: ResourceManager, private val assetNotNeedAccount: AssetNotNeedAccountUseCase -) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateMixin { +) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateService { companion object { private const val KEY_ALERT_RESULT = "result" @@ -45,7 +45,7 @@ class NetworkIssuesViewModel @Inject constructor( private var lastSelectedNetworkIssueState: NetworkIssueItemState? = null val state = combine( - networkStateMixin.networkIssuesFlow.stateIn(viewModelScope, SharingStarted.Eagerly, emptySet()), + networkStateService.networkIssuesFlow.stateIn(viewModelScope, SharingStarted.Eagerly, emptySet()), walletInteractor.assetsFlow().map { it.filter { !it.hasAccount && !it.asset.markedNotNeed }.map { NetworkIssueItemState( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt index 692ea45ed2..4b4e4702f1 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt @@ -13,7 +13,7 @@ import jp.co.soramitsu.common.compose.component.ActionItemType import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.SwipeState import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.mixin.api.NetworkStateUi import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.orZero @@ -40,8 +40,8 @@ class SearchAssetsViewModel @Inject constructor( private val interactor: WalletInteractor, private val chainInteractor: ChainInteractor, private val router: WalletRouter, - private val networkStateMixin: NetworkStateMixin -) : BaseViewModel(), NetworkStateUi by networkStateMixin, SearchAssetsScreenInterface { + private val networkStateService: networkStateService +) : BaseViewModel(), NetworkStateUi by networkStateService, SearchAssetsScreenInterface { private val _showUnsupportedChainAlert = MutableLiveData>() val showUnsupportedChainAlert: LiveData> = _showUnsupportedChainAlert diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt index ec161e04e8..de0c320aae 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt @@ -8,14 +8,13 @@ import javax.inject.Provider import javax.inject.Singleton import jp.co.soramitsu.common.data.network.NetworkApiCreator import jp.co.soramitsu.common.data.storage.Preferences +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.interfaces.FileProvider -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.core.network.JsonFactory import jp.co.soramitsu.core.runtime.ChainConnection import jp.co.soramitsu.core.runtime.RuntimeFactory import jp.co.soramitsu.coredb.dao.AssetDao -import jp.co.soramitsu.coredb.dao.AssetReadOnlyCache import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.coredb.dao.MetaAccountDao import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry @@ -99,13 +98,13 @@ class ChainRegistryModule { runtimeSyncService: RuntimeSyncService, runtimeFilesCache: RuntimeFilesCache, chainDao: ChainDao, - networkStateMixin: NetworkStateMixin + networkStateService: NetworkStateService ) = RuntimeProviderPool( runtimeFactory, runtimeSyncService, runtimeFilesCache, chainDao, - networkStateMixin + networkStateService ) @Provides @@ -118,12 +117,12 @@ class ChainRegistryModule { socketProvider: Provider, externalRequirementsFlow: MutableStateFlow, nodesSettingsStorage: NodesSettingsStorage, - networkStateMixin: NetworkStateMixin + networkStateService: NetworkStateService ) = ConnectionPool( socketProvider, externalRequirementsFlow, nodesSettingsStorage, - networkStateMixin + networkStateService ) @Provides @@ -141,9 +140,9 @@ class ChainRegistryModule { @Provides @Singleton fun provideEthereumPool( - networkStateMixin: NetworkStateMixin + networkStateService: NetworkStateService ) = - EthereumConnectionPool(networkStateMixin) + EthereumConnectionPool(networkStateService) @Provides @@ -156,7 +155,7 @@ class ChainRegistryModule { chainSyncService: ChainSyncService, runtimeSyncService: RuntimeSyncService, updatesMixin: UpdatesMixin, - networkStateMixin: NetworkStateMixin, + networkStateService: NetworkStateService, ethereumConnectionPool: EthereumConnectionPool, assetReadOnlyCache: AssetDao, chainsRepository: ChainsRepository @@ -168,7 +167,7 @@ class ChainRegistryModule { chainSyncService, runtimeSyncService, updatesMixin, - networkStateMixin, + networkStateService, ethereumConnectionPool, assetReadOnlyCache, chainsRepository diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 6ab351b1a4..5f06e06721 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -4,7 +4,7 @@ import android.util.Log import javax.inject.Inject import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.NetworkIssueType -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.utils.diffed @@ -28,7 +28,6 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.NodeId import jp.co.soramitsu.runtime.multiNetwork.connection.ConnectionPool -import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumChainConnection import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumConnectionPool import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProvider import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProviderPool @@ -48,7 +47,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -71,7 +69,7 @@ class ChainRegistry @Inject constructor( private val chainSyncService: ChainSyncService, private val runtimeSyncService: RuntimeSyncService, private val updatesMixin: UpdatesMixin, - private val networkStateMixin: NetworkStateMixin, + private val networkStateService: NetworkStateService, private val ethereumConnectionPool: EthereumConnectionPool, private val assetsCache: AssetReadOnlyCache, private val chainsRepository: ChainsRepository, @@ -139,13 +137,13 @@ class ChainRegistry @Inject constructor( runCatching { setupChain(chain) }.onFailure { - networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) + networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) Log.e( "ChainRegistry", "error while sync in chain registry $it" ) }.onSuccess { - networkStateMixin.notifyChainSyncSuccess( + networkStateService.notifyChainSyncSuccess( chain.id ) } @@ -252,8 +250,9 @@ class ChainRegistry @Inject constructor( withContext(dispatcher) { val chain = getChain(id.chainId) if (!chain.isEthereumChain) { - connectionPool.getConnection(id.chainId).socketService.switchUrl(id.nodeUrl) - notifyNodeSwitched(id.chainId, id.nodeUrl) + connectionPool.getConnectionOrNull(id.chainId)?.socketService?.switchUrl(id.nodeUrl)?.let { + notifyNodeSwitched(id.chainId, id.nodeUrl) + } } } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt index d2c740ae52..cffaeeea4b 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt @@ -6,8 +6,7 @@ import javax.inject.Provider import jp.co.soramitsu.common.BuildConfig import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.NetworkIssueType -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin -import jp.co.soramitsu.common.mixin.api.NetworkStateUi +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.core.models.ChainNode import jp.co.soramitsu.core.runtime.ChainConnection @@ -26,6 +25,8 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -37,92 +38,82 @@ class ConnectionPool @Inject constructor( private val socketServiceProvider: Provider, private val externalRequirementFlow: MutableStateFlow, private val nodesSettingsStorage: NodesSettingsStorage, - private val networkStateMixin: NetworkStateMixin -) : NetworkStateUi by networkStateMixin, CoroutineScope by CoroutineScope(Dispatchers.Default) { + private val networkStateService: NetworkStateService +) : CoroutineScope by CoroutineScope(Dispatchers.Default) { - private val pool = ConcurrentHashMap() +// private val pool = ConcurrentHashMap() + private val poolFlow = MutableStateFlow>(emptyMap()) private val connectionWatcher = MutableStateFlow(Event(Unit)) - private val connections = connectionWatcher.flatMapLatest { - val connListFlow = pool.map { - it.value.isConnecting.map { isConnecting -> - it.value.chain.id to isConnecting - } - } - val connChainsListFlow = combine(connListFlow) { chains -> - chains.toMap() - } - connChainsListFlow - } +// private val connectionIssues = connectionWatcher.flatMapLatest { +// val connListFlow = pool.map { +// +// it.value.isConnecting.map { isConnecting -> +// it.value.chain to isConnecting +// } +// } +// val connectionIssues = combine(connListFlow) { chains -> +// val issues = +// chains.filter { (_, isConnecting) -> isConnecting }.mapNotNull { (iChain, _) -> +// val chain = iChain as? Chain ?: return@mapNotNull null +// NetworkIssueItemState( +// iconUrl = chain.icon, +// title = chain.name, +// type = when { +// chain.nodes.size > 1 -> NetworkIssueType.Node +// else -> NetworkIssueType.Network +// }, +// chainId = chain.id, +// chainName = chain.name, +// assetId = chain.utilityAsset?.id.orEmpty() +// ) +// } +// issues +// } +// +// connectionIssues +// } + +// private val showConnecting = connectionWatcher.flatMapLatest { +// val isConnectedListFlow = pool.map { it.value.isConnected } +// val hasConnectionsFlow = combine(isConnectedListFlow) { it.any { it } } +// +// val isPausedListFlow = pool.map { it.value.isPaused } +// val hasPausesFlow = combine(isPausedListFlow) { it.any { it } } +// +// val isConnectingListFlow = pool.map { it.value.isConnecting } +// val hasConnectingFlow = combine(isConnectingListFlow) { it.any { it } } +// .filter { connecting -> connecting } +// val showConnecting = combine( +// hasConnectionsFlow, +// hasConnectingFlow, +// hasPausesFlow +// ) { connected, connecting, paused -> +// !(connected || paused) && connecting +// } +// showConnecting +// } +// .distinctUntilChanged() +// .debounce(ConnectingStatusDebounce) - private val connectionIssues = connectionWatcher.flatMapLatest { - val connListFlow = pool.map { + init { + observeChainsStatus() + } - it.value.isConnecting.map { isConnecting -> - it.value.chain to isConnecting - } - } - val connectionIssues = combine(connListFlow) { chains -> - val issues = - chains.filter { (_, isConnecting) -> isConnecting }.mapNotNull { (iChain, _) -> - val chain = iChain as? Chain ?: return@mapNotNull null - NetworkIssueItemState( - iconUrl = chain.icon, - title = chain.name, - type = when { - chain.nodes.size > 1 -> NetworkIssueType.Node - else -> NetworkIssueType.Network - }, - chainId = chain.id, - chainName = chain.name, - assetId = chain.utilityAsset?.id.orEmpty() - ) - } - issues - } + private fun observeChainsStatus() { - connectionIssues } - private val showConnecting = connectionWatcher.flatMapLatest { - val isConnectedListFlow = pool.map { it.value.isConnected } - val hasConnectionsFlow = combine(isConnectedListFlow) { it.any { it } } - - val isPausedListFlow = pool.map { it.value.isPaused } - val hasPausesFlow = combine(isPausedListFlow) { it.any { it } } - - val isConnectingListFlow = pool.map { it.value.isConnecting } - val hasConnectingFlow = combine(isConnectingListFlow) { it.any { it } } - .filter { connecting -> connecting } - val showConnecting = combine( - hasConnectionsFlow, - hasConnectingFlow, - hasPausesFlow - ) { connected, connecting, paused -> - !(connected || paused) && connecting - } - showConnecting + fun getConnection(chainId: ChainId): ChainConnection { + return poolFlow.value[chainId] + return pool.getValue(chainId) } - .distinctUntilChanged() - .debounce(ConnectingStatusDebounce) - init { - connections.onEach { - networkStateMixin.updateChainConnection(it) - }.launchIn(scope = this) - - connectionIssues.onEach { - networkStateMixin.updateNetworkIssues(it) - }.launchIn(this) - - showConnecting.onEach { - networkStateMixin.updateShowConnecting(it) - }.launchIn(this) + suspend fun awaitConnection(chainId: ChainId): ChainConnection { + return poolFlow.map { it[chainId] }.filterNotNull().first() } - fun getConnection(chainId: ChainId): ChainConnection = pool.getValue(chainId) - - fun getConnectionOrNull(chainId: ChainId): ChainConnection? = pool.getOrDefault(chainId, null) + fun getConnectionOrNull(chainId: ChainId): ChainConnection? = poolFlow.value.getOrDefault(chainId, null) fun setupConnection( chain: Chain, diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt index 9d6c015dda..a209dae795 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt @@ -5,7 +5,7 @@ import java.net.URI import java.util.concurrent.ConcurrentHashMap import jp.co.soramitsu.common.BuildConfig import jp.co.soramitsu.common.data.network.runtime.binding.cast -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.utils.cycle import jp.co.soramitsu.core.models.ChainNode import jp.co.soramitsu.runtime.multiNetwork.chain.model.BSCChainId @@ -17,7 +17,6 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.goerliChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polygonChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polygonTestnetChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.sepoliaChainId -import jp.co.soramitsu.runtime.multiNetwork.runtime.RuntimeProvider import jp.co.soramitsu.runtime.multiNetwork.toSyncIssue import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope @@ -40,7 +39,7 @@ import org.web3j.protocol.websocket.WebSocketService private const val EVM_CONNECTION_TAG = "EVM Connection" class EthereumConnectionPool( - private val networkStateMixin: NetworkStateMixin, + private val networkStateService: NetworkStateService, ) { private val poolStateFlow = MutableStateFlow>(mutableMapOf()) @@ -62,7 +61,7 @@ class EthereumConnectionPool( it[chain.id] = EthereumChainConnection( chain, onSelectedNodeChange = onSelectedNodeChange - ) { networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) } + ) { networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) } } } return poolStateFlow.value.getValue(chain.id) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt index 281fb1476e..923b3e4005 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt @@ -1,6 +1,6 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.core.runtime.ConstructedRuntime import jp.co.soramitsu.core.runtime.RuntimeFactory import jp.co.soramitsu.coredb.dao.ChainDao @@ -31,7 +31,7 @@ class RuntimeProvider( private val runtimeSyncService: RuntimeSyncService, private val runtimeFilesCache: RuntimeFilesCache, private val chainDao: ChainDao, - private val networkStateMixin: NetworkStateMixin, + private val networkStateService: NetworkStateService, private val chain: Chain ) : CoroutineScope by CoroutineScope(Dispatchers.Default) { @@ -141,10 +141,10 @@ class RuntimeProvider( } runtimeFlow.emit(runtime) ChainsStateTracker.updateState(chainId) { it.copy(runtimeConstruction = ChainState.Status.Completed) } - networkStateMixin.notifyChainSyncSuccess(chainId) + networkStateService.notifyChainSyncSuccess(chainId) }.onFailure { error -> ChainsStateTracker.updateState(chainId) { it.copy(runtimeConstruction = ChainState.Status.Failed(error)) } - networkStateMixin.notifyChainSyncProblem(chain.toSyncIssue()) + networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) when (error) { ChainInfoNotInCacheException -> runtimeSyncService.cacheNotFound(chainId) else -> error.printStackTrace() diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt index 4c1e8ce3bc..0ad82e729c 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderPool.kt @@ -1,7 +1,7 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime import jp.co.soramitsu.common.data.network.runtime.binding.cast -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.core.runtime.RuntimeFactory import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain @@ -15,7 +15,7 @@ class RuntimeProviderPool( private val runtimeSyncService: RuntimeSyncService, private val runtimeFilesCache: RuntimeFilesCache, private val chainDao: ChainDao, - private val networkStateMixin: NetworkStateMixin + private val networkStateService: NetworkStateService ) { private val poolStateFlow = @@ -40,7 +40,7 @@ class RuntimeProviderPool( runtimeSyncService, runtimeFilesCache, chainDao, - networkStateMixin, + networkStateService, chain ) } @@ -55,6 +55,5 @@ class RuntimeProviderPool( it.remove(chainId)?.apply { finish() } } } - } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt index 2f42d14874..51bc1a0fd5 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSyncService.kt @@ -110,7 +110,7 @@ class RuntimeSyncService( val metadataHash = if (force || shouldSyncMetadata) { val runtimeMetadata = - connectionPool.getConnection(chainId).socketService.executeAsyncCatching( + connectionPool.awaitConnection(chainId).socketService.executeAsyncCatching( GetMetadataRequest, mapper = pojo().nonNull() ).getOrNull() diff --git a/runtime/src/test/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderTest.kt b/runtime/src/test/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderTest.kt index 5758019d13..b7024d4789 100644 --- a/runtime/src/test/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderTest.kt +++ b/runtime/src/test/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProviderTest.kt @@ -1,6 +1,6 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime -import jp.co.soramitsu.common.mixin.api.NetworkStateMixin +import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.core.runtime.ConstructedRuntime import jp.co.soramitsu.core.runtime.RuntimeFactory import jp.co.soramitsu.coredb.dao.ChainDao @@ -50,7 +50,7 @@ class RuntimeProviderTest { lateinit var chainDao: ChainDao @Mock - lateinit var networkStateMixin: NetworkStateMixin + lateinit var networkStateService: networkStateService lateinit var runtimeProvider: RuntimeProvider @@ -208,7 +208,7 @@ class RuntimeProviderTest { runtimeSyncService, runtimeFilesCache, chainDao, - networkStateMixin, + networkStateService, chain ) } From 25925021528573a40d199a34d2673e25153e3cce Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 6 May 2024 12:59:49 +0700 Subject: [PATCH 042/100] network issues wip --- .../app/root/presentation/RootActivity.kt | 12 +- .../app/root/presentation/RootViewModel.kt | 8 +- .../common/compose/component/FeeInfo.kt | 1 - .../common/domain/NetworkStateService.kt | 43 ++++--- .../jp/co/soramitsu/common/utils/KoltinExt.kt | 2 + common/src/main/res/values/strings.xml | 1 + .../account/impl/domain/WalletSyncService.kt | 15 +-- .../repository/CrowdloanRepositoryImpl.kt | 1 - .../staking/api/data/StakingSharedState.kt | 1 - .../updaters/StakingLedgerUpdater.kt | 1 - .../AccountControllerBalanceUpdater.kt | 1 - .../historical/HistoricalUpdateMediator.kt | 1 - .../data/repository/IdentityRepositoryImpl.kt | 3 +- .../impl/data/repository/PayoutRepository.kt | 1 - .../staking/impl/domain/StakingInteractor.kt | 1 - .../domain/interfaces/WalletInteractor.kt | 3 + .../updaters/BalancesUpdateSystem.kt | 34 ++--- .../wallet/impl/di/WalletFeatureModule.kt | 11 +- .../impl/domain/WalletInteractorImpl.kt | 29 +++-- .../assetDetails/AssetDetailsViewModel.kt | 15 ++- .../balance/list/BalanceListViewModel.kt | 98 ++++++++++----- .../presentation/balance/list/WalletScreen.kt | 3 +- .../presentation/balance/list/WalletState.kt | 9 +- .../presentation/balance/list/model/Mapper.kt | 9 ++ .../networkissues/NetworkIssuesViewModel.kt | 24 +++- .../searchAssets/SearchAssetsViewModel.kt | 13 +- .../impl/presentation/common/NetworkIssue.kt | 46 +++++-- .../runtime/di/ChainRegistryModule.kt | 5 +- .../runtime/multiNetwork/ChainRegistry.kt | 117 ++++++------------ .../multiNetwork/connection/ConnectionPool.kt | 43 +++---- .../connection/EthereumConnectionPool.kt | 3 +- .../multiNetwork/runtime/RuntimeProvider.kt | 3 +- .../runtime/RuntimeSubscriptionPool.kt | 6 +- .../runtime/RuntimeVersionSubscription.kt | 3 + .../updaters/SingleChainUpdateSystem.kt | 5 +- .../repository/ChainStateRepository.kt | 3 +- .../runtime/state/SingleAssetSharedState.kt | 1 - .../storage/source/RemoteStorageSource.kt | 4 +- 38 files changed, 309 insertions(+), 270 deletions(-) create mode 100644 feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/model/Mapper.kt diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt index f645a39689..5af323a6c9 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt @@ -133,12 +133,12 @@ class RootActivity : BaseActivity(), LifecycleObserver { } override fun subscribe(viewModel: RootViewModel) { - viewModel.showConnectingBarFlow.observe(lifecycleScope) { show -> - when { - show -> showBadConnectionView() - else -> hideBadConnectionView() - } - } +// viewModel.showConnectingBarFlow.observe(lifecycleScope) { show -> +// when { +// show -> showBadConnectionView() +// else -> hideBadConnectionView() +// } +// } viewModel.messageLiveData.observe( this, diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index 3add3e4ccf..deeaee36a4 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -12,8 +12,7 @@ import javax.inject.Inject import jp.co.soramitsu.app.R import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.mixin.api.networkStateService -import jp.co.soramitsu.common.mixin.api.NetworkStateUi +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.core.runtime.ChainConnection @@ -35,9 +34,8 @@ class RootViewModel @Inject constructor( private val interactor: RootInteractor, private val rootRouter: RootRouter, private val externalConnectionRequirementFlow: MutableStateFlow, - private val resourceManager: ResourceManager, - private val networkStateService: networkStateService -) : BaseViewModel(), NetworkStateUi by networkStateService { + private val resourceManager: ResourceManager +) : BaseViewModel() { companion object { private const val IDLE_MINUTES: Long = 20 } diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt index 1be2b488c9..fca7668247 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt @@ -70,7 +70,6 @@ fun FeeInfo(state: FeeInfoViewState, modifier: Modifier = Modifier, tooltipClick } } Column(modifier = Modifier.weight(1f)) { - Log.d("&&&", "state.feeAmount = ${state.feeAmount}") state.feeAmount?.let { CapsTitle(text = it, textAlign = TextAlign.End, modifier = Modifier.align(Alignment.End)) } ?: Shimmer( diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt b/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt index fc20ecb535..63a5144cb5 100644 --- a/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt +++ b/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt @@ -1,41 +1,56 @@ package jp.co.soramitsu.common.domain -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState +import jp.co.soramitsu.common.domain.model.NetworkIssueType import jp.co.soramitsu.core.models.ChainId +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.update class NetworkStateService { private val connectionPoolProblems = MutableStateFlow>(emptySet()) private val chainsSyncProblems = MutableStateFlow>(emptySet()) - private val _showConnectingBarFlow = MutableStateFlow(false) - val showConnectingBarFlow = _showConnectingBarFlow +// private val _showConnectingBarFlow = MutableStateFlow(false) +// val showConnectingBarFlow = _showConnectingBarFlow - val networkIssuesFlow = combine( + val networkIssuesFlow: Flow> = combine( connectionPoolProblems, chainsSyncProblems ) { connectionPoolProblems, chainsSyncProblems -> - connectionPoolProblems + chainsSyncProblems + val nodesIssues = connectionPoolProblems.map { it to NetworkIssueType.Node } + val runtimeIssues = chainsSyncProblems.map { it to NetworkIssueType.Network } + (nodesIssues + runtimeIssues).toMap() } - fun updateShowConnecting(isShow: Boolean) { - _showConnectingBarFlow.value = isShow +// fun updateShowConnecting(isShow: Boolean) { +// _showConnectingBarFlow.value = isShow +// } + + fun notifyConnectionProblem(chainId: ChainId) { + connectionPoolProblems.update { it + chainId } + } + + fun notifyConnectionSuccess(chainId: ChainId) { + connectionPoolProblems.update { + it - chainId + } } - fun updateNetworkIssues(list: List) { + fun updateNetworkIssues(list: List) { connectionPoolProblems.value = list.toSet() } - fun notifyChainSyncProblem(issue: NetworkIssueItemState) { - val previousSet = chainsSyncProblems.value - val newSet = previousSet + issue - chainsSyncProblems.value = newSet + fun notifyChainSyncProblem(chainId: ChainId) { + chainsSyncProblems.update { + it + chainId + } } fun notifyChainSyncSuccess(id: ChainId) { - val newSet = chainsSyncProblems.value.toMutableSet().apply { removeIf { it.chainId == id } } - chainsSyncProblems.value = newSet + chainsSyncProblems.update { + it - id + } } } diff --git a/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt b/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt index d5c5d574af..6d08ded12a 100644 --- a/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt +++ b/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt @@ -101,4 +101,6 @@ fun BigDecimal?.isNotZero(): Boolean = !isZero() fun BigDecimal.greaterThen(other: BigDecimal): Boolean = this.compareTo(other) == 1 fun BigInteger?.isZero(): Boolean = this?.compareTo(BigInteger.ZERO) == 0 +fun BigInteger?.greaterThanOrEquals(other: BigInteger): Boolean = this?.compareTo(other) == 1 || this?.compareTo(other) == 1 +fun BigInteger?.lessThan(other: BigInteger): Boolean = this?.compareTo(other) == -1 fun BigInteger?.isNotZero(): Boolean = !isZero() diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index c32e007cd8..1e0565afff 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -513,6 +513,7 @@ Add Account Switch Node Add an account + Connection Error: Unable to connect to the network. Please try again. Network is unavailable Node is unavailable Network Issues diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index eb171c4044..6ddebf0970 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -71,7 +71,6 @@ class WalletSyncService( val ethereumChains = chains.filter { it.isEthereumChain } val substrateChains = chains.filter { !it.isEthereumChain } - val metaAccounts = localMetaAccounts.map { accountInfo -> mapMetaAccountLocalToMetaAccount( @@ -153,9 +152,6 @@ class WalletSyncService( if (isEquilibriumTypeChain) { buildEquilibriumAssets(metaAccounts, chain, runtime) } else { - if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ - Log.d("&&&", "hydra dx chain assets: ${chain.assets}") - } val allAccountsStorageKeys = metaAccounts.mapNotNull { metaAccount -> val accountId = @@ -168,9 +164,7 @@ class WalletSyncService( accountId ) }.flatten() - if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ - Log.d("&&&", "hydra dx storage keys: ${allAccountsStorageKeys}") - } + val keysToQuery = allAccountsStorageKeys.mapNotNull { metadata -> // if storage key build is failed - we put the empty assets @@ -189,18 +183,13 @@ class WalletSyncService( } metadata.key }.toList() - if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ - Log.d("&&&", "hydra dx keysToQuery: ${keysToQuery}") - } val storageKeyToResult = remoteStorageSource.queryKeys( keysToQuery, chain.id, null ) - if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ - Log.d("&&&", "hydra dx storageKeyToResult: ${keysToQuery}") - } + allAccountsStorageKeys.map { metadata -> val hexRaw = storageKeyToResult.getOrDefault( diff --git a/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/data/repository/CrowdloanRepositoryImpl.kt b/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/data/repository/CrowdloanRepositoryImpl.kt index 0a0722f3dd..3408b121df 100644 --- a/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/data/repository/CrowdloanRepositoryImpl.kt +++ b/feature-crowdloan-impl/src/main/java/jp/co/soramitsu/crowdloan/impl/data/repository/CrowdloanRepositoryImpl.kt @@ -23,7 +23,6 @@ import jp.co.soramitsu.crowdloan.impl.storage.CrowdloanStorage import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId -import jp.co.soramitsu.runtime.multiNetwork.getRuntime import jp.co.soramitsu.runtime.storage.source.StorageDataSource import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.shared_utils.hash.Hasher.blake2b256 diff --git a/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt b/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt index 4873c41adf..f5075a7aba 100644 --- a/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt +++ b/feature-staking-api/src/main/java/jp/co/soramitsu/staking/api/data/StakingSharedState.kt @@ -11,7 +11,6 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.reefChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.soraMainChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.soraTestChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.ternoaChainId -import jp.co.soramitsu.runtime.multiNetwork.chainWithAsset import jp.co.soramitsu.runtime.state.SingleAssetSharedState import jp.co.soramitsu.wallet.impl.domain.TokenUseCase import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt index 4bf2bc2ab5..b7aacb56f2 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt @@ -13,7 +13,6 @@ import jp.co.soramitsu.core.updater.Updater import jp.co.soramitsu.coredb.dao.AccountStakingDao import jp.co.soramitsu.coredb.model.AccountStakingLocal import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.getRuntime import jp.co.soramitsu.runtime.network.updaters.insert import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.shared_utils.runtime.AccountId diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/controller/AccountControllerBalanceUpdater.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/controller/AccountControllerBalanceUpdater.kt index e838b98a3a..3d60f2126e 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/controller/AccountControllerBalanceUpdater.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/controller/AccountControllerBalanceUpdater.kt @@ -7,7 +7,6 @@ import jp.co.soramitsu.common.utils.system import jp.co.soramitsu.core.updater.SubscriptionBuilder import jp.co.soramitsu.core.updater.Updater import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry -import jp.co.soramitsu.runtime.multiNetwork.getRuntime import jp.co.soramitsu.shared_utils.runtime.metadata.storage import jp.co.soramitsu.shared_utils.runtime.metadata.storageKey import jp.co.soramitsu.staking.api.data.StakingSharedState diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/historical/HistoricalUpdateMediator.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/historical/HistoricalUpdateMediator.kt index c75dd221d4..1fa6a0e293 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/historical/HistoricalUpdateMediator.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/historical/HistoricalUpdateMediator.kt @@ -8,7 +8,6 @@ import jp.co.soramitsu.core.updater.SubscriptionBuilder import jp.co.soramitsu.core.updater.Updater import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId -import jp.co.soramitsu.runtime.multiNetwork.getRuntime import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.staking.api.data.StakingSharedState import jp.co.soramitsu.staking.impl.data.network.blockhain.updaters.fetchValuesToCache diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt index 77d05df8f8..9d570b23eb 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt @@ -7,7 +7,6 @@ import jp.co.soramitsu.runtime.ext.accountFromMapKey import jp.co.soramitsu.runtime.ext.hexAccountIdOf import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain -import jp.co.soramitsu.runtime.multiNetwork.getSocket import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.shared_utils.runtime.definitions.types.Type @@ -34,7 +33,7 @@ class IdentityRepositoryImpl( chain: Chain, accountIdsHex: List ): AccountIdMap = withContext(Dispatchers.Default) { - val socketService = chainRegistry.getSocket(chain.id) + val socketService = chainRegistry.awaitConnection(chain.id).socketService val runtime = chainRegistry.getRuntime(chain.id) val identityModule = runtime.metadata.module("Identity") diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/PayoutRepository.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/PayoutRepository.kt index 411231e421..8c563cff49 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/PayoutRepository.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/PayoutRepository.kt @@ -11,7 +11,6 @@ import jp.co.soramitsu.runtime.ext.accountIdOf import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId -import jp.co.soramitsu.runtime.multiNetwork.getService import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.shared_utils.runtime.AccountId diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractor.kt index 7c4de6100e..e5ba140b21 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractor.kt @@ -22,7 +22,6 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polkadotChainId -import jp.co.soramitsu.runtime.multiNetwork.getRuntimeOrNull import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.shared_utils.runtime.metadata.RuntimeMetadata import jp.co.soramitsu.shared_utils.runtime.metadata.moduleOrNull diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt index ddf9aa2803..d1e8c99fc3 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt @@ -9,6 +9,7 @@ import jp.co.soramitsu.common.data.model.CursorPage import jp.co.soramitsu.common.data.network.runtime.binding.EqAccountInfo import jp.co.soramitsu.common.data.network.runtime.binding.EqOraclePricePoint import jp.co.soramitsu.common.data.secrets.v2.MetaAccountSecrets +import jp.co.soramitsu.common.domain.model.NetworkIssueType import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.core.models.ChainId import jp.co.soramitsu.coredb.model.AddressBookContact @@ -158,4 +159,6 @@ interface WalletInteractor { fun getAssetManagementIntroPassed(): Boolean suspend fun saveAssetManagementIntroPassed() + fun networkIssuesFlow(): Flow> + suspend fun retryChainSync(chainId: ChainId): Result } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt index bdf9101621..ee1de3e678 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt @@ -2,19 +2,15 @@ package jp.co.soramitsu.wallet.impl.data.network.blockchain.updaters import android.annotation.SuppressLint import android.util.Log -import it.airgap.beaconsdk.core.internal.utils.failure -import it.airgap.beaconsdk.core.internal.utils.onEachFailure -import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository +import it.airgap.beaconsdk.core.internal.utils.success import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.account.api.domain.model.address import jp.co.soramitsu.account.impl.data.mappers.mapMetaAccountLocalToMetaAccount import jp.co.soramitsu.account.impl.domain.buildStorageKeys import jp.co.soramitsu.account.impl.domain.handleBalanceResponse -import jp.co.soramitsu.common.data.network.rpc.BulkRetriever import jp.co.soramitsu.common.data.network.runtime.binding.ExtrinsicStatusEvent import jp.co.soramitsu.common.data.network.runtime.binding.SimpleBalanceData -import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.utils.failure import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.common.utils.requireException @@ -29,9 +25,6 @@ import jp.co.soramitsu.coredb.model.OperationLocal import jp.co.soramitsu.runtime.ext.addressOf import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain -import jp.co.soramitsu.runtime.multiNetwork.getSocket -import jp.co.soramitsu.runtime.multiNetwork.getSocketOrNull -import jp.co.soramitsu.runtime.multiNetwork.toSyncIssue import jp.co.soramitsu.runtime.network.subscriptionFlowCatching import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.RuntimeRequest @@ -54,14 +47,12 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.transform import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -75,7 +66,6 @@ class BalancesUpdateSystem( private val assetCache: AssetCache, private val substrateSource: SubstrateRemoteSource, private val operationDao: OperationDao, - private val networkStateService: networkStateService, private val ethereumRemoteSource: EthereumRemoteSource ) : UpdateSystem { @@ -104,11 +94,8 @@ class BalancesUpdateSystem( listenEthereumBalancesByTrigger(chain, metaAccount).launchIn(scope) } else { if (!chain.isEthereumBased || metaAccount.ethereumPublicKey != null) { - subscribeChainBalances(chain, metaAccount).onEachFailure { - logError( - chain, - it - ) + subscribeChainBalances(chain, metaAccount).onEach { result -> + result.onFailure { logError(chain, it) } }.launchIn(scope) } } @@ -127,7 +114,7 @@ class BalancesUpdateSystem( val specificChainTriggered = triggeredChainId != null val currentChainTriggered = triggeredChainId == chain.id - if (specificChainTriggered && currentChainTriggered.not()) return@map Result.failure() + if (specificChainTriggered && currentChainTriggered.not()) return@map Result.success(Unit) kotlin.runCatching { fetchEthereumBalances(chain, listOf(metaAccount)) } } @@ -143,11 +130,9 @@ class BalancesUpdateSystem( ?.observeWithTimeout(RUNTIME_AWAITING_TIMEOUT) ?.flatMapLatest { runtimeResult -> if (runtimeResult.isFailure) { - networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) return@flatMapLatest flowOf(runtimeResult) } val runtime = runtimeResult.requireValue() - networkStateService.notifyChainSyncSuccess(chain.id) val storageKeys = buildStorageKeys( @@ -158,10 +143,11 @@ class BalancesUpdateSystem( .getOrNull() ?: return@flatMapLatest flowOf(Result.failure(RuntimeException("Can't build storage keys for meta account ${metaAccount.name}, chain: ${chain.name}"))) - val socketService = runCatching { chainRegistry.getSocketOrNull(chain.id) } - .onFailure { return@flatMapLatest flowOf(Result.failure(it)) } - .getOrNull() - ?: return@flatMapLatest flowOf(Result.failure(RuntimeException("Error getting socket for chain ${chain.name}"))) + val socketService = + runCatching { chainRegistry.awaitConnection(chain.id).socketService } + .onFailure { return@flatMapLatest flowOf(Result.failure(it)) } + .getOrNull() + ?: return@flatMapLatest flowOf(Result.failure(RuntimeException("Error getting socket for chain ${chain.name}"))) val request = SubscribeBalanceRequest(storageKeys.mapNotNull { it.key }) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt index 725fff0a89..1643aa3843 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt @@ -20,12 +20,11 @@ import jp.co.soramitsu.common.data.network.HttpExceptionHandler import jp.co.soramitsu.common.data.network.NetworkApiCreator import jp.co.soramitsu.common.data.network.coingecko.CoingeckoApi import jp.co.soramitsu.common.data.network.config.RemoteConfigFetcher -import jp.co.soramitsu.common.data.network.rpc.BulkRetriever import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.interfaces.FileProvider -import jp.co.soramitsu.common.mixin.api.networkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.QrBitmapDecoder @@ -267,7 +266,8 @@ class WalletFeatureModule { selectedFiat: SelectedFiat, updatesMixin: UpdatesMixin, xcmEntitiesFetcher: XcmEntitiesFetcher, - chainsRepository: ChainsRepository + chainsRepository: ChainsRepository, + networkStateService: NetworkStateService ): WalletInteractor = WalletInteractorImpl( walletRepository, addressBookRepository, @@ -279,7 +279,8 @@ class WalletFeatureModule { selectedFiat, updatesMixin, xcmEntitiesFetcher, - chainsRepository + chainsRepository, + networkStateService ) @Provides @@ -379,7 +380,6 @@ class WalletFeatureModule { assetCache: AssetCache, substrateSource: SubstrateRemoteSource, operationDao: OperationDao, - networkStateService: networkStateService, ethereumRemoteSource: EthereumRemoteSource ): UpdateSystem = BalancesUpdateSystem( chainRegistry, @@ -387,7 +387,6 @@ class WalletFeatureModule { assetCache, substrateSource, operationDao, - networkStateService, ethereumRemoteSource ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index 66b6962e37..780e3c2a5f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -21,6 +21,7 @@ import jp.co.soramitsu.common.interfaces.FileProvider import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.domain.NetworkStateService +import jp.co.soramitsu.common.domain.model.NetworkIssueType import jp.co.soramitsu.common.model.AssetBooleanState import jp.co.soramitsu.common.utils.Modules import jp.co.soramitsu.common.utils.mapList @@ -35,7 +36,6 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.isPolkadotOrKusama import jp.co.soramitsu.runtime.multiNetwork.chain.model.polkadotChainId -import jp.co.soramitsu.runtime.multiNetwork.chainWithAsset import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.shared_utils.runtime.extrinsic.ExtrinsicBuilder @@ -60,6 +60,7 @@ import jp.co.soramitsu.wallet.impl.domain.model.Transfer import jp.co.soramitsu.wallet.impl.domain.model.WalletAccount import jp.co.soramitsu.wallet.impl.domain.model.toPhishingModel import jp.co.soramitsu.xcm.domain.XcmEntitiesFetcher +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -71,6 +72,7 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.withIndex import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeoutOrNull import jp.co.soramitsu.core.models.Asset as CoreAsset private const val QR_PREFIX_SUBSTRATE = "substrate" @@ -95,7 +97,9 @@ class WalletInteractorImpl( private val selectedFiat: SelectedFiat, private val updatesMixin: UpdatesMixin, private val xcmEntitiesFetcher: XcmEntitiesFetcher, - private val chainsRepository: ChainsRepository + private val chainsRepository: ChainsRepository, + private val networkStateService: NetworkStateService, + private val coroutineContext: CoroutineContext = Dispatchers.Default, ) : WalletInteractor, UpdatesProviderUi by updatesMixin { @OptIn(ExperimentalCoroutinesApi::class) @@ -651,16 +655,27 @@ class WalletInteractorImpl( AssetSorting.FiatBalance.toString() }.map { sortingAsString -> sortingAsString?.let { - AssetSorting.values().find { sorting -> sorting.name == it } + AssetSorting.entries.find { sorting -> sorting.name == it } } ?: AssetSorting.FiatBalance } } - override fun getChainInitializationStatus(chainId: ChainId): NetworkStateService { - + override fun networkIssuesFlow(): Flow> { + return networkStateService.networkIssuesFlow } - override fun networkIssuesFlow(): Flow<> { - + override suspend fun retryChainSync(chainId: ChainId): Result { + return withContext(coroutineContext) { + val chain = chainsRepository.getChain(chainId) + chainRegistry.setupChain(chain) + val runtime = withTimeoutOrNull(15_000L) { + chainRegistry.awaitRuntimeProvider(chainId).get() + } + if(runtime == null) { + return@withContext Result.failure(Exception("Failed to sync chain")) + } else { + return@withContext Result.success(Unit) + } + } } } \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt index 6e521925d6..f97f509872 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetDetails/AssetDetailsViewModel.kt @@ -18,6 +18,7 @@ import jp.co.soramitsu.common.compose.component.MainToolbarViewState import jp.co.soramitsu.common.compose.component.MultiToggleButtonState import jp.co.soramitsu.common.compose.component.NetworkIssueType import jp.co.soramitsu.common.compose.component.ToolbarHomeIconState +import jp.co.soramitsu.common.domain.model.NetworkIssue import jp.co.soramitsu.common.presentation.LoadingState import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.applyFiatRate @@ -187,7 +188,15 @@ class AssetDetailsViewModel @Inject constructor( }.launchIn(viewModelScope) } - private val networkIssuesFlow = interactor.networkNotReadyFlow() + private val networkIssuesFlow = interactor.networkIssuesFlow().map { issuesMap -> + issuesMap.mapValues { + when(it.value) { + jp.co.soramitsu.common.domain.model.NetworkIssueType.Node -> NetworkIssueType.Node + jp.co.soramitsu.common.domain.model.NetworkIssueType.Network -> NetworkIssueType.Network + jp.co.soramitsu.common.domain.model.NetworkIssueType.Account -> NetworkIssueType.Account + } + } + } private fun subscribeAssets() { val assetSortingFlow = interactor.observeAssetSorting() @@ -225,9 +234,7 @@ class AssetDetailsViewModel @Inject constructor( val networkIssueType = if (asset?.hasAccount == false) { NetworkIssueType.Account } else { - networkIssues.firstOrNull { - it.chainId == chain.id - }?.type + networkIssues[chain.id] } AssetDetailsItemViewState( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 0e0d746f6d..ab9b9d7dd5 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -11,6 +11,7 @@ import co.jp.soramitsu.walletconnect.domain.WalletConnectInteractor import com.walletconnect.android.internal.common.exception.MalformedWalletConnectUri import dagger.hilt.android.lifecycle.HiltViewModel import java.math.BigDecimal +import java.math.BigInteger import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario @@ -28,7 +29,6 @@ import jp.co.soramitsu.common.compose.component.ChainSelectorViewStateWithFilter import jp.co.soramitsu.common.compose.component.ChangeBalanceViewState import jp.co.soramitsu.common.compose.component.MainToolbarViewStateWithFilters import jp.co.soramitsu.common.compose.component.MultiToggleButtonState -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.SwipeState import jp.co.soramitsu.common.compose.component.ToolbarHomeIconState import jp.co.soramitsu.common.compose.models.LoadableListPage @@ -39,9 +39,9 @@ import jp.co.soramitsu.common.data.network.coingecko.FiatChooserEvent import jp.co.soramitsu.common.data.network.coingecko.FiatCurrency import jp.co.soramitsu.common.domain.FiatCurrencies import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.domain.SelectedFiat -import jp.co.soramitsu.common.mixin.api.networkStateService -import jp.co.soramitsu.common.mixin.api.NetworkStateUi +import jp.co.soramitsu.common.domain.model.NetworkIssueType import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.presentation.LoadingState @@ -51,7 +51,9 @@ import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.combine import jp.co.soramitsu.common.utils.formatAsChange import jp.co.soramitsu.common.utils.formatFiat +import jp.co.soramitsu.common.utils.greaterThanOrEquals import jp.co.soramitsu.common.utils.inBackground +import jp.co.soramitsu.common.utils.lessThan import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.common.view.bottomSheet.list.dynamic.DynamicListBottomSheet @@ -82,6 +84,7 @@ import jp.co.soramitsu.wallet.impl.presentation.balance.chainselector.toChainIte import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.AssetType import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.BalanceListItemModel import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.toAssetState +import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.toUiModel import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenModel import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenView import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.ScreenModel @@ -113,6 +116,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -128,7 +132,7 @@ class BalanceListViewModel @Inject constructor( private val selectedFiat: SelectedFiat, private val accountInteractor: AccountInteractor, private val updatesMixin: UpdatesMixin, - private val networkStateService: networkStateService, + private val networkStateService: NetworkStateService, private val resourceManager: ResourceManager, private val clipboardManager: ClipboardManager, private val currentAccountAddress: CurrentAccountAddressUseCase, @@ -136,7 +140,7 @@ class BalanceListViewModel @Inject constructor( private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, private val nftInteractor: NFTInteractor, private val walletConnectInteractor: WalletConnectInteractor -) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateService, +) : BaseViewModel(), UpdatesProviderUi by updatesMixin, WalletScreenInterface { private var awaitAssetsJob: Job? = null @@ -181,9 +185,7 @@ class BalanceListViewModel @Inject constructor( } } - private val networkIssueStateFlow = selectedChainId.map { - it?.let { } - } + private val networkIssueStateFlow = MutableStateFlow(null) private val currentMetaAccountFlow = interactor.selectedLightMetaAccountFlow() @@ -196,26 +198,26 @@ class BalanceListViewModel @Inject constructor( private val showNetworkIssues = MutableStateFlow(false) + private val currentAssetsFlow = MutableStateFlow>(emptyList()) + private val assetStates = combine( interactor.assetsFlowAndAccount(), chainInteractor.getChainsFlow(), selectedChainId, interactor.selectedMetaAccountFlow(), - networkIssuesFlow, interactor.observeSelectedAccountChainSelectFilter() ) { (walletId: Long, assets: List), chains: List, selectedChainId: ChainId?, currentMetaAccountFlow: MetaAccount, - networkIssues: Set, appliedFilterAsString: String -> val filter = ChainSelectorViewStateWithFilters.Filter.entries.find { it.name == appliedFilterAsString } ?: ChainSelectorViewStateWithFilters.Filter.All - val shouldShowNetworkIssues = - selectedChainId == null && (networkIssues.isNotEmpty() || assets.any { it.hasAccount.not() }) +// val shouldShowNetworkIssues = +// selectedChainId == null && (networkIssues.isNotEmpty() || assets.any { it.hasAccount.not() }) showNetworkIssues.value = false val selectedAccountFavoriteChains = currentMetaAccountFlow.favoriteChains @@ -241,19 +243,21 @@ class BalanceListViewModel @Inject constructor( else -> emptyList() } - val filteredAssets = assets + val filteredAssets = assets.asSequence() .filter { it.asset.enabled != false && ((selectedChainId == null && filter == ChainSelectorViewStateWithFilters.Filter.All) || it.asset.token.configuration.chainId == selectedChainId || it.asset.token.configuration.chainId in filteredChains.map { it.id }) } - + .filter { it.asset.freeInPlanks.greaterThanOrEquals(BigInteger.ZERO) } + .toList() + currentAssetsFlow.update { filteredAssets } val balanceListItems = AssetListHelper.processAssets( assets = filteredAssets, filteredChains = filteredChains, selectedChainId = selectedChainId, - networkIssues = networkIssues + networkIssues = emptySet() ) val assetStates: List = balanceListItems @@ -364,22 +368,26 @@ class BalanceListViewModel @Inject constructor( ) } - private val assetTypeState = combine( + private val assetTypeState: Flow = combine( selectedChainId, assetTypeSelectorState, assetStates, nftInteractor.nftFiltersFlow(), createNFTCollectionScreenViewsFlow(), - ) { selectedChainId, selectorState, assetStates, filters, (pageViews, screenLayout) -> + networkIssueStateFlow.onEach { networkIssueState -> Log.d("&&&", "network issues onEach: $networkIssueState")} + ) { selectedChainId, selectorState, assetStates, filters, (pageViews, screenLayout), networkIssueState -> when (selectorState.currentSelection) { AssetType.Currencies -> { - if(selectedChainId != null && assetStates.isEmpty()) { - + val isSelectedChainHasIssues = networkIssueState != null + Log.d("&&&", "isSelectedChainHasIssues ${isSelectedChainHasIssues}") + if (isSelectedChainHasIssues) { + requireNotNull(networkIssueState) + } else { + WalletAssetsState.Assets( + assets = assetStates, + isHideVisible = selectedChainId != null + ) } - WalletAssetsState.Assets( - assets = assetStates, - isHideVisible = selectedChainId != null - ) } AssetType.NFTs -> { @@ -411,14 +419,30 @@ class BalanceListViewModel @Inject constructor( .stateIn(viewModelScope, SharingStarted.Eagerly, initialValue = null) } - private fun observeNetworkState() { - networkStateService.showConnectingBarFlow - .onEach { hasConnectionProblems -> - if (!hasConnectionProblems) { - refresh() - } - } - .launchIn(viewModelScope) + private fun observeNetworkIssues() { + combine( + currentAssetsFlow, + interactor.networkIssuesFlow(), + selectedChainId + ) { currentAssets, networkIssues, selectedChainId -> + if (selectedChainId == null) return@combine null + + val isAllAssetsWithProblems = + currentAssets.isNotEmpty() && currentAssets.filter { it.asset.token.configuration.chainId == selectedChainId } + .all { it.asset.freeInPlanks == null || it.asset.freeInPlanks.lessThan(BigInteger.ZERO) } + hashCode() + if (isAllAssetsWithProblems.not()) return@combine null + + val selectedChainIssue = networkIssues[selectedChainId] ?: NetworkIssueType.Network + + WalletAssetsState.NetworkIssue( + selectedChainId, + selectedChainIssue.toUiModel(), + false + ) + }.onEach { newState -> + networkIssueStateFlow.update { newState } + }.launchIn(viewModelScope) } // we open screen - no assets in the list @@ -564,8 +588,9 @@ class BalanceListViewModel @Inject constructor( init { subscribeScreenState() - observeNetworkState() + observeNetworkIssues() observeFiatSymbolChange() + sync() router.chainSelectorPayloadFlow.map { chainId -> val walletId = interactor.getSelectedMetaAccount().id @@ -598,7 +623,15 @@ class BalanceListViewModel @Inject constructor( } override fun onRetry() { + val (chainId, issueType, _) = networkIssueStateFlow.value ?: return + if (issueType != jp.co.soramitsu.common.compose.component.NetworkIssueType.Account) { + viewModelScope.launch { + networkIssueStateFlow.update { it?.copy(retryButtonLoading = true) } + interactor.retryChainSync(chainId) + networkIssueStateFlow.update { it?.copy(retryButtonLoading = false) } + } + } } private fun refresh() { @@ -641,6 +674,7 @@ class BalanceListViewModel @Inject constructor( private fun sync() { viewModelScope.launch { withContext(Dispatchers.Default) { + Log.d("&&&", "sync assets rates") getAvailableFiatCurrencies.sync() interactor.syncAssetsRates().onFailure { withContext(Dispatchers.Main) { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt index 39d11223f9..a1125654ba 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletScreen.kt @@ -22,7 +22,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.SwipeableState import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import androidx.compose.runtime.remember @@ -141,7 +140,7 @@ fun WalletScreen( ) } is WalletAssetsState.NetworkIssue -> { - NetworkIssue(callback::onRetry) + NetworkIssue(data.assetsState.retryButtonLoading, callback::onRetry) } } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt index 1301cc4ec9..1f5696b3de 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/WalletState.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Stable import jp.co.soramitsu.common.compose.component.AssetBalanceViewState import jp.co.soramitsu.common.compose.component.ChangeBalanceViewState import jp.co.soramitsu.common.compose.component.MultiToggleButtonState +import jp.co.soramitsu.common.compose.component.NetworkIssueType import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.wallet.impl.presentation.balance.nft.list.models.NFTCollectionsScreenModel @@ -45,10 +46,14 @@ sealed interface WalletAssetsState { ): WalletAssetsState, AssetListState(assets) @Immutable - data object NetworkIssue: WalletAssetsState + data class NetworkIssue( + val chainId: String, + val issueType: NetworkIssueType, + val retryButtonLoading: Boolean + ): WalletAssetsState @JvmInline value class NftAssets( val collectionScreenModel: NFTCollectionsScreenModel ): WalletAssetsState -} \ No newline at end of file +} diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/model/Mapper.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/model/Mapper.kt new file mode 100644 index 0000000000..907ca3a66c --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/model/Mapper.kt @@ -0,0 +1,9 @@ +package jp.co.soramitsu.wallet.impl.presentation.balance.list.model + +import jp.co.soramitsu.common.domain.model.NetworkIssueType + +fun NetworkIssueType.toUiModel() = when (this) { + NetworkIssueType.Node -> jp.co.soramitsu.common.compose.component.NetworkIssueType.Node + NetworkIssueType.Network -> jp.co.soramitsu.common.compose.component.NetworkIssueType.Network + NetworkIssueType.Account -> jp.co.soramitsu.common.compose.component.NetworkIssueType.Account +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt index ad8e3fb280..71c8c1c777 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt @@ -10,14 +10,14 @@ import jp.co.soramitsu.common.AlertViewState import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.NetworkIssueType -import jp.co.soramitsu.common.mixin.api.networkStateService -import jp.co.soramitsu.common.mixin.api.NetworkStateUi +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.feature_wallet_impl.R import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import jp.co.soramitsu.wallet.impl.presentation.WalletRouter +import jp.co.soramitsu.wallet.impl.presentation.balance.list.model.toUiModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first @@ -33,10 +33,10 @@ class NetworkIssuesViewModel @Inject constructor( private val walletInteractor: WalletInteractor, private val accountInteractor: AccountInteractor, private val updatesMixin: UpdatesMixin, - private val networkStateService: networkStateService, + private val networkStateService: NetworkStateService, private val resourceManager: ResourceManager, private val assetNotNeedAccount: AssetNotNeedAccountUseCase -) : BaseViewModel(), UpdatesProviderUi by updatesMixin, NetworkStateUi by networkStateService { +) : BaseViewModel(), UpdatesProviderUi by updatesMixin { companion object { private const val KEY_ALERT_RESULT = "result" @@ -44,8 +44,22 @@ class NetworkIssuesViewModel @Inject constructor( private var lastSelectedNetworkIssueState: NetworkIssueItemState? = null + // todo do we need this screen? + private val networkIssuesState = networkStateService.networkIssuesFlow.map { issuesMap -> + issuesMap.entries.map { + NetworkIssueItemState( + iconUrl = "stub",//it.asset.token.configuration.chainIcon ?: it.asset.token.configuration.iconUrl, + title = "stub", + type = it.value.toUiModel(), + chainId = it.key, + chainName = "stub",//it.asset.token.configuration.chainName, + assetId = "stub"//it.asset.token.configuration.id + ) + } + }.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList()) + val state = combine( - networkStateService.networkIssuesFlow.stateIn(viewModelScope, SharingStarted.Eagerly, emptySet()), + networkIssuesState, walletInteractor.assetsFlow().map { it.filter { !it.hasAccount && !it.asset.markedNotNeed }.map { NetworkIssueItemState( diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt index 4b4e4702f1..b383f12360 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt @@ -10,11 +10,8 @@ import java.math.BigDecimal import javax.inject.Inject import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.compose.component.ActionItemType -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState import jp.co.soramitsu.common.compose.component.SwipeState import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState -import jp.co.soramitsu.common.mixin.api.networkStateService -import jp.co.soramitsu.common.mixin.api.NetworkStateUi import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain @@ -39,9 +36,8 @@ class SearchAssetsViewModel @Inject constructor( val savedStateHandle: SavedStateHandle, private val interactor: WalletInteractor, private val chainInteractor: ChainInteractor, - private val router: WalletRouter, - private val networkStateService: networkStateService -) : BaseViewModel(), NetworkStateUi by networkStateService, SearchAssetsScreenInterface { + private val router: WalletRouter +) : BaseViewModel(), SearchAssetsScreenInterface { private val _showUnsupportedChainAlert = MutableLiveData>() val showUnsupportedChainAlert: LiveData> = _showUnsupportedChainAlert @@ -54,13 +50,12 @@ class SearchAssetsViewModel @Inject constructor( private val assetStates = combine( interactor.assetsFlow(), chainInteractor.getChainsFlow(), - networkIssuesFlow - ) { assets: List, chains: List, networkIssues: Set -> + ) { assets: List, chains: List -> val balanceListItems = AssetListHelper.processAssets( assets = assets, filteredChains = chains, - networkIssues = networkIssues + networkIssues = emptySet() ) val assetStates: List = balanceListItems diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt index 19b2f024a0..91c8364418 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/common/NetworkIssue.kt @@ -1,53 +1,73 @@ package jp.co.soramitsu.wallet.impl.presentation.common -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import jp.co.soramitsu.common.compose.component.B0 import jp.co.soramitsu.common.compose.component.GradientIcon import jp.co.soramitsu.common.compose.component.GrayButton import jp.co.soramitsu.common.compose.component.H3 +import jp.co.soramitsu.common.compose.component.MarginVertical +import jp.co.soramitsu.common.compose.theme.FearlessAppTheme import jp.co.soramitsu.common.compose.theme.alertYellow import jp.co.soramitsu.common.compose.theme.white50 import jp.co.soramitsu.feature_wallet_impl.R -// compose component for network issues, contains warning icon, error message and try again button @Composable -fun NetworkIssue(onRetry: () -> Unit) { +fun NetworkIssue(retryButtonLoading: Boolean, onRetry: () -> Unit) { Column( modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(16.dp) + horizontalAlignment = Alignment.CenterHorizontally ) { - //Connection Error: Unable to connect to the network. Please try again. + Spacer(modifier = Modifier.weight(1f)) + GradientIcon( iconRes = R.drawable.ic_alert_24, color = alertYellow, modifier = Modifier.align(Alignment.CenterHorizontally), contentPadding = PaddingValues(bottom = 4.dp) ) - - H3(text = "Connection Error") + MarginVertical(margin = 16.dp) + H3(text = stringResource(R.string.common_search_assets_alert_title)) + MarginVertical(margin = 16.dp) B0( - text = "Unable to connect to the network. Please try again.", - color = white50 + text = stringResource(R.string.network_issue_main), + color = white50, + textAlign = TextAlign.Center, ) - GrayButton(modifier = Modifier.width(200.dp).height(48.dp), text = "Try again", onClick = onRetry) + Spacer(modifier = Modifier.weight(1f)) + GrayButton( + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + text = stringResource(id = R.string.common_try_again), + loading = retryButtonLoading, + onClick = onRetry + ) + MarginVertical(margin = 80.dp) } } @Preview @Composable fun NetworkIssuePreview() { - NetworkIssue { + FearlessAppTheme { + Column { + NetworkIssue(true) { + + } + NetworkIssue(false) { + } + } } } \ No newline at end of file diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt index de0c320aae..5106cd3158 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt @@ -129,8 +129,9 @@ class ChainRegistryModule { @Singleton fun provideRuntimeVersionSubscriptionPool( chainDao: ChainDao, - runtimeSyncService: RuntimeSyncService - ) = RuntimeSubscriptionPool(chainDao, runtimeSyncService) + runtimeSyncService: RuntimeSyncService, + networkStateService: NetworkStateService + ) = RuntimeSubscriptionPool(chainDao, runtimeSyncService, networkStateService) @Provides @Singleton diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 5f06e06721..b714a82b90 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -2,21 +2,16 @@ package jp.co.soramitsu.runtime.multiNetwork import android.util.Log import javax.inject.Inject -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState -import jp.co.soramitsu.common.compose.component.NetworkIssueType import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.utils.diffed import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.common.utils.mapList -import jp.co.soramitsu.common.utils.requireException -import jp.co.soramitsu.common.utils.requireValue import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.core.models.IChain import jp.co.soramitsu.core.runtime.ChainConnection import jp.co.soramitsu.core.runtime.IChainRegistry -import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.coredb.dao.AssetReadOnlyCache import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.coredb.model.chain.ChainNodeLocal @@ -85,9 +80,9 @@ class ChainRegistry @Inject constructor( .distinctUntilChanged() .shareIn(scope, SharingStarted.Eagerly, replay = 1) - val chainsById = currentChains.map { chains -> chains.associateBy { it.id } } - .inBackground() - .shareIn(scope, SharingStarted.Eagerly, replay = 1) +// val chainsById = currentChains.map { chains -> chains.associateBy { it.id } } +// .inBackground() +// .shareIn(scope, SharingStarted.Eagerly, replay = 1) private val enabledAssetsFlow = assetsCache.observeAllEnabledAssets() .onStart { emit(emptyList()) } @@ -137,7 +132,7 @@ class ChainRegistry @Inject constructor( runCatching { setupChain(chain) }.onFailure { - networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) + networkStateService.notifyChainSyncProblem(chain.id) Log.e( "ChainRegistry", "error while sync in chain registry $it" @@ -186,7 +181,7 @@ class ChainRegistry @Inject constructor( if (runtimeProviderPool.getRuntimeProviderOrNull(chain.id)?.getOrNull() != null) return - if (connection.state.value is SocketStateMachine.State.Disconnected) { + if (connection.state.value !is SocketStateMachine.State.Connected) { connection.socketService.start(chain.nodes.first().url) } @@ -204,33 +199,6 @@ class ChainRegistry @Inject constructor( return connectionPool.getConnectionOrNull(chain.id) != null && runtime != null } - override fun getConnection(chainId: String) = connectionPool.getConnection(chainId) - - @Deprecated( - "Since we have ethereum chains, which don't have runtime, we must use the function with nullable return value", - ReplaceWith("getRuntimeOrNull(chainId)") - ) - override suspend fun getRuntime(chainId: ChainId): RuntimeSnapshot { - return awaitRuntimeProvider(chainId).get() - } - - suspend fun getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { - return getRuntimeProviderOrNull(chainId)?.getOrNull() - } - - fun getConnectionOrNull(chainId: String) = connectionPool.getConnectionOrNull(chainId) - - suspend fun awaitRuntimeProvider(chainId: String): RuntimeProvider { - return runtimeProviderPool.awaitRuntimeProvider(chainId) - } - - suspend fun getRuntimeProviderOrNull(chainId: String): RuntimeProvider? { - return runtimeProviderPool.getRuntimeProviderOrNull(chainId) - } - - fun getEthereumConnection(chainId: String) = ethereumConnectionPool.getOrNull(chainId) - suspend fun awaitEthereumConnection(chainId: String) = ethereumConnectionPool.await(chainId) - suspend fun getAsset(chainId: ChainId, chainAssetId: String): Asset? { return getChain(chainId).assetsById[chainAssetId] } @@ -285,56 +253,47 @@ class ChainRegistry @Inject constructor( suspend fun getRemoteRuntimeVersion(chainId: ChainId): Int? { return chainDao.runtimeInfo(chainId)?.remoteVersion } -} -suspend fun ChainRegistry.chainWithAsset( - chainId: ChainId, - assetId: String -): Pair { - val chain = getChain(chainId) + suspend fun chainWithAsset( + chainId: ChainId, + assetId: String + ): Pair { + val chain = getChain(chainId) - return chain to chain.assetsById.getValue(assetId) -} + return chain to chain.assetsById.getValue(assetId) + } -suspend fun ChainRegistry.getRuntime(chainId: ChainId): RuntimeSnapshot { - return awaitRuntimeProvider(chainId).get() -} + override fun getConnection(chainId: String) = connectionPool.getConnectionOrThrow(chainId) -suspend fun ChainRegistry.getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { - return getRuntimeProviderOrNull(chainId)?.getOrNull() -} + suspend fun awaitConnection(chainId: ChainId) = connectionPool.awaitConnection(chainId) + @Deprecated( + "Since we have ethereum chains, which don't have runtime, we must use the function with nullable return value", + ReplaceWith("getRuntimeOrNull(chainId)") + ) + override suspend fun getRuntime(chainId: ChainId): RuntimeSnapshot { + return awaitRuntimeProvider(chainId).get() + } -suspend fun ChainRegistry.getRuntimeCatching(chainId: ChainId): Result { - val providerResult = kotlin.runCatching { awaitRuntimeProvider(chainId) } + suspend fun getRuntimeOrNull(chainId: ChainId): RuntimeSnapshot? { + return getRuntimeProviderOrNull(chainId)?.getOrNull() + } - return if (providerResult.isFailure) { - Result.failure(providerResult.requireException()) - } else { - kotlin.runCatching { providerResult.requireValue().get() } + suspend fun awaitRuntimeProvider(chainId: String): RuntimeProvider { + return runtimeProviderPool.awaitRuntimeProvider(chainId) } -} -fun ChainRegistry.getSocket(chainId: ChainId) = getConnection(chainId).socketService -fun ChainRegistry.getSocketOrNull(chainId: ChainId) = - getConnectionOrNull(chainId)?.socketService + fun getRuntimeProviderOrNull(chainId: String): RuntimeProvider? { + return runtimeProviderPool.getRuntimeProviderOrNull(chainId) + } -suspend fun ChainRegistry.getService(chainId: ChainId): ChainService { - return ChainService( - runtimeProvider = awaitRuntimeProvider(chainId), - connection = getConnection(chainId) - ) -} + fun getEthereumConnectionOrNull(chainId: String) = ethereumConnectionPool.getOrNull(chainId) + suspend fun awaitEthereumConnection(chainId: String) = ethereumConnectionPool.await(chainId) -fun Chain.toSyncIssue(): NetworkIssueItemState { - return NetworkIssueItemState( - iconUrl = this.icon, - title = this.name, - type = when { - this.nodes.size > 1 -> NetworkIssueType.Node - else -> NetworkIssueType.Network - }, - chainId = this.id, - chainName = this.name, - assetId = this.utilityAsset?.id.orEmpty() - ) + suspend fun getService(chainId: ChainId): ChainService { + return ChainService( + runtimeProvider = awaitRuntimeProvider(chainId), + connection = getConnection(chainId) + ) + } } + diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt index cffaeeea4b..0d55a6669d 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt @@ -1,16 +1,12 @@ package jp.co.soramitsu.runtime.multiNetwork.connection -import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Provider import jp.co.soramitsu.common.BuildConfig -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState -import jp.co.soramitsu.common.compose.component.NetworkIssueType import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.core.models.ChainNode import jp.co.soramitsu.core.runtime.ChainConnection -import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.runtime.multiNetwork.ChainState import jp.co.soramitsu.runtime.multiNetwork.ChainsStateTracker import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain @@ -21,16 +17,12 @@ import jp.co.soramitsu.shared_utils.wsrpc.state.SocketStateMachine import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update private const val ConnectingStatusDebounce = 750L @@ -96,31 +88,21 @@ class ConnectionPool @Inject constructor( // .distinctUntilChanged() // .debounce(ConnectingStatusDebounce) - init { - observeChainsStatus() - } - - private fun observeChainsStatus() { - - } - - fun getConnection(chainId: ChainId): ChainConnection { - return poolFlow.value[chainId] - return pool.getValue(chainId) - } suspend fun awaitConnection(chainId: ChainId): ChainConnection { return poolFlow.map { it[chainId] }.filterNotNull().first() } fun getConnectionOrNull(chainId: ChainId): ChainConnection? = poolFlow.value.getOrDefault(chainId, null) + fun getConnectionOrThrow(chainId: ChainId): ChainConnection = poolFlow.value.getValue(chainId) fun setupConnection( chain: Chain, onSelectedNodeChange: (chainId: ChainId, newNodeUrl: String) -> Unit ): ChainConnection { var isNew = false - val connection = pool.getOrPut(chain.id) { + + val connection = poolFlow.value[chain.id] ?: run { isNew = true val nodes = chain.nodes.map { @@ -135,6 +117,9 @@ class ConnectionPool @Inject constructor( onSelectedNodeChange = { onSelectedNodeChange(chain.id, clearDwellirApiKey(it)) }, isAutoBalanceEnabled = { nodesSettingsStorage.getIsAutoSelectNodes(chain.id) } ).also { connection -> + poolFlow.update { pool -> + pool + (chain.id to connection) + } connection.state.onEach {connectionState -> val newState = when(connectionState) { is SocketStateMachine.State.Connected -> ChainState.ConnectionStatus.Connected(connectionState.url) @@ -143,8 +128,14 @@ class ConnectionPool @Inject constructor( is SocketStateMachine.State.Paused -> ChainState.ConnectionStatus.Paused(connectionState.url) is SocketStateMachine.State.WaitingForReconnect -> ChainState.ConnectionStatus.Connecting(connectionState.url) } + ChainsStateTracker.updateState(chain) { it.copy(connectionStatus = newState) } + when(connectionState) { + is SocketStateMachine.State.Connected -> networkStateService.notifyConnectionSuccess(chain.id) + is SocketStateMachine.State.WaitingForReconnect -> networkStateService.notifyConnectionProblem(chain.id) + else -> Unit + } }.launchIn(this) } } @@ -159,8 +150,14 @@ class ConnectionPool @Inject constructor( } fun removeConnection(chainId: ChainId) { - pool.remove(chainId)?.apply { finish() } + val connection = getConnectionOrNull(chainId) + poolFlow.update { + + it.minus(chainId) + } + connection?.finish() connectionWatcher.tryEmit(Event(Unit)) + networkStateService.notifyConnectionSuccess(chainId) } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt index a209dae795..6c5051282f 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt @@ -17,7 +17,6 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.goerliChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polygonChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.polygonTestnetChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.sepoliaChainId -import jp.co.soramitsu.runtime.multiNetwork.toSyncIssue import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -61,7 +60,7 @@ class EthereumConnectionPool( it[chain.id] = EthereumChainConnection( chain, onSelectedNodeChange = onSelectedNodeChange - ) { networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) } + ) { networkStateService.notifyChainSyncProblem(chain.id) } } } return poolStateFlow.value.getValue(chain.id) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt index 923b3e4005..217de2d1d1 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeProvider.kt @@ -8,7 +8,6 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainState import jp.co.soramitsu.runtime.multiNetwork.ChainsStateTracker import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.reefChainId -import jp.co.soramitsu.runtime.multiNetwork.toSyncIssue import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -144,7 +143,7 @@ class RuntimeProvider( networkStateService.notifyChainSyncSuccess(chainId) }.onFailure { error -> ChainsStateTracker.updateState(chainId) { it.copy(runtimeConstruction = ChainState.Status.Failed(error)) } - networkStateService.notifyChainSyncProblem(chain.toSyncIssue()) + networkStateService.notifyChainSyncProblem(chain.id) when (error) { ChainInfoNotInCacheException -> runtimeSyncService.cacheNotFound(chainId) else -> error.printStackTrace() diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt index e863f6fdcb..6c33285161 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt @@ -5,10 +5,12 @@ import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import kotlinx.coroutines.cancel import java.util.concurrent.ConcurrentHashMap +import jp.co.soramitsu.common.domain.NetworkStateService class RuntimeSubscriptionPool( private val chainDao: ChainDao, - private val runtimeSyncService: RuntimeSyncService + private val runtimeSyncService: RuntimeSyncService, + private val networkStateService: NetworkStateService, ) { private val pool = ConcurrentHashMap() @@ -17,7 +19,7 @@ class RuntimeSubscriptionPool( fun setupRuntimeSubscription(chain: Chain, connection: ChainConnection, runtimeProvider: RuntimeProvider): RuntimeVersionSubscription { return pool.getOrPut(chain.id) { - RuntimeVersionSubscription(chain.id, connection, chainDao, runtimeSyncService, runtimeProvider) + RuntimeVersionSubscription(chain.id, connection, chainDao, runtimeSyncService, networkStateService, runtimeProvider) } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt index c7852077ed..b2acd99be2 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt @@ -1,6 +1,7 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime import android.util.Log +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.data.network.runtime.binding.bindNumber import jp.co.soramitsu.common.data.network.runtime.binding.requireType import jp.co.soramitsu.common.utils.constant @@ -41,6 +42,7 @@ class RuntimeVersionSubscription( connection: ChainConnection, private val chainDao: ChainDao, private val runtimeSyncService: RuntimeSyncService, + private val networkStateService: NetworkStateService, runtimeProvider: RuntimeProvider, dispatcher: CoroutineDispatcher = Dispatchers.Default ) { @@ -82,6 +84,7 @@ class RuntimeVersionSubscription( ChainsStateTracker.updateState(chainId) { it.copy(runtimeVersion = ChainState.Status.Completed) } } .catch { error -> + networkStateService.notifyChainSyncProblem(chainId) ChainsStateTracker.updateState(chainId) { it.copy( runtimeVersion = ChainState.Status.Failed( diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/network/updaters/SingleChainUpdateSystem.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/network/updaters/SingleChainUpdateSystem.kt index 9998e8df97..a1934faa0a 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/network/updaters/SingleChainUpdateSystem.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/network/updaters/SingleChainUpdateSystem.kt @@ -6,7 +6,6 @@ import jp.co.soramitsu.core.updater.UpdateSystem import jp.co.soramitsu.core.updater.Updater import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain -import jp.co.soramitsu.runtime.multiNetwork.getSocket import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.storage.subscribeUsing import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -23,8 +22,8 @@ class SingleChainUpdateSystem( ) : UpdateSystem { override fun start(): Flow = chainFlow.flatMapLatest { chain -> - val socket = chainRegistry.getSocket(chain.id) - val runtimeMetadata = chainRegistry.getRuntime(chain.id).metadata + val socket = chainRegistry.awaitConnection(chain.id).socketService + val runtimeMetadata = chainRegistry.awaitRuntimeProvider(chain.id).get().metadata val scopeFlows = updaters.groupBy(Updater::scope).map { (scope, scopeUpdaters) -> scope.invalidationFlow().flatMapLatest { diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/repository/ChainStateRepository.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/repository/ChainStateRepository.kt index 63a9512d8e..72d1bd12ac 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/repository/ChainStateRepository.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/repository/ChainStateRepository.kt @@ -10,7 +10,6 @@ import jp.co.soramitsu.core.extrinsic.mortality.IChainStateRepository import jp.co.soramitsu.runtime.di.LOCAL_STORAGE_SOURCE import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId -import jp.co.soramitsu.runtime.multiNetwork.getRuntime import jp.co.soramitsu.runtime.storage.source.StorageDataSource import jp.co.soramitsu.runtime.storage.source.observeNonNull import jp.co.soramitsu.runtime.storage.source.queryNonNull @@ -28,7 +27,7 @@ class ChainStateRepository @Inject constructor( ) : IChainStateRepository { override suspend fun expectedBlockTimeInMillis(chainId: ChainId, defaultTime: BigInteger): BigInteger { - val runtime = chainRegistry.getRuntime(chainId) + val runtime = chainRegistry.awaitRuntimeProvider(chainId).get() return runCatching { runtime.metadata.babe().numberConstant("ExpectedBlockTime", runtime) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/state/SingleAssetSharedState.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/state/SingleAssetSharedState.kt index 0cd90b82b8..1d7f7eabce 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/state/SingleAssetSharedState.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/state/SingleAssetSharedState.kt @@ -6,7 +6,6 @@ import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId -import jp.co.soramitsu.runtime.multiNetwork.chainWithAsset import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/RemoteStorageSource.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/RemoteStorageSource.kt index 7e43bcb483..66a49f6cdb 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/RemoteStorageSource.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/storage/source/RemoteStorageSource.kt @@ -63,6 +63,6 @@ class RemoteStorageSource( return response?.result as? String? } - private fun getSocketService(chainId: String) = - chainRegistry.getConnection(chainId).socketService + private suspend fun getSocketService(chainId: String) = + chainRegistry.awaitConnection(chainId).socketService } From a510a3bd7bf0e996490bfb895b68a8a3857ddd68 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 6 May 2024 13:28:52 +0700 Subject: [PATCH 043/100] fix runtime version subscription, fix merge conflicts --- .../jp/co/soramitsu/common/utils/KoltinExt.kt | 2 +- .../java/jp/co/soramitsu/coredb/AppDatabase.kt | 2 ++ .../balance/list/BalanceListViewModel.kt | 5 ++--- .../runtime/RuntimeSubscriptionPool.kt | 4 ++-- .../runtime/RuntimeVersionSubscription.kt | 17 +++-------------- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt b/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt index 6d08ded12a..6cd4102a35 100644 --- a/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt +++ b/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt @@ -101,6 +101,6 @@ fun BigDecimal?.isNotZero(): Boolean = !isZero() fun BigDecimal.greaterThen(other: BigDecimal): Boolean = this.compareTo(other) == 1 fun BigInteger?.isZero(): Boolean = this?.compareTo(BigInteger.ZERO) == 0 -fun BigInteger?.greaterThanOrEquals(other: BigInteger): Boolean = this?.compareTo(other) == 1 || this?.compareTo(other) == 1 +fun BigInteger?.greaterThanOrEquals(other: BigInteger): Boolean = this?.compareTo(other) == 1 || this?.compareTo(other) == 0 fun BigInteger?.lessThan(other: BigInteger): Boolean = this?.compareTo(other) == -1 fun BigInteger?.isNotZero(): Boolean = !isZero() diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt index ef55f93c49..ecddfe043b 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt @@ -68,6 +68,7 @@ import jp.co.soramitsu.coredb.migrations.Migration_61_62 import jp.co.soramitsu.coredb.migrations.Migration_62_63 import jp.co.soramitsu.coredb.migrations.Migration_63_64 import jp.co.soramitsu.coredb.migrations.Migration_64_65 +import jp.co.soramitsu.coredb.migrations.Migration_65_66 import jp.co.soramitsu.coredb.migrations.RemoveAccountForeignKeyFromAsset_17_18 import jp.co.soramitsu.coredb.migrations.RemoveLegacyData_35_36 import jp.co.soramitsu.coredb.migrations.RemoveStakingRewardsTable_22_23 @@ -179,6 +180,7 @@ abstract class AppDatabase : RoomDatabase() { .addMigrations(Migration_62_63) .addMigrations(Migration_63_64) .addMigrations(Migration_64_65) + .addMigrations(Migration_65_66) .build() } return instance!! diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index ab9b9d7dd5..ef9adf8edf 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -374,12 +374,12 @@ class BalanceListViewModel @Inject constructor( assetStates, nftInteractor.nftFiltersFlow(), createNFTCollectionScreenViewsFlow(), - networkIssueStateFlow.onEach { networkIssueState -> Log.d("&&&", "network issues onEach: $networkIssueState")} + networkIssueStateFlow ) { selectedChainId, selectorState, assetStates, filters, (pageViews, screenLayout), networkIssueState -> when (selectorState.currentSelection) { AssetType.Currencies -> { val isSelectedChainHasIssues = networkIssueState != null - Log.d("&&&", "isSelectedChainHasIssues ${isSelectedChainHasIssues}") + if (isSelectedChainHasIssues) { requireNotNull(networkIssueState) } else { @@ -674,7 +674,6 @@ class BalanceListViewModel @Inject constructor( private fun sync() { viewModelScope.launch { withContext(Dispatchers.Default) { - Log.d("&&&", "sync assets rates") getAvailableFiatCurrencies.sync() interactor.syncAssetsRates().onFailure { withContext(Dispatchers.Main) { diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt index 6c33285161..0e7c98c494 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeSubscriptionPool.kt @@ -17,9 +17,9 @@ class RuntimeSubscriptionPool( fun getRuntimeSubscription(chainId: String) = pool.getValue(chainId) - fun setupRuntimeSubscription(chain: Chain, connection: ChainConnection, runtimeProvider: RuntimeProvider): RuntimeVersionSubscription { + fun setupRuntimeSubscription(chain: Chain, connection: ChainConnection): RuntimeVersionSubscription { return pool.getOrPut(chain.id) { - RuntimeVersionSubscription(chain.id, connection, chainDao, runtimeSyncService, networkStateService, runtimeProvider) + RuntimeVersionSubscription(chain.id, connection, chainDao, runtimeSyncService, networkStateService) } } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt index b2acd99be2..98a7e29e8b 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt @@ -43,7 +43,6 @@ class RuntimeVersionSubscription( private val chainDao: ChainDao, private val runtimeSyncService: RuntimeSyncService, private val networkStateService: NetworkStateService, - runtimeProvider: RuntimeProvider, dispatcher: CoroutineDispatcher = Dispatchers.Default ) { private val scope = CoroutineScope(dispatcher + SupervisorJob()) @@ -61,18 +60,15 @@ class RuntimeVersionSubscription( connection.socketService.subscriptionFlow(SubscribeStateRuntimeVersionRequest) .map { it.runtimeVersionChange().specVersion } .catch { - val runtime = runtimeProvider.getOrNullWithTimeout() - - val version = runtime?.getVersionConstant() - ?: connection.getVersionChainRpc() + Log.d("&&&", "catch SubscribeStateRuntimeVersionRequest") + val version = connection.getVersionChainRpc() ?: connection.getVersionStateRpc() ?: error("Runtime version not obtained") - + Log.d("&&&", "got version single $version") emit(version) } ) } - .onEach { runtimeVersionResult -> chainDao.updateRemoteRuntimeVersion( chainId, @@ -103,13 +99,6 @@ class RuntimeVersionSubscription( } } - private fun RuntimeSnapshot.getVersionConstant(): Int? = runCatching { - val versionConstant = metadata.system().constant("Version") - val decodedVersion = versionConstant.type?.fromByteArrayOrNull(this, versionConstant.value) - requireType(decodedVersion) - bindNumber(decodedVersion["specVersion"]).toInt() - }.getOrNull() - private suspend fun ChainConnection.getVersionChainRpc(): Int? = runCatching { socketService.executeAsync( request = RuntimeVersionRequest(), From 8578911234e79f30fb51594f3bc7946433fd6f1b Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 7 May 2024 16:02:28 +0700 Subject: [PATCH 044/100] wip --- .../app/root/di/RootFeatureModule.kt | 3 ++ .../app/root/domain/RootInteractor.kt | 21 ++++++++++++-- .../app/root/presentation/RootActivity.kt | 29 +++++++++++++++---- .../app/root/presentation/RootViewModel.kt | 20 ++++++++++--- .../common/compose/component/FeeInfo.kt | 1 - .../common/domain/NetworkStateService.kt | 7 ----- .../account/impl/domain/WalletSyncService.kt | 12 ++++++-- .../wallet/api/data/cache/AssetCache.kt | 23 +++++++-------- .../updaters/BalancesUpdateSystem.kt | 13 ++++----- .../impl/domain/WalletInteractorImpl.kt | 2 ++ .../balance/list/BalanceListViewModel.kt | 9 +++--- .../runtime/multiNetwork/ChainRegistry.kt | 1 - .../runtime/RuntimeVersionSubscription.kt | 17 ++++------- 13 files changed, 101 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt index ea22ee5b61..21ea5cd32b 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt @@ -6,6 +6,7 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Named import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario +import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.data.storage.Preferences @@ -22,6 +23,7 @@ class RootFeatureModule { @Named("BalancesUpdateSystem") walletUpdateSystem: UpdateSystem, pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, preferences: Preferences, + accountRepository: AccountRepository, walletSyncService: WalletSyncService ): RootInteractor { return RootInteractor( @@ -29,6 +31,7 @@ class RootFeatureModule { walletRepository, pendulumPreInstalledAccountsScenario, preferences, + accountRepository, walletSyncService ) } diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt index 0a6c832fae..ab3537dc17 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt @@ -1,10 +1,13 @@ package jp.co.soramitsu.app.root.domain +import android.util.Log import com.walletconnect.web3.wallet.client.Web3Wallet import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario +import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.data.storage.appConfig +import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.domain.model.AppConfig import jp.co.soramitsu.common.domain.model.toDomain import jp.co.soramitsu.common.utils.inBackground @@ -15,6 +18,9 @@ import jp.co.soramitsu.wallet.impl.data.buyToken.ExternalProvider import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.withContext @@ -23,10 +29,21 @@ class RootInteractor( private val walletRepository: WalletRepository, private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, private val preferences: Preferences, - private val walletSyncService: WalletSyncService + private val accountRepository: AccountRepository, + private val walletSyncService: WalletSyncService, ) { - fun runBalancesUpdate(): Flow = updateSystem.start().inBackground() + fun runWalletsSync() { + walletSyncService.start() + } + + suspend fun runBalancesUpdate(): Flow = withContext(Dispatchers.Default) { + Log.d("&&&", "run balances update, awaiting accounts") + // await all accounts initialized + val s = accountRepository.allMetaAccountsFlow().filter { accounts -> accounts.all { it.initialized } }.filter { it.isNotEmpty() }.first() + Log.d("&&&", "run balances update, initialized accounts: ${s.map { it.name }}") + return@withContext updateSystem.start().inBackground() + } fun isBuyProviderRedirectLink(link: String) = ExternalProvider.REDIRECT_URL_BASE in link diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt index 5af323a6c9..438ddbe4a4 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt @@ -83,6 +83,23 @@ class RootActivity : BaseActivity(), LifecycleObserver { super.onAvailable(network) viewModel.onNetworkAvailable() } + + override fun onLosing(network: Network, maxMsToLive: Int) { + super.onLosing(network, maxMsToLive) + viewModel.onConnectionLost() + } + +// override fun onLost(network: Network) { +// super.onLost(network) +// viewModel.onConnectionLost() +// } + +// override fun onUnavailable() { +// super.onUnavailable() +// viewModel.onConnectionLost() +// } + + } val networkRequest = NetworkRequest.Builder() @@ -133,12 +150,12 @@ class RootActivity : BaseActivity(), LifecycleObserver { } override fun subscribe(viewModel: RootViewModel) { -// viewModel.showConnectingBarFlow.observe(lifecycleScope) { show -> -// when { -// show -> showBadConnectionView() -// else -> hideBadConnectionView() -// } -// } + viewModel.showConnectingBar.observe(lifecycleScope) { show -> + when { + show -> showBadConnectionView() + else -> hideBadConnectionView() + } + } viewModel.messageLiveData.observe( this, diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index deeaee36a4..ab599dc140 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -24,9 +24,11 @@ import kotlin.time.toDuration import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @HiltViewModel @@ -84,10 +86,12 @@ class RootViewModel @Inject constructor( shouldHandleResumeInternetConnection = false interactor.chainRegistrySyncUp() } - interactor.runBalancesUpdate() - .onEach { handleUpdatesSideEffect(it) } - .launchIn(this) - + viewModelScope.launch { + interactor.runWalletsSync() + interactor.runBalancesUpdate() + .onEach { handleUpdatesSideEffect(it) } + .launchIn(this) + } updatePhishingAddresses() } @@ -171,13 +175,21 @@ class RootViewModel @Inject constructor( checkAppVersion() } + + private val _showConnectingBar = MutableStateFlow(false) + val showConnectingBar: StateFlow = _showConnectingBar fun onNetworkAvailable() { + _showConnectingBar.update { false } // todo this code triggers redundant requests and balance updates. Needs research // viewModelScope.launch { // checkAppVersion() // } } + fun onConnectionLost(){ + _showConnectingBar.update { true } + } + private fun observeWalletConnectEvents() { WCDelegate.walletEvents.onEach { when (it) { diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt index fca7668247..2908bc6f15 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/FeeInfo.kt @@ -1,6 +1,5 @@ package jp.co.soramitsu.common.compose.component -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt b/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt index 63a5144cb5..f1adf4b721 100644 --- a/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt +++ b/common/src/main/java/jp/co/soramitsu/common/domain/NetworkStateService.kt @@ -12,9 +12,6 @@ class NetworkStateService { private val connectionPoolProblems = MutableStateFlow>(emptySet()) private val chainsSyncProblems = MutableStateFlow>(emptySet()) -// private val _showConnectingBarFlow = MutableStateFlow(false) -// val showConnectingBarFlow = _showConnectingBarFlow - val networkIssuesFlow: Flow> = combine( connectionPoolProblems, chainsSyncProblems @@ -24,10 +21,6 @@ class NetworkStateService { (nodesIssues + runtimeIssues).toMap() } -// fun updateShowConnecting(isShow: Boolean) { -// _showConnectingBarFlow.value = isShow -// } - fun notifyConnectionProblem(chainId: ChainId) { connectionPoolProblems.update { it + chainId } } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index 6ddebf0970..39195aff64 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -53,16 +53,17 @@ class WalletSyncService( private val scope = CoroutineScope(dispatcher + SupervisorJob() + CoroutineExceptionHandler { _, throwable -> Log.d( - "&&&", + "WalletSyncService", "WalletSyncService scope error: $throwable" ) }) private var syncJob: Job? = null - init { + fun start(){ metaAccountDao.observeNotInitializedMetaAccounts().filter { it.isNotEmpty() } .onEach { localMetaAccounts -> + Log.d("&&&", "syncing accounts: ${localMetaAccounts.map { it.metaAccount.name }}" ) syncJob?.cancel() syncJob = scope.launch { chainRegistry.configsSyncDeferred.join() @@ -131,6 +132,8 @@ class WalletSyncService( assetDao.insertAssets(localAssets) } } + }.invokeOnCompletion { + Log.d("&&&", "completed sync ethereum chains" ) } launch { substrateChains.onEach { chain -> @@ -228,12 +231,17 @@ class WalletSyncService( assetDao.insertAssets(localAssets) } } + }.invokeOnCompletion { + Log.d("&&&", "completed sync substrate chains" ) } + this }.coroutineContext.job.join() + Log.d("&&&", "mark accounts initialized: ${metaAccounts.map { it.name }} " ) metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) } }.launchIn(scope) + } private suspend fun buildEquilibriumAssets( diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt index 267ff5c1a6..e6d32bc095 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt @@ -1,9 +1,11 @@ package jp.co.soramitsu.wallet.api.data.cache +import android.util.Log import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi +import jp.co.soramitsu.common.utils.isZero import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.coredb.dao.AssetDao import jp.co.soramitsu.coredb.dao.AssetReadOnlyCache @@ -51,24 +53,21 @@ class AssetCache( val cachedAsset = assetDao.getAsset(metaId, accountId, chainId, assetId)?.asset when { - cachedAsset == null -> assetDao.insertAsset( - builder.invoke( - AssetLocal.createEmpty( - accountId, - assetId, - chainId, - metaId, - priceId - ) - ) - ) + cachedAsset == null -> { +// Log.d("&&&", "cached asset is null: ${chainAsset.name}") + val emptyAsset = AssetLocal.createEmpty(accountId, assetId, chainId, metaId, priceId) + val newAsset = builder.invoke(emptyAsset) + assetDao.insertAsset(newAsset.copy(enabled = newAsset.freeInPlanks == null || newAsset.freeInPlanks.isZero())) + } cachedAsset.accountId.contentEquals(emptyAccountIdValue) -> { +// Log.d("&&&", "cached asset has no account: ${chainAsset.name}") assetDao.deleteAsset(metaId, emptyAccountIdValue, chainId, assetId) - assetDao.insertAsset(builder.invoke(cachedAsset.copy(accountId = accountId, tokenPriceId = priceId))) + assetDao.insertAsset(builder.invoke(cachedAsset.copy(accountId = accountId, tokenPriceId = priceId, enabled = cachedAsset.freeInPlanks == null || cachedAsset.freeInPlanks.isZero()))) } else -> { +// Log.d("&&&", "cached asset is OK, updating balances: ${chainAsset.name}") val updatedAsset = builder.invoke(cachedAsset.copy(tokenPriceId = priceId)) if (cachedAsset.bondedInPlanks == updatedAsset.bondedInPlanks && cachedAsset.feeFrozenInPlanks == updatedAsset.feeFrozenInPlanks && diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt index ee1de3e678..9a9e9078c2 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt @@ -2,7 +2,6 @@ package jp.co.soramitsu.wallet.impl.data.network.blockchain.updaters import android.annotation.SuppressLint import android.util.Log -import it.airgap.beaconsdk.core.internal.utils.success import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.account.api.domain.model.address @@ -46,7 +45,6 @@ import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -170,13 +168,13 @@ class BalancesUpdateSystem( val eqHexRaw = storageKeyToHexRaw.second val balanceData = bindEquilibriumAccountData(eqHexRaw, runtime) val balances = balanceData?.data?.balances.orEmpty() - chain.assets.forEach { asset -> + chain.assets.forEach asset@{ asset -> val balance = balances.getOrDefault( asset.currencyId?.toBigInteger().orZero(), null ).orZero() val metadata = storageKeys.firstOrNull { it.key == storageKeyToHexRaw.first } - ?: return@forEach + ?: return@asset assetCache.updateAsset( metadata.metaAccountId, metadata.accountId, @@ -223,7 +221,7 @@ class BalancesUpdateSystem( accounts.forEach { account -> val address = account.address(chain) ?: return@forEach val accountId = account.accountId(chain) ?: return@forEach - chain.assets.forEach { asset -> + chain.assets.forEach asset@{ asset -> val balance = kotlin.runCatching { ethereumRemoteSource.fetchEthBalance(asset, address) } .onFailure { @@ -232,7 +230,7 @@ class BalancesUpdateSystem( "fetchEthBalance error ${it.message} ${it.localizedMessage} $it" ) } - .getOrNull() ?: return@forEach + .getOrNull() ?: return@asset val balanceData = SimpleBalanceData(balance) assetCache.updateAsset( metaId = account.id, @@ -307,7 +305,8 @@ class BalancesUpdateSystem( } override fun start(): Flow { - return emptyFlow()//subscribeFlow() + Log.d("&&&", "balance update system has started") + return subscribeFlow() } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index 780e3c2a5f..f3490e514f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -41,6 +41,7 @@ import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.shared_utils.runtime.extrinsic.ExtrinsicBuilder import jp.co.soramitsu.shared_utils.runtime.metadata.moduleOrNull import jp.co.soramitsu.shared_utils.ss58.SS58Encoder.toAddress +import jp.co.soramitsu.wallet.impl.data.network.blockchain.updaters.BalanceUpdateTrigger import jp.co.soramitsu.wallet.impl.data.repository.HistoryRepository import jp.co.soramitsu.wallet.impl.domain.interfaces.AddressBookRepository import jp.co.soramitsu.wallet.impl.domain.interfaces.AssetSorting @@ -671,6 +672,7 @@ class WalletInteractorImpl( val runtime = withTimeoutOrNull(15_000L) { chainRegistry.awaitRuntimeProvider(chainId).get() } + BalanceUpdateTrigger.invoke(chainId) if(runtime == null) { return@withContext Result.failure(Exception("Failed to sync chain")) } else { diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index ef9adf8edf..56e2622550 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -250,11 +250,14 @@ class BalanceListViewModel @Inject constructor( it.asset.token.configuration.chainId == selectedChainId || it.asset.token.configuration.chainId in filteredChains.map { it.id }) } - .filter { it.asset.freeInPlanks.greaterThanOrEquals(BigInteger.ZERO) } .toList() + currentAssetsFlow.update { filteredAssets } + + val filteredAssetsWithoutBrokenAssets = filteredAssets.filter { it.asset.freeInPlanks.greaterThanOrEquals(BigInteger.ZERO) } + val balanceListItems = AssetListHelper.processAssets( - assets = filteredAssets, + assets = filteredAssetsWithoutBrokenAssets, filteredChains = filteredChains, selectedChainId = selectedChainId, networkIssues = emptySet() @@ -379,7 +382,6 @@ class BalanceListViewModel @Inject constructor( when (selectorState.currentSelection) { AssetType.Currencies -> { val isSelectedChainHasIssues = networkIssueState != null - if (isSelectedChainHasIssues) { requireNotNull(networkIssueState) } else { @@ -430,7 +432,6 @@ class BalanceListViewModel @Inject constructor( val isAllAssetsWithProblems = currentAssets.isNotEmpty() && currentAssets.filter { it.asset.token.configuration.chainId == selectedChainId } .all { it.asset.freeInPlanks == null || it.asset.freeInPlanks.lessThan(BigInteger.ZERO) } - hashCode() if (isAllAssetsWithProblems.not()) return@combine null val selectedChainIssue = networkIssues[selectedChainId] ?: NetworkIssueType.Network diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index b714a82b90..2a31402f9b 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -6,7 +6,6 @@ import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.utils.diffed -import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.core.models.IChain diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt index 98a7e29e8b..d91224018f 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/runtime/RuntimeVersionSubscription.kt @@ -2,17 +2,10 @@ package jp.co.soramitsu.runtime.multiNetwork.runtime import android.util.Log import jp.co.soramitsu.common.domain.NetworkStateService -import jp.co.soramitsu.common.data.network.runtime.binding.bindNumber -import jp.co.soramitsu.common.data.network.runtime.binding.requireType -import jp.co.soramitsu.common.utils.constant -import jp.co.soramitsu.common.utils.system import jp.co.soramitsu.core.runtime.ChainConnection import jp.co.soramitsu.coredb.dao.ChainDao import jp.co.soramitsu.runtime.multiNetwork.ChainState import jp.co.soramitsu.runtime.multiNetwork.ChainsStateTracker -import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot -import jp.co.soramitsu.shared_utils.runtime.definitions.types.composite.Struct -import jp.co.soramitsu.shared_utils.runtime.definitions.types.fromByteArrayOrNull import jp.co.soramitsu.shared_utils.wsrpc.executeAsync import jp.co.soramitsu.shared_utils.wsrpc.mappers.nonNull import jp.co.soramitsu.shared_utils.wsrpc.mappers.pojo @@ -22,7 +15,6 @@ import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.StateRuntimeVers import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.SubscribeRuntimeVersionRequest import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.SubscribeStateRuntimeVersionRequest import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.chain.runtimeVersionChange -import jp.co.soramitsu.shared_utils.wsrpc.state.SocketStateMachine import jp.co.soramitsu.shared_utils.wsrpc.subscriptionFlow import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -46,6 +38,7 @@ class RuntimeVersionSubscription( dispatcher: CoroutineDispatcher = Dispatchers.Default ) { private val scope = CoroutineScope(dispatcher + SupervisorJob()) + init { runCatching { ChainsStateTracker.updateState(chainId) { it.copy(runtimeVersion = ChainState.Status.Started) } @@ -57,14 +50,16 @@ class RuntimeVersionSubscription( .map { it.runtimeVersionChange().specVersion } .catch { emitAll( - connection.socketService.subscriptionFlow(SubscribeStateRuntimeVersionRequest) + connection.socketService.subscriptionFlow( + SubscribeStateRuntimeVersionRequest + ) .map { it.runtimeVersionChange().specVersion } .catch { - Log.d("&&&", "catch SubscribeStateRuntimeVersionRequest") + val version = connection.getVersionChainRpc() ?: connection.getVersionStateRpc() ?: error("Runtime version not obtained") - Log.d("&&&", "got version single $version") + emit(version) } ) From 12fa51f220d10cebaa5b1501b2104c251447f0a1 Mon Sep 17 00:00:00 2001 From: Deneath Date: Wed, 8 May 2024 14:40:31 +0700 Subject: [PATCH 045/100] asset management fixes --- .../app/root/presentation/RootActivity.kt | 16 ++-------------- .../jp/co/soramitsu/common/utils/KoltinExt.kt | 5 +++++ .../jp/co/soramitsu/coredb/model/AssetLocal.kt | 5 +++-- .../impl/domain/TotalBalanceUseCaseImpl.kt | 3 ++- .../soramitsu/wallet/impl/domain/model/Asset.kt | 14 +++++++++++--- .../impl/data/repository/WalletRepositoryImpl.kt | 3 ++- .../impl/domain/ValidateTransferUseCaseImpl.kt | 7 ++++--- 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt index 438ddbe4a4..827d31f5e0 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt @@ -84,22 +84,10 @@ class RootActivity : BaseActivity(), LifecycleObserver { viewModel.onNetworkAvailable() } - override fun onLosing(network: Network, maxMsToLive: Int) { - super.onLosing(network, maxMsToLive) + override fun onLost(network: Network) { + super.onLost(network) viewModel.onConnectionLost() } - -// override fun onLost(network: Network) { -// super.onLost(network) -// viewModel.onConnectionLost() -// } - -// override fun onUnavailable() { -// super.onUnavailable() -// viewModel.onConnectionLost() -// } - - } val networkRequest = NetworkRequest.Builder() diff --git a/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt b/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt index 6cd4102a35..cdedeba64d 100644 --- a/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt +++ b/common/src/main/java/jp/co/soramitsu/common/utils/KoltinExt.kt @@ -99,8 +99,13 @@ fun BigDecimal?.isZero(): Boolean = this?.compareTo(BigDecimal.ZERO) == 0 fun BigDecimal?.isNotZero(): Boolean = !isZero() fun BigDecimal.greaterThen(other: BigDecimal): Boolean = this.compareTo(other) == 1 +fun BigInteger.greaterThen(other: BigInteger): Boolean = this.compareTo(other) == 1 fun BigInteger?.isZero(): Boolean = this?.compareTo(BigInteger.ZERO) == 0 fun BigInteger?.greaterThanOrEquals(other: BigInteger): Boolean = this?.compareTo(other) == 1 || this?.compareTo(other) == 0 fun BigInteger?.lessThan(other: BigInteger): Boolean = this?.compareTo(other) == -1 fun BigInteger?.isNotZero(): Boolean = !isZero() + +fun BigInteger?.positiveOrNull(): BigInteger? { + return this?.takeIf { it.greaterThen(BigInteger.ZERO) } +} diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt index ff65b42829..c265e9a624 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/model/AssetLocal.kt @@ -7,6 +7,7 @@ import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.coredb.model.chain.ChainLocal import jp.co.soramitsu.shared_utils.runtime.AccountId import java.math.BigInteger +import jp.co.soramitsu.common.utils.positiveOrNull /*** This table is used for storing assets in database. * freeInPlanks - has three states: @@ -64,13 +65,13 @@ data class AssetLocal( } val totalInPlanks: BigInteger - get() = freeInPlanks.orZero() + reservedInPlanks.orZero() + get() = freeInPlanks.positiveOrNull().orZero() + reservedInPlanks.orZero() private val locked: BigInteger get() = maxOf(miscFrozenInPlanks.orZero(), feeFrozenInPlanks.orZero()) val transferableInPlanks: BigInteger - get() = maxOf(freeInPlanks.orZero() - locked, BigInteger.ZERO) + get() = maxOf(freeInPlanks.positiveOrNull().orZero() - locked, BigInteger.ZERO) } data class AssetUpdateItem( diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt index 401eed8739..d8096d57a4 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt @@ -12,6 +12,7 @@ import jp.co.soramitsu.common.utils.isNotZero import jp.co.soramitsu.common.utils.isZero import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.common.utils.percentageToFraction +import jp.co.soramitsu.common.utils.positiveOrNull import jp.co.soramitsu.coredb.dao.AssetDao import jp.co.soramitsu.coredb.model.AssetWithToken import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository @@ -74,7 +75,7 @@ class TotalBalanceUseCaseImpl( ?: return@fold TotalBalance.Empty val total = - current.asset.freeInPlanks.orZero() + current.asset.reservedInPlanks.orZero() + current.asset.freeInPlanks.positiveOrNull().orZero() + current.asset.reservedInPlanks.orZero() val totalDecimal = total.toBigDecimal(scale = chainAsset.precision) val fiatAmount = totalDecimal.applyFiatRate(current.token?.fiatRate) diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/model/Asset.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/model/Asset.kt index a0ebbc1dab..8c10fd5e31 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/model/Asset.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/model/Asset.kt @@ -6,7 +6,9 @@ import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.common.model.AssetKey import jp.co.soramitsu.common.utils.applyFiatRate import jp.co.soramitsu.common.utils.formatFiat +import jp.co.soramitsu.common.utils.lessThan import jp.co.soramitsu.common.utils.orZero +import jp.co.soramitsu.common.utils.positiveOrNull import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.core.models.Asset as CoreAsset @@ -70,7 +72,7 @@ data class Asset( ) } - private val free = token.amountFromPlanks(freeInPlanks.orZero()) + private val free = token.amountFromPlanks(freeInPlanks.positiveOrNull().orZero()) val reserved = token.amountFromPlanks(reservedInPlanks.orZero()) private val miscFrozen = token.amountFromPlanks(miscFrozenInPlanks.orZero()) private val feeFrozen = token.amountFromPlanks(feeFrozenInPlanks.orZero()) @@ -82,7 +84,7 @@ data class Asset( val availableForStaking: BigDecimal = maxOf(free - frozen, BigDecimal.ZERO) val transferable = free - locked - val transferableInPlanks = freeInPlanks?.let { it - miscFrozenInPlanks.orZero().max(feeFrozenInPlanks.orZero()) }.orZero() + val transferableInPlanks = freeInPlanks.positiveOrNull()?.let { it - miscFrozenInPlanks.orZero().max(feeFrozenInPlanks.orZero()) }.orZero() val isAssetFrozen = status == STATUS_FROZEN val sendAvailable: BigDecimal = if (isAssetFrozen) BigDecimal.ZERO else transferable @@ -103,4 +105,10 @@ data class Asset( fun calculateTotalBalance( freeInPlanks: BigInteger?, reservedInPlanks: BigInteger? -) = freeInPlanks?.let { freeInPlanks + reservedInPlanks.orZero() } +): BigInteger? { + return if(freeInPlanks != null && freeInPlanks.lessThan(BigInteger.ZERO)) { + BigInteger.ZERO + } else { + freeInPlanks?.let { freeInPlanks + reservedInPlanks.orZero() } + } +} diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index e9d675df28..4d94286d27 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -565,7 +565,8 @@ class WalletRepositoryImpl( val chains = it.keys.map { mapChainLocalToChain(it) } val chainsById = chains.associateBy { it.id } val assets = it.values.map { mapAssetLocalToAsset(chainsById, it) } - chains.zip(assets).toMap() + val chainToAssetMap = chains.zip(assets).toMap() + chainToAssetMap.filter { pair -> pair.value != null && pair.value?.enabled == true } } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/ValidateTransferUseCaseImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/ValidateTransferUseCaseImpl.kt index ebf92ddee1..66aeee23d9 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/ValidateTransferUseCaseImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/ValidateTransferUseCaseImpl.kt @@ -7,6 +7,7 @@ import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.common.utils.formatCryptoDetail import jp.co.soramitsu.common.utils.isZero import jp.co.soramitsu.common.utils.orZero +import jp.co.soramitsu.common.utils.positiveOrNull import jp.co.soramitsu.common.utils.sumByBigDecimal import jp.co.soramitsu.core.models.ChainAssetType import jp.co.soramitsu.core.models.ChainId @@ -184,7 +185,7 @@ class ValidateTransferUseCaseImpl( val utilityAssetBalance = utilityAsset?.transferableInPlanks.orZero() val destinationChainUtilityAsset = destinationChain.utilityAsset val totalDestinationUtilityAssetBalanceInPlanks = kotlin.runCatching { destinationChainUtilityAsset?.let { walletRepository.getTotalBalance(it, destinationChain, destinationAccountId) } }.getOrNull().orZero() - val resultedBalance = (originAsset.freeInPlanks ?: originTransferable) - (amountInPlanks + originFee + tip) + val resultedBalance = (originAsset.freeInPlanks.positiveOrNull() ?: originTransferable) - (amountInPlanks + originFee + tip) mapOf( TransferValidationResult.InsufficientBalance to (amountInPlanks + originFee + tip > originAvailable), @@ -195,7 +196,7 @@ class ValidateTransferUseCaseImpl( } originAssetConfig.isUtility -> { - val resultedBalance = (originAsset.freeInPlanks ?: originTransferable) - (amountInPlanks + originFee + tip) + val resultedBalance = (originAsset.freeInPlanks.positiveOrNull() ?: originTransferable) - (amountInPlanks + originFee + tip) mapOf( TransferValidationResult.InsufficientBalance to (amountInPlanks + originFee + tip > originAvailable), @@ -343,7 +344,7 @@ class ValidateTransferUseCaseImpl( } originAssetConfig.isUtility -> { - val resultedBalance = (originAsset.freeInPlanks ?: transferable) - (amountInPlanks + originFee + tip) + val resultedBalance = (originAsset.freeInPlanks.positiveOrNull() ?: transferable) - (amountInPlanks + originFee + tip) val assetEdFormatted = originExistentialDeposit.formatCryptoDetailFromPlanks(originAsset.token.configuration) mapOf( getTransferValidationResultExistentialDeposit(isCrossChainTransfer, assetEdFormatted) to (resultedBalance < originExistentialDeposit), From 549cc43c3bb7dc3a78f8502ed8d9384b9693faaa Mon Sep 17 00:00:00 2001 From: Deneath Date: Wed, 8 May 2024 15:11:29 +0700 Subject: [PATCH 046/100] self review fixes --- .../app/root/domain/RootInteractor.kt | 7 +-- .../app/root/presentation/RootViewModel.kt | 3 +- .../account/impl/domain/WalletSyncService.kt | 6 --- .../confirm/ConfirmMnemonicViewModel.kt | 25 --------- .../wallet/api/data/cache/AssetCache.kt | 4 -- .../updaters/BalancesUpdateSystem.kt | 1 - .../data/repository/WalletRepositoryImpl.kt | 29 ++++++---- .../wallet/impl/di/WalletFeatureModule.kt | 6 --- .../balance/list/BalanceListViewModel.kt | 3 -- .../networkissues/NetworkIssuesViewModel.kt | 1 + runtime/build.gradle | 2 +- .../runtime/multiNetwork/ChainRegistry.kt | 4 -- .../multiNetwork/connection/ConnectionPool.kt | 54 ------------------- 13 files changed, 22 insertions(+), 123 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt index ab3537dc17..50eb740537 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt @@ -1,13 +1,11 @@ package jp.co.soramitsu.app.root.domain -import android.util.Log import com.walletconnect.web3.wallet.client.Web3Wallet import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.data.storage.appConfig -import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.domain.model.AppConfig import jp.co.soramitsu.common.domain.model.toDomain import jp.co.soramitsu.common.utils.inBackground @@ -20,7 +18,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.withContext @@ -30,7 +27,7 @@ class RootInteractor( private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, private val preferences: Preferences, private val accountRepository: AccountRepository, - private val walletSyncService: WalletSyncService, + private val walletSyncService: WalletSyncService ) { fun runWalletsSync() { @@ -38,10 +35,8 @@ class RootInteractor( } suspend fun runBalancesUpdate(): Flow = withContext(Dispatchers.Default) { - Log.d("&&&", "run balances update, awaiting accounts") // await all accounts initialized val s = accountRepository.allMetaAccountsFlow().filter { accounts -> accounts.all { it.initialized } }.filter { it.isNotEmpty() }.first() - Log.d("&&&", "run balances update, initialized accounts: ${s.map { it.name }}") return@withContext updateSystem.start().inBackground() } diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index ab599dc140..7c767fa65d 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -12,7 +12,6 @@ import javax.inject.Inject import jp.co.soramitsu.app.R import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.core.runtime.ChainConnection @@ -186,7 +185,7 @@ class RootViewModel @Inject constructor( // } } - fun onConnectionLost(){ + fun onConnectionLost() { _showConnectingBar.update { true } } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index 39195aff64..5877204f48 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -63,7 +63,6 @@ class WalletSyncService( fun start(){ metaAccountDao.observeNotInitializedMetaAccounts().filter { it.isNotEmpty() } .onEach { localMetaAccounts -> - Log.d("&&&", "syncing accounts: ${localMetaAccounts.map { it.metaAccount.name }}" ) syncJob?.cancel() syncJob = scope.launch { chainRegistry.configsSyncDeferred.join() @@ -132,8 +131,6 @@ class WalletSyncService( assetDao.insertAssets(localAssets) } } - }.invokeOnCompletion { - Log.d("&&&", "completed sync ethereum chains" ) } launch { substrateChains.onEach { chain -> @@ -231,13 +228,10 @@ class WalletSyncService( assetDao.insertAssets(localAssets) } } - }.invokeOnCompletion { - Log.d("&&&", "completed sync substrate chains" ) } this }.coroutineContext.job.join() - Log.d("&&&", "mark accounts initialized: ${metaAccounts.map { it.name }} " ) metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) } }.launchIn(scope) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt index 1ebc88d50d..52df894912 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/confirm/ConfirmMnemonicViewModel.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.launch class ConfirmMnemonicViewModel @Inject constructor( private val resourceManager: ResourceManager, private val interactor: AccountInteractor, - private val walletInteractor: WalletInteractor, private val router: AccountRouter, private val deviceVibrator: DeviceVibrator, private val savedStateHandle: SavedStateHandle @@ -139,8 +138,6 @@ class ConfirmMnemonicViewModel @Inject constructor( val result = interactor.createAccount(accountName, mnemonicString, cryptoType, substrateDerivationPath, ethereumDerivationPath, isBackedUp) if (result.isSuccess) { -// setupNewAccountAssetsVisibility() - continueBasedOnCodeStatus() } else { showError(result.requireException()) @@ -149,28 +146,6 @@ class ConfirmMnemonicViewModel @Inject constructor( } } - private suspend fun setupNewAccountAssetsVisibility() { - walletInteractor.saveChainSelectFilter( - walletInteractor.getSelectedMetaAccount().id, - ChainSelectorViewStateWithFilters.Filter.Popular.toString() - ) - - walletInteractor.getChains().filter { it.isNotEmpty() }.firstOrNull()?.let { chains -> - val defaultAssetStatesForNewAccount = chains.map { chain -> - val isPopular = chain.rank != null - chain.assets.map { - AssetBooleanState( - chainId = it.chainId, - assetId = it.id, - value = it.isUtility && isPopular - ) - } - }.flatten() - - walletInteractor.updateAssetsHiddenState(defaultAssetStatesForNewAccount) - } - } - private fun createChainAccount(extras: ConfirmMnemonicPayload.CreateChainExtras) { viewModelScope.launch { val mnemonicString = originMnemonic.joinToString(" ") diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt index e6d32bc095..a40a01db96 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/data/cache/AssetCache.kt @@ -1,6 +1,5 @@ package jp.co.soramitsu.wallet.api.data.cache -import android.util.Log import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.mixin.api.UpdatesMixin @@ -54,20 +53,17 @@ class AssetCache( val cachedAsset = assetDao.getAsset(metaId, accountId, chainId, assetId)?.asset when { cachedAsset == null -> { -// Log.d("&&&", "cached asset is null: ${chainAsset.name}") val emptyAsset = AssetLocal.createEmpty(accountId, assetId, chainId, metaId, priceId) val newAsset = builder.invoke(emptyAsset) assetDao.insertAsset(newAsset.copy(enabled = newAsset.freeInPlanks == null || newAsset.freeInPlanks.isZero())) } cachedAsset.accountId.contentEquals(emptyAccountIdValue) -> { -// Log.d("&&&", "cached asset has no account: ${chainAsset.name}") assetDao.deleteAsset(metaId, emptyAccountIdValue, chainId, assetId) assetDao.insertAsset(builder.invoke(cachedAsset.copy(accountId = accountId, tokenPriceId = priceId, enabled = cachedAsset.freeInPlanks == null || cachedAsset.freeInPlanks.isZero()))) } else -> { -// Log.d("&&&", "cached asset is OK, updating balances: ${chainAsset.name}") val updatedAsset = builder.invoke(cachedAsset.copy(tokenPriceId = priceId)) if (cachedAsset.bondedInPlanks == updatedAsset.bondedInPlanks && cachedAsset.feeFrozenInPlanks == updatedAsset.feeFrozenInPlanks && diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt index 9a9e9078c2..2fbb431cd5 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/network/blockchain/updaters/BalancesUpdateSystem.kt @@ -305,7 +305,6 @@ class BalancesUpdateSystem( } override fun start(): Flow { - Log.d("&&&", "balance update system has started") return subscribeFlow() } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index 4d94286d27..26715f4794 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -6,8 +6,6 @@ import java.math.BigInteger import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.domain.model.accountId -import jp.co.soramitsu.common.compose.component.NetworkIssueItemState -import jp.co.soramitsu.common.compose.component.NetworkIssueType import jp.co.soramitsu.common.data.network.HttpExceptionHandler import jp.co.soramitsu.common.data.network.coingecko.CoingeckoApi import jp.co.soramitsu.common.data.network.config.AppConfigRemote @@ -69,7 +67,6 @@ import jp.co.soramitsu.wallet.impl.data.network.phishing.PhishingApi import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletConstants import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository import jp.co.soramitsu.wallet.impl.domain.model.Asset -import jp.co.soramitsu.wallet.impl.domain.model.Asset.Companion.createEmpty import jp.co.soramitsu.wallet.impl.domain.model.AssetWithStatus import jp.co.soramitsu.wallet.impl.domain.model.Fee import jp.co.soramitsu.wallet.impl.domain.model.Transfer @@ -104,11 +101,8 @@ class WalletRepositoryImpl( private val availableFiatCurrencies: GetAvailableFiatCurrencies, private val updatesMixin: UpdatesMixin, private val remoteConfigFetcher: RemoteConfigFetcher, - private val preferences: Preferences, private val accountRepository: AccountRepository, private val chainsRepository: ChainsRepository, - private val selectedFiat: SelectedFiat, - private val tokenPriceDao: TokenPriceDao, private val extrinsicService: ExtrinsicService, private val remoteStorageSource: StorageDataSource ) : WalletRepository, UpdatesProviderUi by updatesMixin { @@ -123,7 +117,7 @@ class WalletRepositoryImpl( return combine( chainsRepository.chainsByIdFlow(), assetCache.observeAssets(meta.id) - ){ chainsById, assetsLocal -> + ) { chainsById, assetsLocal -> val chainAccounts = meta.chainAccounts.values.toList() val updatedAssets = assetsLocal.mapNotNull { asset -> mapAssetLocalToAsset(chainsById, asset)?.let { @@ -169,7 +163,7 @@ class WalletRepositoryImpl( syncAllRates(chains, currencyId) } - private suspend fun syncAllRates(chains: List, currencyId: String) { + private suspend fun syncAllRates(chains: List, currencyId: String) { val priceIdsWithChainlinkId = chains.map { it.assets.mapNotNull { asset -> asset.priceId?.let { priceId -> @@ -189,7 +183,7 @@ class WalletRepositoryImpl( updatesMixin.startUpdateTokens(allPriceIds) var coingeckoPriceStats: Map> = emptyMap() - var chainlinkPrices: Map = emptyMap() + var chainlinkPrices: Map = emptyMap() coroutineScope { launch { @@ -215,7 +209,12 @@ class WalletRepositoryImpl( listOf( TokenPriceLocal(priceId, stat[currencyId], fiatCurrency?.symbol, change), chainlinkId?.let { - TokenPriceLocal(chainlinkId, chainlinkPrices[chainlinkId], fiatCurrency?.symbol, change) + TokenPriceLocal( + chainlinkId, + chainlinkPrices[chainlinkId], + fiatCurrency?.symbol, + change + ) } ) }.flatten().filterNotNull() @@ -342,7 +341,15 @@ class WalletRepositoryImpl( ethereumSource.performTransfer(chain, transfer, privateKey.toHexString(true)) .requireValue() // handle error } else { - substrateSource.performTransfer(accountId, chain, transfer, tip, appId, additional, batchAll) + substrateSource.performTransfer( + accountId, + chain, + transfer, + tip, + appId, + additional, + batchAll + ) } val accountAddress = chain.addressOf(accountId) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt index 1643aa3843..fb479e9f0a 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt @@ -173,11 +173,8 @@ class WalletFeatureModule { availableFiatCurrencies: GetAvailableFiatCurrencies, updatesMixin: UpdatesMixin, remoteConfigFetcher: RemoteConfigFetcher, - preferences: Preferences, accountRepository: AccountRepository, chainsRepository: ChainsRepository, - selectedFiat: SelectedFiat, - tokenPriceDao: TokenPriceDao, extrinsicService: ExtrinsicService, @Named(REMOTE_STORAGE_SOURCE) remoteStorageSource: StorageDataSource @@ -195,11 +192,8 @@ class WalletFeatureModule { availableFiatCurrencies, updatesMixin, remoteConfigFetcher, - preferences, accountRepository, chainsRepository, - selectedFiat, - tokenPriceDao, extrinsicService, remoteStorageSource ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt index 56e2622550..b2d2bbc31a 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/list/BalanceListViewModel.kt @@ -132,7 +132,6 @@ class BalanceListViewModel @Inject constructor( private val selectedFiat: SelectedFiat, private val accountInteractor: AccountInteractor, private val updatesMixin: UpdatesMixin, - private val networkStateService: NetworkStateService, private val resourceManager: ResourceManager, private val clipboardManager: ClipboardManager, private val currentAccountAddress: CurrentAccountAddressUseCase, @@ -216,8 +215,6 @@ class BalanceListViewModel @Inject constructor( it.name == appliedFilterAsString } ?: ChainSelectorViewStateWithFilters.Filter.All -// val shouldShowNetworkIssues = -// selectedChainId == null && (networkIssues.isNotEmpty() || assets.any { it.hasAccount.not() }) showNetworkIssues.value = false val selectedAccountFavoriteChains = currentMetaAccountFlow.favoriteChains diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt index 71c8c1c777..7f93e40e81 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/networkissues/NetworkIssuesViewModel.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @HiltViewModel +@Deprecated("Seems like we don't need this anymore") class NetworkIssuesViewModel @Inject constructor( private val walletRouter: WalletRouter, private val walletInteractor: WalletInteractor, diff --git a/runtime/build.gradle b/runtime/build.gradle index 9bc69bea68..8d58334593 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -18,7 +18,7 @@ android { buildTypes { debug { - buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/master/chains/v9/chains.json\"" + buildConfigField "String", "CHAINS_URL", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/develop-free/chains/v9/chains_dev.json\"" } release { diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 2a31402f9b..da8bafad63 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -79,10 +79,6 @@ class ChainRegistry @Inject constructor( .distinctUntilChanged() .shareIn(scope, SharingStarted.Eagerly, replay = 1) -// val chainsById = currentChains.map { chains -> chains.associateBy { it.id } } -// .inBackground() -// .shareIn(scope, SharingStarted.Eagerly, replay = 1) - private val enabledAssetsFlow = assetsCache.observeAllEnabledAssets() .onStart { emit(emptyList()) } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt index 0d55a6669d..da17f7ac60 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt @@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update -private const val ConnectingStatusDebounce = 750L class ConnectionPool @Inject constructor( private val socketServiceProvider: Provider, @@ -33,62 +32,9 @@ class ConnectionPool @Inject constructor( private val networkStateService: NetworkStateService ) : CoroutineScope by CoroutineScope(Dispatchers.Default) { -// private val pool = ConcurrentHashMap() private val poolFlow = MutableStateFlow>(emptyMap()) private val connectionWatcher = MutableStateFlow(Event(Unit)) -// private val connectionIssues = connectionWatcher.flatMapLatest { -// val connListFlow = pool.map { -// -// it.value.isConnecting.map { isConnecting -> -// it.value.chain to isConnecting -// } -// } -// val connectionIssues = combine(connListFlow) { chains -> -// val issues = -// chains.filter { (_, isConnecting) -> isConnecting }.mapNotNull { (iChain, _) -> -// val chain = iChain as? Chain ?: return@mapNotNull null -// NetworkIssueItemState( -// iconUrl = chain.icon, -// title = chain.name, -// type = when { -// chain.nodes.size > 1 -> NetworkIssueType.Node -// else -> NetworkIssueType.Network -// }, -// chainId = chain.id, -// chainName = chain.name, -// assetId = chain.utilityAsset?.id.orEmpty() -// ) -// } -// issues -// } -// -// connectionIssues -// } - -// private val showConnecting = connectionWatcher.flatMapLatest { -// val isConnectedListFlow = pool.map { it.value.isConnected } -// val hasConnectionsFlow = combine(isConnectedListFlow) { it.any { it } } -// -// val isPausedListFlow = pool.map { it.value.isPaused } -// val hasPausesFlow = combine(isPausedListFlow) { it.any { it } } -// -// val isConnectingListFlow = pool.map { it.value.isConnecting } -// val hasConnectingFlow = combine(isConnectingListFlow) { it.any { it } } -// .filter { connecting -> connecting } -// val showConnecting = combine( -// hasConnectionsFlow, -// hasConnectingFlow, -// hasPausesFlow -// ) { connected, connecting, paused -> -// !(connected || paused) && connecting -// } -// showConnecting -// } -// .distinctUntilChanged() -// .debounce(ConnectingStatusDebounce) - - suspend fun awaitConnection(chainId: ChainId): ChainConnection { return poolFlow.map { it[chainId] }.filterNotNull().first() } From 18a41989a77bed14ce4e5a759a5afe2015f5777c Mon Sep 17 00:00:00 2001 From: Deneath Date: Wed, 8 May 2024 15:37:23 +0700 Subject: [PATCH 047/100] fixed DB version --- core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt index ecddfe043b..1f65e03a36 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/AppDatabase.kt @@ -94,7 +94,7 @@ import jp.co.soramitsu.coredb.model.chain.FavoriteChainLocal import jp.co.soramitsu.coredb.model.chain.MetaAccountLocal @Database( - version = 65, + version = 66, entities = [ AccountLocal::class, AddressBookContact::class, From b25c5271d803d5db70819d3529ea78ebbf0c9030 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 13 May 2024 11:14:33 +0700 Subject: [PATCH 048/100] fixed migration, fixed assets on search screen --- .../co/soramitsu/app/root/presentation/RootActivity.kt | 1 - .../java/jp/co/soramitsu/coredb/migrations/Migrations.kt | 1 + .../account/impl/domain/TotalBalanceUseCaseImpl.kt | 3 ++- .../balance/searchAssets/SearchAssetsViewModel.kt | 9 ++++++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt index 827d31f5e0..964e79e1f5 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt @@ -9,7 +9,6 @@ import android.net.NetworkCapabilities import android.net.NetworkRequest import android.net.Uri import android.os.Bundle -import android.util.Log import android.view.View import android.view.animation.Animation import android.view.animation.TranslateAnimation diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt index dcbff13857..d2a7038420 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt @@ -6,6 +6,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase val Migration_65_66 = object : Migration(65, 66) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE meta_accounts ADD COLUMN `initialized` INTEGER NOT NULL DEFAULT 0") + db.execSQL("DELETE FROM assets") } } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt index d8096d57a4..b4747f1719 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/TotalBalanceUseCaseImpl.kt @@ -61,7 +61,8 @@ class TotalBalanceUseCaseImpl( val filtered = assets .asSequence() - .filter { it.asset.freeInPlanks != null && it.asset.freeInPlanks.isNotZero() && it.token?.fiatSymbol != null } + .filter { it.asset.enabled == true } + .filter { it.asset.freeInPlanks != null && it.asset.freeInPlanks.positiveOrNull().isNotZero() && it.token?.fiatSymbol != null } .toList() // todo I did this workaround because sometimes there is a wrong symbol in asset list. Need research diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt index b383f12360..2af6d8e0c2 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/searchAssets/SearchAssetsViewModel.kt @@ -7,12 +7,14 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import java.math.BigDecimal +import java.math.BigInteger import javax.inject.Inject import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.compose.component.ActionItemType import jp.co.soramitsu.common.compose.component.SwipeState import jp.co.soramitsu.common.compose.viewstate.AssetListItemViewState import jp.co.soramitsu.common.utils.Event +import jp.co.soramitsu.common.utils.greaterThanOrEquals import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId @@ -51,9 +53,14 @@ class SearchAssetsViewModel @Inject constructor( interactor.assetsFlow(), chainInteractor.getChainsFlow(), ) { assets: List, chains: List -> + val readyToUseAssets = assets + .asSequence() + .filter { it.asset.freeInPlanks.greaterThanOrEquals(BigInteger.ZERO) } + .filter { it.asset.enabled == true } + .toList() val balanceListItems = AssetListHelper.processAssets( - assets = assets, + assets = readyToUseAssets, filteredChains = chains, networkIssues = emptySet() ) From dfb60efd72b4e1ccfa56471682ae49f7f12e62ad Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 13 May 2024 15:18:01 +0700 Subject: [PATCH 049/100] fixed config failed loading --- .../app/root/di/RootFeatureModule.kt | 7 +++-- .../app/root/domain/RootInteractor.kt | 13 ++++++-- .../app/root/presentation/RootViewModel.kt | 23 +++++++++----- .../account/impl/domain/WalletSyncService.kt | 3 +- .../domain/interfaces/WalletRepository.kt | 2 -- .../data/repository/WalletRepositoryImpl.kt | 4 --- .../runtime/di/ChainRegistryModule.kt | 2 +- .../runtime/multiNetwork/ChainRegistry.kt | 30 ++++++++++++++----- 8 files changed, 57 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt index 21ea5cd32b..5e2d91ad99 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt @@ -11,6 +11,7 @@ import jp.co.soramitsu.account.impl.domain.WalletSyncService import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.core.updater.UpdateSystem +import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository @InstallIn(SingletonComponent::class) @@ -24,7 +25,8 @@ class RootFeatureModule { pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, preferences: Preferences, accountRepository: AccountRepository, - walletSyncService: WalletSyncService + walletSyncService: WalletSyncService, + chainRegistry: ChainRegistry ): RootInteractor { return RootInteractor( walletUpdateSystem, @@ -32,7 +34,8 @@ class RootFeatureModule { pendulumPreInstalledAccountsScenario, preferences, accountRepository, - walletSyncService + walletSyncService, + chainRegistry ) } } diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt index 50eb740537..0eab597bf6 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt @@ -12,6 +12,7 @@ import jp.co.soramitsu.common.utils.inBackground import jp.co.soramitsu.common.utils.requireValue import jp.co.soramitsu.core.updater.UpdateSystem import jp.co.soramitsu.core.updater.Updater +import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.wallet.impl.data.buyToken.ExternalProvider import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository import kotlinx.coroutines.Dispatchers @@ -27,8 +28,14 @@ class RootInteractor( private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, private val preferences: Preferences, private val accountRepository: AccountRepository, - private val walletSyncService: WalletSyncService + private val walletSyncService: WalletSyncService, + private val chainRegistry: ChainRegistry, ) { + suspend fun syncChainsConfigs(): Result { + return withContext(Dispatchers.Default) { + return@withContext chainRegistry.syncConfigs() + } + } fun runWalletsSync() { walletSyncService.start() @@ -36,7 +43,7 @@ class RootInteractor( suspend fun runBalancesUpdate(): Flow = withContext(Dispatchers.Default) { // await all accounts initialized - val s = accountRepository.allMetaAccountsFlow().filter { accounts -> accounts.all { it.initialized } }.filter { it.isNotEmpty() }.first() + accountRepository.allMetaAccountsFlow().filter { accounts -> accounts.all { it.initialized } }.filter { it.isNotEmpty() }.first() return@withContext updateSystem.start().inBackground() } @@ -64,7 +71,7 @@ class RootInteractor( } } - fun chainRegistrySyncUp() = walletRepository.chainRegistrySyncUp() + fun chainRegistrySyncUp() = chainRegistry.syncUp() suspend fun fetchFeatureToggle() = withContext(Dispatchers.Default) { pendulumPreInstalledAccountsScenario.fetchFeatureToggle() } diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index 7c767fa65d..40b3426436 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -21,6 +21,7 @@ import kotlin.concurrent.timerTask import kotlin.time.DurationUnit import kotlin.time.toDuration import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -63,21 +64,28 @@ class RootViewModel @Inject constructor( init { viewModelScope.launch { - interactor.fetchFeatureToggle() + syncConfigs() } - checkAppVersion() observeWalletConnectEvents() } - private fun checkAppVersion() { - viewModelScope.launch { + private suspend fun syncConfigs() { + coroutineScope { + checkAppVersion() + interactor.fetchFeatureToggle() + interactor.syncChainsConfigs().onFailure { + _showNoInternetConnectionAlert.value = Event(Unit) + } + } + } + + private suspend fun checkAppVersion() { val appConfigResult = interactor.getRemoteConfig() if (appConfigResult.getOrNull()?.isCurrentVersionSupported == false) { _showUnsupportedAppVersionAlert.value = Event(Unit) } else { runBalancesUpdate() } - } } private fun runBalancesUpdate() { @@ -171,10 +179,11 @@ class RootViewModel @Inject constructor( } fun retryLoadConfigClicked() { - checkAppVersion() + viewModelScope.launch { + syncConfigs() + } } - private val _showConnectingBar = MutableStateFlow(false) val showConnectingBar: StateFlow = _showConnectingBar fun onNetworkAvailable() { diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index 5877204f48..6870d7b328 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.job +import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withTimeoutOrNull @@ -65,7 +66,7 @@ class WalletSyncService( .onEach { localMetaAccounts -> syncJob?.cancel() syncJob = scope.launch { - chainRegistry.configsSyncDeferred.join() + chainRegistry.configsSyncDeferred.joinAll() val chains = chainsRepository.getChains() val ethereumChains = chains.filter { it.isEthereumChain } diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt index 6ef8189f42..6cef5f3930 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletRepository.kt @@ -91,8 +91,6 @@ interface WalletRepository { suspend fun getRemoteConfig(): Result - fun chainRegistrySyncUp() - suspend fun getSingleAssetPriceCoingecko(priceId: String, currency: String): BigDecimal? suspend fun getControllerAccount(chainId: ChainId, accountId: AccountId): AccountId? suspend fun getStashAccount(chainId: ChainId, accountId: AccountId): AccountId? diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index 26715f4794..603789010f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -552,10 +552,6 @@ class WalletRepositoryImpl( return kotlin.runCatching { remoteConfigFetcher.getAppConfig() } } - override fun chainRegistrySyncUp() { - chainRegistry.syncUp() - } - override suspend fun getControllerAccount(chainId: ChainId, accountId: AccountId): AccountId? { return substrateSource.getControllerAccount(chainId, accountId) } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt index 5106cd3158..2f2a359efa 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/di/ChainRegistryModule.kt @@ -159,7 +159,7 @@ class ChainRegistryModule { networkStateService: NetworkStateService, ethereumConnectionPool: EthereumConnectionPool, assetReadOnlyCache: AssetDao, - chainsRepository: ChainsRepository + chainsRepository: ChainsRepository, ): ChainRegistry = ChainRegistry( runtimeProviderPool, chainConnectionPool, diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index da8bafad63..ebda156d6e 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -6,6 +6,7 @@ import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.utils.diffed +import jp.co.soramitsu.common.utils.failure import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.core.models.IChain @@ -31,6 +32,7 @@ import jp.co.soramitsu.shared_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.shared_utils.wsrpc.state.SocketStateMachine import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async @@ -43,10 +45,10 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -65,7 +67,7 @@ class ChainRegistry @Inject constructor( private val updatesMixin: UpdatesMixin, private val networkStateService: NetworkStateService, private val ethereumConnectionPool: EthereumConnectionPool, - private val assetsCache: AssetReadOnlyCache, + assetsCache: AssetReadOnlyCache, private val chainsRepository: ChainsRepository, private val dispatcher: CoroutineDispatcher = Dispatchers.Default ) : IChainRegistry, UpdatesProviderUi by updatesMixin { @@ -99,18 +101,32 @@ class ChainRegistry @Inject constructor( .filter { it.addedOrModified.isNotEmpty() || it.removed.isNotEmpty() } .flowOn(dispatcher) - val configsSyncDeferred = scope.async { - launch { chainSyncService.syncUp() } - launch { runtimeSyncService.syncTypes() } - } + var configsSyncDeferred: MutableList> = mutableListOf() init { syncUp() } + suspend fun syncConfigs() = withContext(dispatcher) { + val chainSyncDeferred = async { chainSyncService.syncUp() } + val typesResultDeferred = async { runtimeSyncService.syncTypes() } + + configsSyncDeferred.add(chainSyncDeferred) + configsSyncDeferred.add(typesResultDeferred) + + val chainsSyncResult = chainSyncDeferred.await() + val typesResult = typesResultDeferred.await() + + return@withContext if(chainsSyncResult.isSuccess && typesResult.isSuccess) { + Result.success(Unit) + } else { + Result.failure("failed to load chains configs") + } + } + fun syncUp() { scope.launch { - configsSyncDeferred.join() + configsSyncDeferred.joinAll() chainsToSync .onEach { (removed, addedOrModified, all) -> From 43ff08bed363683087abb1d1b5e995e47fce75ed Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 14 May 2024 13:30:47 +0700 Subject: [PATCH 050/100] Fixed assets visibility for popular utility assets if there is at least one positive balance for account --- .../jp/co/soramitsu/coredb/dao/AssetDao.kt | 3 ++ .../account/impl/domain/WalletSyncService.kt | 49 ++++++++++++++++--- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt index 5569956c50..4256a02302 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/AssetDao.kt @@ -144,6 +144,9 @@ abstract class AssetDao : AssetReadOnlyCache { } } + @Query("UPDATE assets SET enabled = CASE WHEN EXISTS (SELECT 1 FROM assets WHERE metaId = :metaId AND freeInPlanks > 0) THEN 0 ELSE enabled END WHERE metaId = :metaId AND (freeInPlanks IS NULL OR freeInPlanks = 0)") + abstract fun hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaId: Long) + @Query( """ SELECT symbol FROM chain_assets WHERE chain_assets.id = :assetId diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index 6870d7b328..7a18e347f7 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -7,8 +7,8 @@ import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.account.impl.data.mappers.mapMetaAccountLocalToMetaAccount import jp.co.soramitsu.common.data.network.runtime.binding.AssetBalance import jp.co.soramitsu.common.data.network.runtime.binding.toAssetBalance -import jp.co.soramitsu.common.utils.isNotZero import jp.co.soramitsu.common.utils.orZero +import jp.co.soramitsu.common.utils.positiveOrNull import jp.co.soramitsu.core.models.ChainAssetType import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.coredb.dao.AssetDao @@ -29,6 +29,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn @@ -37,6 +38,7 @@ import kotlinx.coroutines.job import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope +import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull class WalletSyncService( @@ -69,8 +71,8 @@ class WalletSyncService( chainRegistry.configsSyncDeferred.joinAll() val chains = chainsRepository.getChains() - val ethereumChains = chains.filter { it.isEthereumChain } - val substrateChains = chains.filter { !it.isEthereumChain } + val ethereumChains = chains.filter { it.isEthereumChain }.sortedByDescending { it.rank } + val substrateChains = chains.filter { !it.isEthereumChain }.sortedByDescending { it.rank } val metaAccounts = localMetaAccounts.map { accountInfo -> @@ -79,6 +81,7 @@ class WalletSyncService( accountInfo ) } + val accountHasAssetWithPositiveBalanceMap = mutableMapOf() supervisorScope { launch { @@ -110,6 +113,13 @@ class WalletSyncService( ) }.getOrNull() + if(balance.positiveOrNull() != null) { + accountHasAssetWithPositiveBalanceMap[metaAccount.id] = true + } + + val isPopularUtilityAsset = chain.rank != null && chainAsset.isUtility + val accountHasAssetWithPositiveBalance = accountHasAssetWithPositiveBalanceMap[metaAccount.id] == true + AssetLocal( id = chainAsset.id, chainId = chain.id, @@ -123,13 +133,14 @@ class WalletSyncService( bondedInPlanks = BigInteger.ZERO, redeemableInPlanks = BigInteger.ZERO, unbondingInPlanks = BigInteger.ZERO, - enabled = balance.isNotZero() || chain.rank != null && chainAsset.isUtility + enabled = balance.positiveOrNull() != null || (!accountHasAssetWithPositiveBalance && isPopularUtilityAsset) ) } }.flatten() } val localAssets = assetsDeferred.await() assetDao.insertAssets(localAssets) + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts) } } } @@ -207,6 +218,13 @@ class WalletSyncService( ).getOrNull().toAssetBalance() } ?: AssetBalance() + if(assetBalance.freeInPlanks.positiveOrNull() != null) { + accountHasAssetWithPositiveBalanceMap[metadata.metaAccountId] = true + } + + val isPopularUtilityAsset = chain.rank != null && metadata.asset.isUtility + val accountHasAssetWithPositiveBalance = accountHasAssetWithPositiveBalanceMap[metadata.metaAccountId] == true + AssetLocal( id = metadata.asset.id, chainId = chain.id, @@ -220,23 +238,39 @@ class WalletSyncService( bondedInPlanks = assetBalance.bondedInPlanks, redeemableInPlanks = assetBalance.redeemableInPlanks, unbondingInPlanks = assetBalance.unbondingInPlanks, - enabled = assetBalance.freeInPlanks.isNotZero() || chain.rank != null && metadata.asset.isUtility + enabled = assetBalance.freeInPlanks.positiveOrNull() != null || (!accountHasAssetWithPositiveBalance && isPopularUtilityAsset) ) } + emptyAssets } } val localAssets = assetsDeferred.await() assetDao.insertAssets(localAssets) + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts) } } } this }.coroutineContext.job.join() - metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) + + coroutineScope { + metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts) + } } }.launchIn(scope) + } + private suspend fun hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts: List) { + coroutineScope { + metaAccounts.forEach { + withContext(Dispatchers.IO) { + assetDao.hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance( + it.id + ) + } + } + } } private suspend fun buildEquilibriumAssets( @@ -286,13 +320,14 @@ class WalletSyncService( equilibriumAssetsBalanceMap.getOrDefault(it, null) .orZero() }.orZero() + AssetLocal( accountId = metadata.accountId, id = asset.id, chainId = asset.chainId, metaId = metadata.metaAccountId, tokenPriceId = asset.priceId, - enabled = false, + enabled = balance.positiveOrNull() != null, freeInPlanks = balance ) } From cfff0d252b5e925b5032cc6e2d586b6fd4ea6f6a Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 14 May 2024 15:25:25 +0700 Subject: [PATCH 051/100] Fixed asset management drop down, fix code formatting --- .../account/impl/domain/WalletSyncService.kt | 30 ++++++++++++------- .../manageassets/ManageAssetsContent.kt | 10 ++++++- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index 7a18e347f7..abe72d2de5 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -63,7 +63,7 @@ class WalletSyncService( private var syncJob: Job? = null - fun start(){ + fun start() { metaAccountDao.observeNotInitializedMetaAccounts().filter { it.isNotEmpty() } .onEach { localMetaAccounts -> syncJob?.cancel() @@ -71,8 +71,10 @@ class WalletSyncService( chainRegistry.configsSyncDeferred.joinAll() val chains = chainsRepository.getChains() - val ethereumChains = chains.filter { it.isEthereumChain }.sortedByDescending { it.rank } - val substrateChains = chains.filter { !it.isEthereumChain }.sortedByDescending { it.rank } + val ethereumChains = + chains.filter { it.isEthereumChain }.sortedByDescending { it.rank } + val substrateChains = + chains.filter { !it.isEthereumChain }.sortedByDescending { it.rank } val metaAccounts = localMetaAccounts.map { accountInfo -> @@ -113,12 +115,15 @@ class WalletSyncService( ) }.getOrNull() - if(balance.positiveOrNull() != null) { - accountHasAssetWithPositiveBalanceMap[metaAccount.id] = true + if (balance.positiveOrNull() != null) { + accountHasAssetWithPositiveBalanceMap[metaAccount.id] = + true } - val isPopularUtilityAsset = chain.rank != null && chainAsset.isUtility - val accountHasAssetWithPositiveBalance = accountHasAssetWithPositiveBalanceMap[metaAccount.id] == true + val isPopularUtilityAsset = + chain.rank != null && chainAsset.isUtility + val accountHasAssetWithPositiveBalance = + accountHasAssetWithPositiveBalanceMap[metaAccount.id] == true AssetLocal( id = chainAsset.id, @@ -218,12 +223,15 @@ class WalletSyncService( ).getOrNull().toAssetBalance() } ?: AssetBalance() - if(assetBalance.freeInPlanks.positiveOrNull() != null) { - accountHasAssetWithPositiveBalanceMap[metadata.metaAccountId] = true + if (assetBalance.freeInPlanks.positiveOrNull() != null) { + accountHasAssetWithPositiveBalanceMap[metadata.metaAccountId] = + true } - val isPopularUtilityAsset = chain.rank != null && metadata.asset.isUtility - val accountHasAssetWithPositiveBalance = accountHasAssetWithPositiveBalanceMap[metadata.metaAccountId] == true + val isPopularUtilityAsset = + chain.rank != null && metadata.asset.isUtility + val accountHasAssetWithPositiveBalance = + accountHasAssetWithPositiveBalanceMap[metadata.metaAccountId] == true AssetLocal( id = metadata.asset.id, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt index 8479f576df..0283c2b607 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/manageassets/ManageAssetsContent.kt @@ -18,6 +18,7 @@ import androidx.compose.material.Icon import androidx.compose.material.Switch import androidx.compose.material.SwitchColors import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf @@ -156,9 +157,16 @@ fun ManageAssetsContent( ManageAssetItem(assets[0], callback::onEditClicked, callback::onItemClicked, callback::onChecked) } else { val isCollapsed = remember { mutableStateOf(true) } + + LaunchedEffect(key1 = state.searchQuery) { + if (state.searchQuery.isNullOrBlank().not()) { + isCollapsed.value = false + } + } + GroupItem(assets, isCollapsed) - if (isCollapsed.value.not() || state.searchQuery.isNullOrBlank().not()) { + if (isCollapsed.value.not()) { Column { assets.map { ManageAssetItem(it.copy(isGrouped = true), callback::onEditClicked, callback::onItemClicked, callback::onChecked) From 23220bc6c849a9432da16449cc002b34e949c99a Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 16 May 2024 13:01:00 +0700 Subject: [PATCH 052/100] Fixed bugs with empty asset details and chain accounts sync --- .../jp/co/soramitsu/coredb/dao/ChainDao.kt | 3 +- .../co/soramitsu/coredb/dao/MetaAccountDao.kt | 6 + .../soramitsu/coredb/migrations/Migrations.kt | 1 + .../coredb/model/chain/MetaAccountLocal.kt | 3 +- .../data/repository/AccountRepositoryImpl.kt | 9 +- .../account/impl/domain/WalletSyncService.kt | 210 +++++++++++++++++- .../data/repository/WalletRepositoryImpl.kt | 11 +- 7 files changed, 218 insertions(+), 25 deletions(-) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/ChainDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/ChainDao.kt index 7ec48297f3..28e361bb0c 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/ChainDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/ChainDao.kt @@ -172,9 +172,8 @@ abstract class ChainDao { """ SELECT c.*, a.*, tp.* FROM chains c JOIN chain_assets ca ON ca.chainId = c.id AND ca.symbol in (:assetSymbol, '$xcPrefix'||:assetSymbol) - LEFT JOIN assets a ON a.chainId = c.id AND a.id = ca.id + LEFT JOIN assets a ON a.chainId = c.id AND a.id = ca.id AND a.metaId = :accountMetaId AND a.enabled = 1 LEFT JOIN token_price tp ON tp.priceId = a.tokenPriceId - AND a.metaId = :accountMetaId """ ) protected abstract fun observeChainsWithBalanceByName( diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt index 057638e82f..ccc75a4ce9 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/dao/MetaAccountDao.kt @@ -40,6 +40,12 @@ interface MetaAccountDao { @Insert suspend fun insertChainAccount(chainAccount: ChainAccountLocal) + @Query("SELECT * FROM chain_accounts WHERE initialized = 0") + fun observeNotInitializedChainAccounts(): Flow> + + @Query("UPDATE chain_accounts SET initialized = 1 WHERE metaId = :metaId AND chainId = :chainId") + suspend fun markChainAccountInitialized(metaId: Long, chainId: String) :Int + @Query("SELECT * FROM meta_accounts") fun getMetaAccounts(): List diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt index d2a7038420..db60656743 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt @@ -6,6 +6,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase val Migration_65_66 = object : Migration(65, 66) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL("ALTER TABLE meta_accounts ADD COLUMN `initialized` INTEGER NOT NULL DEFAULT 0") + db.execSQL("ALTER TABLE chain_accounts ADD COLUMN `initialized` INTEGER NOT NULL DEFAULT 0") db.execSQL("DELETE FROM assets") } } diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt index 096e188724..971920a67c 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/MetaAccountLocal.kt @@ -81,7 +81,8 @@ class ChainAccountLocal( val publicKey: ByteArray, val accountId: ByteArray, val cryptoType: CryptoType, - val name: String + val name: String, + val initialized: Boolean ) interface JoinedMetaAccountInfo { diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt index 049fe982e2..4ef4ca5169 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/data/repository/AccountRepositoryImpl.kt @@ -415,7 +415,8 @@ class AccountRepositoryImpl( publicKey = publicKey, accountId = accountId, cryptoType = crypto, - name = accountName + name = accountName, + initialized = false ) insertChainAccount(chainAccount) @@ -514,7 +515,8 @@ class AccountRepositoryImpl( publicKey = publicKey, accountId = accountId, cryptoType = crypto, - name = accountName + name = accountName, + initialized = false ) insertChainAccount(chainAccount) @@ -835,7 +837,8 @@ class AccountRepositoryImpl( publicKey = publicKey, accountId = accountId, cryptoType = crypto, - name = accountName + name = accountName, + initialized = false ) insertChainAccount(chainAccount) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt index abe72d2de5..53c92dfe3e 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/WalletSyncService.kt @@ -64,6 +64,11 @@ class WalletSyncService( private var syncJob: Job? = null fun start() { + observeNotInitializedMetaAccounts() + observeNotInitializedChainAccounts() + } + + private fun observeNotInitializedMetaAccounts() { metaAccountDao.observeNotInitializedMetaAccounts().filter { it.isNotEmpty() } .onEach { localMetaAccounts -> syncJob?.cancel() @@ -145,7 +150,7 @@ class WalletSyncService( } val localAssets = assetsDeferred.await() assetDao.insertAssets(localAssets) - hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts) + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalanceByMetaAccounts(metaAccounts) } } } @@ -167,7 +172,7 @@ class WalletSyncService( chain.utilityAsset != null && chain.utilityAsset!!.typeExtra == ChainAssetType.Equilibrium if (isEquilibriumTypeChain) { - buildEquilibriumAssets(metaAccounts, chain, runtime) + buildEquilibriumAssetsByMetaAccounts(metaAccounts, chain, runtime) } else { val allAccountsStorageKeys = metaAccounts.mapNotNull { metaAccount -> @@ -253,7 +258,7 @@ class WalletSyncService( } val localAssets = assetsDeferred.await() assetDao.insertAssets(localAssets) - hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts) + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalanceByMetaAccounts(metaAccounts) } } } @@ -263,34 +268,215 @@ class WalletSyncService( coroutineScope { metaAccountDao.markAccountsInitialized(metaAccounts.map { it.id }) - hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts) + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalanceByMetaAccounts(metaAccounts) } } - }.launchIn(scope) + } + .launchIn(scope) } - private suspend fun hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance(metaAccounts: List) { + private fun observeNotInitializedChainAccounts() { + metaAccountDao.observeNotInitializedChainAccounts().filter { it.isNotEmpty() } + .onEach { chainAccounts -> + chainRegistry.configsSyncDeferred.joinAll() + val chains = chainAccounts.map { chainRegistry.getChain(it.chainId) }.associateBy { it.id } + val ethereumChains = + chains.values.filter { it.isEthereumChain }.sortedByDescending { it.rank } + val substrateChains = + chains.values.filter { !it.isEthereumChain }.sortedByDescending { it.rank } + + supervisorScope { + launch { + ethereumChains.forEach { chain -> + launch { + val assetsDeferred = async { + if (chainRegistry.checkChainSyncedUp(chain).not()) { + chainRegistry.setupChain(chain) + } + val connection = + withTimeoutOrNull(CHAIN_SYNC_TIMEOUT_MILLIS) { + val connection = + chainRegistry.awaitEthereumConnection(chain.id) + // await connecting to the node + connection.statusFlow.first { it is EvmConnectionStatus.Connected } + connection + } + + chainAccounts.map { chainAccount -> + chainAccount.accountId + val accountId = chainAccount.accountId + + chain.assets.map { chainAsset -> + val balance = kotlin.runCatching { + connection?.web3j?.fetchEthBalance( + chainAsset, + accountId.toHexString(true) + ) + }.getOrNull() + + AssetLocal( + id = chainAsset.id, + chainId = chain.id, + accountId = accountId, + metaId = chainAccount.metaId, + tokenPriceId = chainAsset.priceId, + freeInPlanks = balance, + reservedInPlanks = BigInteger.ZERO, + miscFrozenInPlanks = BigInteger.ZERO, + feeFrozenInPlanks = BigInteger.ZERO, + bondedInPlanks = BigInteger.ZERO, + redeemableInPlanks = BigInteger.ZERO, + unbondingInPlanks = BigInteger.ZERO, + enabled = balance.positiveOrNull()!= null || chainAsset.isUtility + ) + } + }.flatten() + } + val localAssets = assetsDeferred.await() + assetDao.insertAssets(localAssets) + } + } + } + launch { + substrateChains.onEach { chain -> + launch { + val assetsDeferred = async { + val emptyAssets: MutableList = mutableListOf() + val runtime = withTimeoutOrNull(CHAIN_SYNC_TIMEOUT_MILLIS) { + if (chainRegistry.checkChainSyncedUp(chain).not()) { + chainRegistry.setupChain(chain) + } + + // awaiting runtime snapshot + chainRegistry.awaitRuntimeProvider(chain.id).get() + } + + val isEquilibriumTypeChain = + chain.utilityAsset != null && chain.utilityAsset!!.typeExtra == ChainAssetType.Equilibrium + + if (isEquilibriumTypeChain) { + buildEquilibriumAssets(chainAccounts.map { it.metaId to it.accountId }, chain, runtime) + } else { + val allAccountsStorageKeys = + chainAccounts.map { chainAccount -> + buildSubstrateStorageKeys( + chain, + runtime, + chainAccount.metaId, + chainAccount.accountId + ) + }.flatten() + + val keysToQuery = + allAccountsStorageKeys.mapNotNull { metadata -> + // if storage key build is failed - we put the empty assets + if (metadata.key == null) { + emptyAssets.add( + AssetLocal( + accountId = metadata.accountId, + id = metadata.asset.id, + chainId = metadata.asset.chainId, + metaId = metadata.metaAccountId, + tokenPriceId = metadata.asset.priceId, + enabled = false, + freeInPlanks = BigInteger.valueOf(-1) + ) + ) + } + metadata.key + }.toList() + + val storageKeyToResult = remoteStorageSource.queryKeys( + keysToQuery, + chain.id, + null + ) + + allAccountsStorageKeys.map { metadata -> + val hexRaw = + storageKeyToResult.getOrDefault( + metadata.key, + null + ) + + val assetBalance = + runtime?.let { + handleBalanceResponse( + it, + metadata.asset, + hexRaw + ).getOrNull().toAssetBalance() + } ?: AssetBalance() + + AssetLocal( + id = metadata.asset.id, + chainId = chain.id, + accountId = metadata.accountId, + metaId = metadata.metaAccountId, + tokenPriceId = metadata.asset.priceId, + freeInPlanks = assetBalance.freeInPlanks, + reservedInPlanks = assetBalance.reservedInPlanks, + miscFrozenInPlanks = assetBalance.miscFrozenInPlanks, + feeFrozenInPlanks = assetBalance.feeFrozenInPlanks, + bondedInPlanks = assetBalance.bondedInPlanks, + redeemableInPlanks = assetBalance.redeemableInPlanks, + unbondingInPlanks = assetBalance.unbondingInPlanks, + enabled = assetBalance.freeInPlanks.positiveOrNull() != null || metadata.asset.isUtility + ) + } + emptyAssets + } + } + val localAssets = assetsDeferred.await() + assetDao.insertAssets(localAssets) + } + } + } + + this + }.coroutineContext.job.join() + coroutineScope { + chainAccounts.forEach { + metaAccountDao.markChainAccountInitialized(it.metaId, it.chainId) + } + } + } + .launchIn(scope) + } + + private suspend fun hideEmptyAssetsIfThereAreAtLeastOnePositiveBalanceByMetaAccounts(metaAccounts: List) { + hideEmptyAssetsIfThereAreAtLeastOnePositiveBalanceByMetaIds(metaAccounts.map { it.id }) + } + + private suspend fun hideEmptyAssetsIfThereAreAtLeastOnePositiveBalanceByMetaIds(metaAccountsIds: List) { coroutineScope { - metaAccounts.forEach { + metaAccountsIds.forEach { withContext(Dispatchers.IO) { assetDao.hideEmptyAssetsIfThereAreAtLeastOnePositiveBalance( - it.id + it ) } } } } - private suspend fun buildEquilibriumAssets( + private suspend fun buildEquilibriumAssetsByMetaAccounts( metaAccounts: List, chain: Chain, runtime: RuntimeSnapshot? + ): List { + return buildEquilibriumAssets(metaAccounts.map { it.id to it.accountId(chain) }, chain, runtime) + } + + private suspend fun buildEquilibriumAssets( + accountInfo: List>, + chain: Chain, + runtime: RuntimeSnapshot? ): List { val emptyAssets: MutableList = mutableListOf() - val allAccountsStorageKeys = metaAccounts.mapNotNull { metaAccount -> - val accountId = metaAccount.accountId(chain) ?: return@mapNotNull null - buildEquilibriumStorageKeys(chain, runtime, metaAccount.id, accountId) + val allAccountsStorageKeys = accountInfo.mapNotNull { (metaId, accountId) -> + accountId ?: return@mapNotNull null + buildEquilibriumStorageKeys(chain, runtime, metaId, accountId) }.associateBy { it.key } val keysToQuery = diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt index 603789010f..049fc185ce 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/WalletRepositoryImpl.kt @@ -16,9 +16,7 @@ import jp.co.soramitsu.common.data.network.runtime.binding.bindString import jp.co.soramitsu.common.data.network.runtime.binding.cast import jp.co.soramitsu.common.data.secrets.v2.KeyPairSchema import jp.co.soramitsu.common.data.secrets.v2.MetaAccountSecrets -import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies -import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.mixin.api.UpdatesMixin import jp.co.soramitsu.common.mixin.api.UpdatesProviderUi import jp.co.soramitsu.common.utils.Modules @@ -34,7 +32,6 @@ import jp.co.soramitsu.core.runtime.storage.returnType import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.coredb.dao.OperationDao import jp.co.soramitsu.coredb.dao.PhishingDao -import jp.co.soramitsu.coredb.dao.TokenPriceDao import jp.co.soramitsu.coredb.dao.emptyAccountIdValue import jp.co.soramitsu.coredb.model.AssetUpdateItem import jp.co.soramitsu.coredb.model.AssetWithToken @@ -564,12 +561,12 @@ class WalletRepositoryImpl( accountMetaId: Long, assetId: String ): Flow> { - return chainsRepository.observeChainsPerAssetFlow(accountMetaId, assetId).map { - val chains = it.keys.map { mapChainLocalToChain(it) } + return chainsRepository.observeChainsPerAssetFlow(accountMetaId, assetId).map { chainsPerAsset -> + val chains = chainsPerAsset.keys.map { mapChainLocalToChain(it) } val chainsById = chains.associateBy { it.id } - val assets = it.values.map { mapAssetLocalToAsset(chainsById, it) } + val assets = chainsPerAsset.values.map { mapAssetLocalToAsset(chainsById, it) } val chainToAssetMap = chains.zip(assets).toMap() - chainToAssetMap.filter { pair -> pair.value != null && pair.value?.enabled == true } + chainToAssetMap.filter { pair -> pair.value != null} } } From 05a6b8efef7c77656cdec4e96fa8ecb68fe19911 Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 16 May 2024 13:27:56 +0700 Subject: [PATCH 053/100] remove debug code --- .../java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt index b258d0b213..49f49866bc 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/BalancesUtils.kt @@ -61,9 +61,6 @@ fun buildSubstrateStorageKeys(chain: Chain, metaAccountId: Long, accountId: ByteArray): List{ return chain.assets.map { asset -> - if(chain.id == "afdc188f45c71dacbaa0b62e16a91f726c7b8699a9748cdf715459de6b7f366d"){ - chain.hashCode() - } StorageKeyWithMetadata( asset, metaAccountId, accountId, runtime?.let { constructBalanceKey(it, asset, accountId) } From ab14ab9d782bc5d40ccdff226b0d7d65b3701cd4 Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 16 May 2024 14:53:17 +0700 Subject: [PATCH 054/100] Fixed oklink explorer --- .../transaction/detail/transfer/TransactionDetailViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt index 87387837dd..3040bf45fb 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/transaction/detail/transfer/TransactionDetailViewModel.kt @@ -65,6 +65,7 @@ class TransactionDetailViewModel @Inject constructor( fun getSupportedExplorers(explorerType: Chain.Explorer.Type, value: String): Map { val explorerUrlType: BlockExplorerUrlBuilder.Type = when (explorerType) { + Chain.Explorer.Type.OKLINK, Chain.Explorer.Type.ETHERSCAN -> BlockExplorerUrlBuilder.Type.TX Chain.Explorer.Type.REEF -> BlockExplorerUrlBuilder.Type.TRANSFER else -> BlockExplorerUrlBuilder.Type.EXTRINSIC From f4d4217eeb3241cda18785b8c67c2118f6ed2d59 Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 17 May 2024 18:18:52 +0700 Subject: [PATCH 055/100] Fixed bugs with chains and assets selectors --- .../impl/domain/interfaces/WalletInteractor.kt | 2 ++ .../wallet/impl/domain/WalletInteractorImpl.kt | 14 +++++++++++--- .../assetselector/AssetSelectViewModel.kt | 2 +- .../chainselector/ChainSelectViewModel.kt | 17 ++++------------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt index d1e8c99fc3..c5f4120868 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/impl/domain/interfaces/WalletInteractor.kt @@ -161,4 +161,6 @@ interface WalletInteractor { suspend fun saveAssetManagementIntroPassed() fun networkIssuesFlow(): Flow> suspend fun retryChainSync(chainId: ChainId): Result + + fun observeCurrentAccountChainsPerAsset(assetId: String): Flow> } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index f3490e514f..3fb8bc533e 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -100,7 +100,7 @@ class WalletInteractorImpl( private val xcmEntitiesFetcher: XcmEntitiesFetcher, private val chainsRepository: ChainsRepository, private val networkStateService: NetworkStateService, - private val coroutineContext: CoroutineContext = Dispatchers.Default, + private val coroutineContext: CoroutineContext = Dispatchers.Default ) : WalletInteractor, UpdatesProviderUi by updatesMixin { @OptIn(ExperimentalCoroutinesApi::class) @@ -272,10 +272,10 @@ class WalletInteractorImpl( override suspend fun getTransferFee( transfer: Transfer, additional: (suspend ExtrinsicBuilder.() -> Unit)? - ): Fee { + ): Fee = withContext(Dispatchers.Default) { val chain = chainsRepository.getChain(transfer.chainAsset.chainId) - return walletRepository.getTransferFee( + return@withContext walletRepository.getTransferFee( chain = chain, transfer = transfer, additional = additional @@ -647,6 +647,14 @@ class WalletInteractorImpl( return walletRepository.observeChainsPerAsset(accountMetaId, assetId) } + override fun observeCurrentAccountChainsPerAsset( + assetId: String + ): Flow> { + return accountRepository.selectedMetaAccountFlow().flatMapLatest { + walletRepository.observeChainsPerAsset(it.id, assetId) + } + } + override fun applyAssetSorting(sorting: AssetSorting) { preferences.putString(ASSET_SORTING_KEY, sorting.name) } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetselector/AssetSelectViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetselector/AssetSelectViewModel.kt index f9bf680745..55eb4d2fd2 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetselector/AssetSelectViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/assetselector/AssetSelectViewModel.kt @@ -52,7 +52,7 @@ class AssetSelectViewModel @Inject constructor( else -> null } } - .map { it.filterNotNull() } + .map { it.filterNotNull().filter { asset -> asset.enabled == true } } .mapList { mapAssetToAssetModel(it) } private val enteredTokenQueryFlow = MutableStateFlow("") diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/chainselector/ChainSelectViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/chainselector/ChainSelectViewModel.kt index 5da8af806c..8d7a466f41 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/chainselector/ChainSelectViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/balance/chainselector/ChainSelectViewModel.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -33,10 +34,10 @@ import jp.co.soramitsu.wallet.api.presentation.WalletRouter as WalletRouterApi class ChainSelectViewModel @Inject constructor( private val walletRouter: WalletRouter, private val walletInteractor: WalletInteractor, - chainInteractor: ChainInteractor, + private val chainInteractor: ChainInteractor, savedStateHandle: SavedStateHandle, private val sharedSendState: SendSharedState, - private val accountInteractor: AccountInteractor + private val accountInteractor: AccountInteractor, ) : BaseViewModel(), ChainSelectScreenContract { private val initialSelectedChainId: ChainId? = savedStateHandle[ChainSelectFragment.KEY_SELECTED_CHAIN_ID] @@ -83,17 +84,7 @@ class ChainSelectViewModel @Inject constructor( private val chainsFlow = allChainsFlow.map { chains -> when { initialSelectedAssetId != null -> { - chains.firstOrNull { - it.assets.any { asset -> asset.id == initialSelectedAssetId } - }?.let { chainOfTheAsset -> - val symbol = chainOfTheAsset.assets - .firstOrNull { it.id == initialSelectedAssetId } - ?.symbol - val chainsWithAsset = chains.filter { - it.assets.any { it.symbol == symbol } - } - chainsWithAsset - } + walletInteractor.observeCurrentAccountChainsPerAsset(initialSelectedAssetId).first().keys.toList() } filterChainIds.isNullOrEmpty() -> { chains From fb4275dc7203a9817c2dcd0528e8d26af018940b Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 17 May 2024 18:23:35 +0700 Subject: [PATCH 056/100] FLW-4595 fixed bug with turning off the send all toggle button --- .../wallet/impl/presentation/send/setup/SendSetupViewModel.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt index d3fde705be..15352b1d5f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/send/setup/SendSetupViewModel.kt @@ -855,6 +855,9 @@ class SendSetupViewModel @Inject constructor( if (checked) { onQuickAmountInput(1.0) + } else { + visibleAmountFlow.value = BigDecimal.ZERO + initialAmountFlow.value = BigDecimal.ZERO } } } From 8c26e46f72d02380d6f170b4d5eee9bf31dbd050 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 20 May 2024 17:14:04 +0700 Subject: [PATCH 057/100] Temporary disable feature with quick inputs --- .../common/compose/component/QuickInput.kt | 81 ++++++++++--------- .../bond/select/SelectBondMoreViewModel.kt | 18 +++-- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/common/src/main/java/jp/co/soramitsu/common/compose/component/QuickInput.kt b/common/src/main/java/jp/co/soramitsu/common/compose/component/QuickInput.kt index 31ef074515..be637cd867 100644 --- a/common/src/main/java/jp/co/soramitsu/common/compose/component/QuickInput.kt +++ b/common/src/main/java/jp/co/soramitsu/common/compose/component/QuickInput.kt @@ -42,46 +42,47 @@ fun QuickInput( onQuickAmountInput: (amount: Double) -> Unit = {}, onDoneClick: () -> Unit = {} ) { - val keyboardController = LocalSoftwareKeyboardController.current - Row( - modifier = modifier - .background(color = backgroundBlack.copy(alpha = 0.75f)) - .height(44.dp) - .padding(horizontal = 10.dp) - ) { - values.map { - Box( - modifier = Modifier - .fillMaxHeight() - .clickable { - onQuickAmountInput(it.value) - } - ) { - B1( - text = it.label, - modifier = Modifier - .align(Alignment.Center) - .padding(horizontal = 6.dp) - ) - } - } - Spacer(modifier = Modifier.weight(1f)) - Box( - modifier = Modifier - .fillMaxHeight() - .clickable { - keyboardController?.hide() - onDoneClick() - } - ) { - H5( - text = stringResource(id = R.string.common_done), - modifier = Modifier - .align(Alignment.Center) - .padding(horizontal = 6.dp) - ) - } - } + // todo temporary disable it till the business logic will be refactored +// val keyboardController = LocalSoftwareKeyboardController.current +// Row( +// modifier = modifier +// .background(color = backgroundBlack.copy(alpha = 0.75f)) +// .height(44.dp) +// .padding(horizontal = 10.dp) +// ) { +// values.map { +// Box( +// modifier = Modifier +// .fillMaxHeight() +// .clickable { +// onQuickAmountInput(it.value) +// } +// ) { +// B1( +// text = it.label, +// modifier = Modifier +// .align(Alignment.Center) +// .padding(horizontal = 6.dp) +// ) +// } +// } +// Spacer(modifier = Modifier.weight(1f)) +// Box( +// modifier = Modifier +// .fillMaxHeight() +// .clickable { +// keyboardController?.hide() +// onDoneClick() +// } +// ) { +// H5( +// text = stringResource(id = R.string.common_done), +// modifier = Modifier +// .align(Alignment.Center) +// .padding(horizontal = 6.dp) +// ) +// } +// } } private enum class TestQuickInput( diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/select/SelectBondMoreViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/select/SelectBondMoreViewModel.kt index 207c8c6e3d..dd35ae9343 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/select/SelectBondMoreViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/select/SelectBondMoreViewModel.kt @@ -41,8 +41,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch private const val DEFAULT_AMOUNT = 1 @@ -92,8 +92,7 @@ class SelectBondMoreViewModel @Inject constructor( .asLiveData() val enteredAmountFlow = MutableStateFlow(DEFAULT_AMOUNT.toString()) - - private val parsedAmountFlow = enteredAmountFlow.mapNotNull { it.toBigDecimalOrNull() } + private val decimalAmountFlow = MutableStateFlow(DEFAULT_AMOUNT.toBigDecimal()) val accountLiveData = stakingScenarioInteractor.getSelectedAccountAddress() .inBackground() @@ -103,7 +102,7 @@ class SelectBondMoreViewModel @Inject constructor( .inBackground() .asLiveData() - val enteredFiatAmountFlow = assetFlow.combine(parsedAmountFlow) { asset, amount -> + val enteredFiatAmountFlow = assetFlow.combine(decimalAmountFlow) { asset, amount -> asset.token.fiatAmount(amount)?.formatFiat(asset.token.fiatSymbol) } .inBackground() @@ -111,6 +110,12 @@ class SelectBondMoreViewModel @Inject constructor( init { listenFee() + enteredAmountFlow.onEach { stringValue -> + decimalAmountFlow.update { + // todo don't do like this, we must create a reversed formatter from String to BigDecimal + stringValue.replace(",", "").toBigDecimalOrNull() ?: it + } + }.launchIn(viewModelScope) } fun nextClicked() { @@ -127,7 +132,7 @@ class SelectBondMoreViewModel @Inject constructor( @OptIn(FlowPreview::class) private fun listenFee() { - parsedAmountFlow + decimalAmountFlow .debounce(DEBOUNCE_DURATION_MILLIS.toDuration(DurationUnit.MILLISECONDS)) .onEach { loadFee(it) } .launchIn(viewModelScope) @@ -157,7 +162,7 @@ class SelectBondMoreViewModel @Inject constructor( val payload = BondMoreValidationPayload( stashAddress = stashAddress(), fee = fee, - amount = parsedAmountFlow.first(), + amount = decimalAmountFlow.value, chainAsset = assetFlow.first().token.configuration ) @@ -244,6 +249,7 @@ class SelectBondMoreViewModel @Inject constructor( } enteredAmountFlow.emit(value.formatCrypto()) + decimalAmountFlow.update { value } } } } From bed6ba3e6ad9b547565c86cfa2363859f9262907 Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 21 May 2024 14:03:22 +0700 Subject: [PATCH 058/100] FLW-4576 fixed bug on NFT transfers --- .../impl/domain/AccountInteractorImpl.kt | 6 ++- .../impl/domain/NFTTransferInteractorImpl.kt | 34 +++++++------ .../usecase/eth/CreateRawEthTransaction.kt | 2 +- .../usecase/eth/EstimateEthTransactionGas.kt | 7 +-- ...kt => estimateEthTransactionNetworkFee.kt} | 28 +++++------ .../nft/impl/domain/utils/Web3AdapterExt.kt | 48 +------------------ .../impl/domain/WalletInteractorImpl.kt | 4 +- 7 files changed, 44 insertions(+), 85 deletions(-) rename feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/{EstimateEthTransactionNetworkFee.kt => estimateEthTransactionNetworkFee.kt} (58%) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/AccountInteractorImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/AccountInteractorImpl.kt index 1cd2d5bbd5..fb21a51fd4 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/AccountInteractorImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/AccountInteractorImpl.kt @@ -11,6 +11,7 @@ import jp.co.soramitsu.common.interfaces.FileProvider import jp.co.soramitsu.core.model.Language import jp.co.soramitsu.core.models.CryptoType import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flatMapLatest @@ -19,7 +20,8 @@ import kotlinx.coroutines.withContext class AccountInteractorImpl( private val accountRepository: AccountRepository, - private val fileProvider: FileProvider + private val fileProvider: FileProvider, + private val context: CoroutineContext = Dispatchers.Default ) : AccountInteractor { override suspend fun generateMnemonic(): List { @@ -224,7 +226,7 @@ class AccountInteractorImpl( override fun selectedMetaAccountFlow() = accountRepository.selectedMetaAccountFlow() - override suspend fun selectedMetaAccount() = accountRepository.getSelectedMetaAccount() + override suspend fun selectedMetaAccount() = withContext(context) { accountRepository.getSelectedMetaAccount() } override suspend fun selectedLightMetaAccount() = accountRepository.getSelectedLightMetaAccount() diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt index 004824d4bc..261972d8c1 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt @@ -8,13 +8,12 @@ import jp.co.soramitsu.common.data.secrets.v2.MetaAccountSecrets import jp.co.soramitsu.nft.domain.NFTTransferInteractor import jp.co.soramitsu.nft.domain.models.NFT import jp.co.soramitsu.nft.impl.domain.usecase.eth.CreateRawEthTransaction -import jp.co.soramitsu.nft.impl.domain.usecase.eth.EstimateEthTransactionNetworkFee +import jp.co.soramitsu.nft.impl.domain.usecase.eth.estimateEthTransactionNetworkFee import jp.co.soramitsu.nft.impl.domain.usecase.eth.ExecuteEthFunction import jp.co.soramitsu.nft.impl.domain.usecase.eth.SendRawEthTransaction import jp.co.soramitsu.nft.impl.domain.usecase.transfer.NFTAccountBalanceAdapter import jp.co.soramitsu.nft.impl.domain.usecase.transfer.NFTTransferAdapter import jp.co.soramitsu.nft.impl.domain.utils.nonNullWeb3j -import jp.co.soramitsu.nft.impl.domain.utils.subscribeNewHeads import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumChainConnection @@ -25,11 +24,18 @@ import kotlinx.coroutines.flow.transform import org.web3j.utils.Numeric import java.math.BigDecimal import java.math.BigInteger +import jp.co.soramitsu.wallet.impl.data.network.blockchain.subscribeBaseFeePerGas +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.withContext class NFTTransferInteractorImpl( private val accountRepository: AccountRepository, private val chainsRepository: ChainsRepository, - private val ethereumConnectionPool: EthereumConnectionPool + private val ethereumConnectionPool: EthereumConnectionPool, + private val coroutineContext: CoroutineContext = Dispatchers.Default ) : NFTTransferInteractor { private fun getWeb3Connection(chainId: ChainId): EthereumChainConnection { @@ -44,7 +50,7 @@ class NFTTransferInteractorImpl( token: NFT, receiver: String, canReceiverAcceptToken: Boolean - ): Flow> { + ): Flow> = withContext(coroutineContext) { val chain = chainsRepository.getChain(token.chainId) val connection = getWeb3Connection(chain.id) @@ -57,8 +63,7 @@ class NFTTransferInteractorImpl( """.trimIndent() ) } - - return connection.subscribeNewHeads().transform { newHead -> + return@withContext connection.subscribeBaseFeePerGas().filter { it != null }.transform { baseFeePerGas -> val nftTransfer = NFTTransferAdapter( web3j = connection.nonNullWeb3j, sender = senderResult.getOrThrow(), @@ -67,24 +72,25 @@ class NFTTransferInteractorImpl( canReceiverAcceptToken = canReceiverAcceptToken ) - val networkFee = connection.EstimateEthTransactionNetworkFee( + val networkFee = connection.estimateEthTransactionNetworkFee( call = nftTransfer, - baseFeePerGas = Numeric.decodeQuantity(newHead.params.result?.baseFeePerGas) + baseFeePerGas = baseFeePerGas ?: return@transform ) - emit(Result.success(networkFee)) - }.catch { emit(Result.failure(it)) } + }.catch { + emit(Result.failure(it)) + }.flowOn(Dispatchers.Default) } override suspend fun send( token: NFT, receiver: String, canReceiverAcceptToken: Boolean - ): Result { + ): Result = withContext(coroutineContext) { val chain = chainsRepository.getChain(token.chainId) val connection = getWeb3Connection(chain.id) - return runCatching { + runCatching { val ethereumSecrets = accountRepository.getMetaAccountSecrets( metaId = accountRepository.getSelectedMetaAccount().id @@ -124,11 +130,11 @@ class NFTTransferInteractorImpl( } } - override suspend fun balance(token: NFT): Result { + override suspend fun balance(token: NFT): Result = withContext(coroutineContext) { val chain = chainsRepository.getChain(token.chainId) val connection = getWeb3Connection(chain.id) - return runCatching { + runCatching { val sender = accountRepository.getSelectedMetaAccount().address(chain) ?: error( """ Currently selected account is unavailable now. diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/CreateRawEthTransaction.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/CreateRawEthTransaction.kt index 1ebacc3d70..0d8056a9ab 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/CreateRawEthTransaction.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/CreateRawEthTransaction.kt @@ -14,7 +14,7 @@ suspend fun EthereumChainConnection.CreateRawEthTransaction(call: EthCall): RawT EIP1559CallImpl.createAsync( ethConnection = this, call = call, - estimateGas = EstimateEthTransactionGas( + estimateGas = estimateEthTransactionGas( call = call ) ) diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionGas.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionGas.kt index f9dc28290e..a8b26df6d7 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionGas.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionGas.kt @@ -9,11 +9,8 @@ import org.web3j.protocol.core.methods.request.Transaction import org.web3j.utils.Numeric import java.math.BigInteger -@Suppress("FunctionName") -suspend fun EthereumChainConnection.EstimateEthTransactionGas(call: EthCall): BigInteger { - val response = nonNullWeb3j.ethEstimateGas( - call.convertToWeb3Transaction() - ).sendAsync().await() +suspend fun EthereumChainConnection.estimateEthTransactionGas(call: EthCall): BigInteger { + val response = nonNullWeb3j.ethEstimateGas(call.convertToWeb3Transaction()).sendAsync().await() return response.map { Numeric.decodeQuantity(it) } } diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionNetworkFee.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt similarity index 58% rename from feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionNetworkFee.kt rename to feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt index 0b3037ae35..55528fe013 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/EstimateEthTransactionNetworkFee.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt @@ -9,26 +9,24 @@ import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumChainConnection import java.math.BigDecimal import java.math.BigInteger -@Suppress("FunctionName", "UseIfInsteadOfWhen") -suspend fun EthereumChainConnection.EstimateEthTransactionNetworkFee( +suspend fun EthereumChainConnection.estimateEthTransactionNetworkFee( call: EthCall, baseFeePerGas: BigInteger ): BigDecimal { - val eip1559Transfer = when (call) { - is EthCall.SmartContractCall -> - EIP1559CallImpl.createAsync( - ethConnection = this, - call = call, - baseFeePerGas = baseFeePerGas, - estimateGas = EstimateEthTransactionGas( - call = call - ) - ) + val eip1559Transfer = if (call is EthCall.SmartContractCall) { + val estimateGas = estimateEthTransactionGas(call = call) - else -> error( + EIP1559CallImpl.createAsync( + ethConnection = this, + call = call, + baseFeePerGas = baseFeePerGas, + estimateGas = estimateGas + ) + } else { + error( """ - Unknown transfer type. - """.trimIndent() + Unknown transfer type. + """.trimIndent() ) } diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt index 6eb11a515a..511bcf0c6d 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt @@ -1,16 +1,12 @@ package jp.co.soramitsu.nft.impl.domain.utils import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumChainConnection -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.future.await -import kotlinx.coroutines.reactive.asFlow import org.web3j.protocol.Web3j import org.web3j.protocol.Web3jService import org.web3j.protocol.core.DefaultBlockParameterName import org.web3j.protocol.core.Request import org.web3j.protocol.core.Response -import org.web3j.protocol.core.methods.response.EthSubscribe -import org.web3j.protocol.websocket.events.Notification import org.web3j.utils.Numeric import java.math.BigInteger @@ -49,8 +45,7 @@ inline fun Response.map(crossinline transform: (T) -> K): K { } suspend fun Web3j.getNonce(address: String): BigInteger { - val response = ethGetTransactionCount(address, DefaultBlockParameterName.PENDING) - .sendAsync().await() + val response = ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).sendAsync().await() return response.map { Numeric.decodeQuantity(it) } } @@ -79,43 +74,4 @@ suspend fun Web3j.getBaseFee(): BigInteger { ).sendAsync().await() return response.map { Numeric.decodeQuantity(it.baseFeePerGas) } -} - -fun EthereumChainConnection.subscribeNewHeads(): Flow { - return nonNullWeb3jService.subscribe( - Request( - // method - "eth_subscribe", - // params - listOf("newHeads"), - // web3jSocket - service, - // type - EthSubscribe::class.java - ), - "eth_unsubscribe", - NewHeadsNotificationExtended::class.java - ).asFlow() -} - -class NewHeadsNotificationExtended : - Notification() - -class NewHeadExtended { - var difficulty: String? = null - var extraData: String? = null - var gasLimit: String? = null - var gasUsed: String? = null - var hash: String? = null - var logsBloom: String? = null - var miner: String? = null - var nonce: String? = null - var number: String? = null - var parentHash: String? = null - var receiptRoot: String? = null - var sha3Uncles: String? = null - var stateRoot: String? = null - var timestamp: String? = null - var transactionRoot: String? = null - var baseFeePerGas: String? = null -} +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt index 3fb8bc533e..831e09f748 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/domain/WalletInteractorImpl.kt @@ -167,11 +167,11 @@ class WalletInteractorImpl( return kotlin.runCatching { getCurrentAssetOrNull(chainId, chainAssetId)!! }.requireValue() } - override suspend fun getCurrentAssetOrNull(chainId: ChainId, chainAssetId: String): Asset? { + override suspend fun getCurrentAssetOrNull(chainId: ChainId, chainAssetId: String): Asset? = withContext(coroutineContext) { val metaAccount = accountRepository.getSelectedMetaAccount() val (chain, chainAsset) = chainsRepository.chainWithAsset(chainId, chainAssetId) - return walletRepository.getAsset( + return@withContext walletRepository.getAsset( metaAccount.id, metaAccount.accountId(chain)!!, chainAsset, From 67162de8331f8bf0b58d653b28f96cf0fe05f431 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 17 May 2024 10:49:27 +0500 Subject: [PATCH 059/100] fix test; clean code --- .../co/soramitsu/ChainSyncIntegrationTest.kt | 60 ------------------- .../app/root/navigation/Navigator.kt | 17 ------ .../data/OnboardingStoriesDataSource.kt | 42 ------------- .../domain/GetEducationalStoriesUseCase.kt | 7 --- .../ShouldShowEducationalStoriesUseCase.kt | 17 ------ .../jp/co/soramitsu/coredb/dao/Helpers.kt | 3 +- .../coredb/dao/MetaAccountDaoTest.kt | 6 +- .../coredb/migrations/V2MigrationTest.kt | 3 +- .../account/impl/di/AccountFeatureModule.kt | 21 ------- .../crowdloan/ExampleInstrumentedTest.kt | 24 -------- .../jp/co/soramitsu/splash/SplashRouter.kt | 6 -- .../splash/presentation/SplashFragment.kt | 1 - .../splash/presentation/SplashViewModel.kt | 41 ------------- .../wallet/impl/presentation/WalletRouter.kt | 3 - 14 files changed, 8 insertions(+), 243 deletions(-) delete mode 100644 app/src/androidTest/java/jp/co/soramitsu/ChainSyncIntegrationTest.kt delete mode 100644 common/src/main/java/jp/co/soramitsu/common/data/OnboardingStoriesDataSource.kt delete mode 100644 common/src/main/java/jp/co/soramitsu/common/domain/GetEducationalStoriesUseCase.kt delete mode 100644 common/src/main/java/jp/co/soramitsu/common/domain/ShouldShowEducationalStoriesUseCase.kt delete mode 100644 feature-crowdloan-impl/src/androidTest/java/jp/co/soramitsu/crowdloan/ExampleInstrumentedTest.kt diff --git a/app/src/androidTest/java/jp/co/soramitsu/ChainSyncIntegrationTest.kt b/app/src/androidTest/java/jp/co/soramitsu/ChainSyncIntegrationTest.kt deleted file mode 100644 index 0f3296e43f..0000000000 --- a/app/src/androidTest/java/jp/co/soramitsu/ChainSyncIntegrationTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package jp.co.soramitsu - -import androidx.room.Room -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import dagger.Component -import jp.co.soramitsu.common.data.network.NetworkApiCreator -import jp.co.soramitsu.common.di.CommonApi -import jp.co.soramitsu.common.di.FeatureContainer -import jp.co.soramitsu.coredb.AppDatabase -import jp.co.soramitsu.runtime.multiNetwork.chain.ChainSyncService -import jp.co.soramitsu.runtime.multiNetwork.chain.remote.ChainFetcher -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import javax.inject.Inject - - -@Component( - dependencies = [ - CommonApi::class, - ] -) -interface TestAppComponent { - - fun inject(test: ChainSyncServiceIntegrationTest) -} - -@RunWith(AndroidJUnit4::class) -class ChainSyncServiceIntegrationTest { - - private val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext - private val featureContainer = context as FeatureContainer - - @Inject - lateinit var networkApiCreator: NetworkApiCreator - - lateinit var chainSyncService: ChainSyncService - - @Before - fun setup() { - val component = DaggerTestAppComponent.builder() - .commonApi(featureContainer.commonApi()) - .build() - - component.inject(this) - - val chainDao = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) - .build() - .chainDao() - - chainSyncService = ChainSyncService(chainDao, networkApiCreator.create(ChainFetcher::class.java)) - } - - @Test - fun shouldFetchAndStoreRealChains() = runBlocking { - chainSyncService.syncUp() - } -} diff --git a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt index ddbd497b94..d583a5781c 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt @@ -177,7 +177,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onCompletion @@ -185,7 +184,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.job import kotlinx.parcelize.Parcelize -import jp.co.soramitsu.common.utils.combine as combineLiveData @Parcelize class NavComponentDelayedNavigation(val globalActionId: Int, val extras: Bundle? = null) : DelayedNavigation @@ -1282,10 +1280,6 @@ class Navigator : return PincodeFragment.getPinCodeBundle(action) } - override fun openEducationalStories(stories: StoryGroupModel) { - navController?.navigate(R.id.action_splash_to_stories, StoryFragment.getBundle(stories)) - } - override fun openSelectWallet() { navController?.navigate(R.id.selectWalletFragment) } @@ -1341,17 +1335,6 @@ class Navigator : navController?.navigateUp() } - override val educationalStoriesCompleted: Flow - get() { - return combineLiveData( - navController?.currentBackStackEntry?.lifecycle?.onResumeObserver() ?: return flowOf(false), - navController?.currentBackStackEntry?.savedStateHandle?.getLiveData(StoryFragment.KEY_STORY) ?: return flowOf(false), - combiner = { (isResumed: Boolean, storiesCompleted: Boolean) -> - isResumed && storiesCompleted - } - ).asFlow() - } - override fun openExperimentalFeatures() { navController?.navigate(R.id.experimentalFragment) } diff --git a/common/src/main/java/jp/co/soramitsu/common/data/OnboardingStoriesDataSource.kt b/common/src/main/java/jp/co/soramitsu/common/data/OnboardingStoriesDataSource.kt deleted file mode 100644 index dfecdd7648..0000000000 --- a/common/src/main/java/jp/co/soramitsu/common/data/OnboardingStoriesDataSource.kt +++ /dev/null @@ -1,42 +0,0 @@ -package jp.co.soramitsu.common.data - -import jp.co.soramitsu.common.domain.model.StoryGroup -import jp.co.soramitsu.common.R - -class OnboardingStoriesDataSource { - val stories: StoryGroup.Onboarding - get() = StoryGroup.Onboarding( - listOf( - StoryGroup.Story.Onboarding( - R.string.stories_version2_slide1_title, - R.string.stories_version2_slide1_subtitle, - R.drawable.background_story_networks, - null - ), - StoryGroup.Story.Onboarding( - R.string.stories_version2_slide2_title, - R.string.stories_version2_slide2_subtitle, - R.drawable.background_story_wallet, - null - ), - StoryGroup.Story.Onboarding( - R.string.stories_version2_slide3_title, - R.string.stories_version2_slide3_subtitle, - R.drawable.background_story_networks_2, - null - ), - StoryGroup.Story.Onboarding( - R.string.stories_version2_slide4_title, - R.string.stories_version2_slide4_subtitle, - R.drawable.background_story_chain_accounts, - null - ), - StoryGroup.Story.Onboarding( - R.string.stories_version2_slide5_title, - R.string.stories_version2_slide5_subtitle, - R.drawable.background_story_ecosystem, - R.string.stories_bottom_close_button - ) - ) - ) -} diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/GetEducationalStoriesUseCase.kt b/common/src/main/java/jp/co/soramitsu/common/domain/GetEducationalStoriesUseCase.kt deleted file mode 100644 index a0d9d05ff9..0000000000 --- a/common/src/main/java/jp/co/soramitsu/common/domain/GetEducationalStoriesUseCase.kt +++ /dev/null @@ -1,7 +0,0 @@ -package jp.co.soramitsu.common.domain - -import jp.co.soramitsu.common.data.OnboardingStoriesDataSource - -class GetEducationalStoriesUseCase(private val storiesDataSource: OnboardingStoriesDataSource) { - operator fun invoke() = storiesDataSource.stories -} diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/ShouldShowEducationalStoriesUseCase.kt b/common/src/main/java/jp/co/soramitsu/common/domain/ShouldShowEducationalStoriesUseCase.kt deleted file mode 100644 index cb0ccf1c4d..0000000000 --- a/common/src/main/java/jp/co/soramitsu/common/domain/ShouldShowEducationalStoriesUseCase.kt +++ /dev/null @@ -1,17 +0,0 @@ -package jp.co.soramitsu.common.domain - -import jp.co.soramitsu.common.data.storage.Preferences -import kotlin.reflect.KProperty - -class ShouldShowEducationalStoriesUseCase(private val preferences: Preferences) { - - private val key = "shouldShowEducationalStories" - - operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return preferences.getBoolean(key, true) - } - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { - preferences.putBoolean(key, value) - } -} diff --git a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/Helpers.kt b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/Helpers.kt index b505a30c5f..b93321504d 100644 --- a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/Helpers.kt +++ b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/Helpers.kt @@ -45,7 +45,8 @@ fun chainOf( paraId = null, rank = null, isChainlinkProvider = false, - supportNft = false + supportNft = false, + isUsesAppId = false ) fun ChainLocal.nodeOf( diff --git a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/MetaAccountDaoTest.kt b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/MetaAccountDaoTest.kt index de463f006d..4114489016 100644 --- a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/MetaAccountDaoTest.kt +++ b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/dao/MetaAccountDaoTest.kt @@ -81,7 +81,8 @@ class MetaAccountDaoTest : DaoTest(AppDatabase::metaAccountDao) ethereumAddress = null, position = 0, googleBackupAddress = null, - isBackedUp = false + isBackedUp = false, + initialized = false ) private fun chainAccount(metaId: Long) = ChainAccountLocal( @@ -90,6 +91,7 @@ class MetaAccountDaoTest : DaoTest(AppDatabase::metaAccountDao) publicKey = byteArrayOf(), cryptoType = CryptoType.SR25519, accountId = byteArrayOf(), - name = "" + name = "", + initialized = true ) } diff --git a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt index c99ccc8f1f..3e6fe6f0cf 100644 --- a/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt +++ b/core-db/src/androidTest/java/jp/co/soramitsu/coredb/migrations/V2MigrationTest.kt @@ -240,7 +240,8 @@ class V2MigrationTest { isSelected = getInt(getColumnIndex(Column.IS_SELECTED)) == 1, position = getInt(getColumnIndex(Column.POSITION)), isBackedUp = false, - googleBackupAddress = null + googleBackupAddress = null, + initialized = true ) metaAccount.id = getLong(getColumnIndex(Column.ID)) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt index b654e8411b..3d68a6fbd9 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/di/AccountFeatureModule.kt @@ -26,7 +26,6 @@ import jp.co.soramitsu.account.impl.domain.NodeHostValidator import jp.co.soramitsu.account.impl.domain.account.details.AccountDetailsInteractor import jp.co.soramitsu.account.impl.presentation.common.mixin.api.CryptoTypeChooserMixin import jp.co.soramitsu.account.impl.presentation.common.mixin.impl.CryptoTypeChooser -import jp.co.soramitsu.common.data.OnboardingStoriesDataSource import jp.co.soramitsu.common.data.network.AppLinksProvider import jp.co.soramitsu.common.data.network.NetworkApiCreator import jp.co.soramitsu.common.data.network.coingecko.CoingeckoApi @@ -35,9 +34,7 @@ import jp.co.soramitsu.common.data.secrets.v2.SecretStoreV2 import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.data.storage.encrypt.EncryptedPreferences import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies -import jp.co.soramitsu.common.domain.GetEducationalStoriesUseCase import jp.co.soramitsu.common.domain.SelectedFiat -import jp.co.soramitsu.common.domain.ShouldShowEducationalStoriesUseCase import jp.co.soramitsu.common.interfaces.FileProvider import jp.co.soramitsu.common.resources.ClipboardManager import jp.co.soramitsu.common.resources.LanguagesHolder @@ -51,7 +48,6 @@ import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.shared_utils.encrypt.json.JsonSeedDecoder import jp.co.soramitsu.shared_utils.encrypt.json.JsonSeedEncoder -import jp.co.soramitsu.wallet.api.data.cache.AssetCache @InstallIn(SingletonComponent::class) @Module @@ -214,23 +210,6 @@ class AccountFeatureModule { return AssetNotNeedAccountUseCaseImpl(chainRegistry, assetDao, tokenPriceDao, selectedFiat) } - @Provides - fun provideStoriesDataSource() = OnboardingStoriesDataSource() - - @Provides - fun provideShouldShowEducationalStories( - preferences: Preferences - ): ShouldShowEducationalStoriesUseCase { - return ShouldShowEducationalStoriesUseCase(preferences) - } - - @Provides - fun provideGetEducationalStories( - onboardingStoriesDataSource: OnboardingStoriesDataSource - ): GetEducationalStoriesUseCase { - return GetEducationalStoriesUseCase(onboardingStoriesDataSource) - } - @Provides fun provideBeaconConnectedUseCase(preferences: Preferences): BeaconConnectedUseCase { return BeaconConnectedUseCase(preferences) diff --git a/feature-crowdloan-impl/src/androidTest/java/jp/co/soramitsu/crowdloan/ExampleInstrumentedTest.kt b/feature-crowdloan-impl/src/androidTest/java/jp/co/soramitsu/crowdloan/ExampleInstrumentedTest.kt deleted file mode 100644 index 56ec86f53b..0000000000 --- a/feature-crowdloan-impl/src/androidTest/java/jp/co/soramitsu/crowdloan/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package jp.co.soramitsu.crowdloan - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("jp.co.soramitsu.feature_crowdloan_impl.test", appContext.packageName) - } -} diff --git a/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt b/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt index d3a64fedbc..2f19596067 100644 --- a/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt +++ b/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt @@ -1,18 +1,12 @@ package jp.co.soramitsu.splash import jp.co.soramitsu.common.navigation.SecureRouter -import jp.co.soramitsu.common.presentation.StoryGroupModel -import kotlinx.coroutines.flow.Flow interface SplashRouter : SecureRouter { - val educationalStoriesCompleted: Flow - fun openAddFirstAccount() fun openCreatePincode() fun openInitialCheckPincode() - - fun openEducationalStories(stories: StoryGroupModel) } diff --git a/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashFragment.kt b/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashFragment.kt index 75f8ba95b7..edeada00be 100644 --- a/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashFragment.kt +++ b/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashFragment.kt @@ -26,7 +26,6 @@ class SplashFragment : BaseFragment() { } override fun subscribe(viewModel: SplashViewModel) { -// viewModel.checkStories() viewModel.openInitialDestination() } } diff --git a/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt b/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt index 9d588b7eba..167c83e2b4 100644 --- a/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt +++ b/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt @@ -6,11 +6,6 @@ import javax.inject.Inject import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.common.base.BaseViewModel -import jp.co.soramitsu.common.domain.GetEducationalStoriesUseCase -import jp.co.soramitsu.common.domain.ShouldShowEducationalStoriesUseCase -import jp.co.soramitsu.common.domain.model.StoryGroup -import jp.co.soramitsu.common.presentation.StoryElement -import jp.co.soramitsu.common.presentation.StoryGroupModel import jp.co.soramitsu.splash.SplashRouter import kotlinx.coroutines.launch @@ -18,35 +13,9 @@ import kotlinx.coroutines.launch class SplashViewModel @Inject constructor( private val router: SplashRouter, private val repository: AccountRepository, - shouldShowEducationalStoriesUseCase: ShouldShowEducationalStoriesUseCase, - private val getEducationalStories: GetEducationalStoriesUseCase, private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario ) : BaseViewModel() { - private var shouldShowEducationalStories by shouldShowEducationalStoriesUseCase - - fun checkStories() { - launch { - when { - repository.isAccountSelected() -> openInitialDestination() - shouldShowEducationalStories -> { - listenForStories() - val stories = getEducationalStories().transform() - router.openEducationalStories(stories) - shouldShowEducationalStories = false - } - else -> openInitialDestination() - } - } - } - - private fun StoryGroup.Onboarding.transform() = - StoryGroupModel( - this.elements.map { - StoryElement.Onboarding(it.titleRes, it.bodyRes, it.image, it.buttonCaptionRes) - } - ) - fun openInitialDestination() { viewModelScope.launch { pendulumPreInstalledAccountsScenario.fetchFeatureToggle() @@ -61,14 +30,4 @@ class SplashViewModel @Inject constructor( } } } - - private fun listenForStories() { - viewModelScope.launch { - router.educationalStoriesCompleted.collect { - if (it) { - openInitialDestination() - } - } - } - } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt index 70a6ccc270..e4ac7a2664 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/WalletRouter.kt @@ -10,7 +10,6 @@ import jp.co.soramitsu.common.navigation.DelayedNavigation import jp.co.soramitsu.common.navigation.PinRequired import jp.co.soramitsu.common.navigation.SecureRouter import jp.co.soramitsu.common.navigation.payload.WalletSelectorPayload -import jp.co.soramitsu.common.presentation.StoryGroupModel import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.wallet.api.domain.model.XcmChainType @@ -133,8 +132,6 @@ interface WalletRouter : SecureRouter, WalletRouterApi { fun openOnboardingNavGraph(chainId: ChainId, metaId: Long, isImport: Boolean) - fun openEducationalStories(stories: StoryGroupModel) - fun openSuccessFragment(avatar: Drawable) fun openTransactionRawData(rawData: String) From f18688d8e40aa80f0cac0fa1ddb6ce5dd5e9c2fd Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 17 May 2024 11:06:52 +0500 Subject: [PATCH 060/100] use new url and data format for welcome slides --- .../co/soramitsu/common/domain/AppVersion.kt | 15 ++++-- .../onboarding/api/data/OnboardingFlow.kt | 29 ++++++++--- feature-onboarding-impl/build.gradle | 2 +- .../impl/data/DeserializationExt.kt | 49 ++++++++++++++----- .../impl/di/OnboardingFeatureModule.kt | 6 ++- .../impl/welcome/OnboardingScreen.kt | 40 +++++++++------ .../impl/welcome/WelcomeFragment.kt | 3 +- .../impl/welcome/WelcomeViewModel.kt | 32 ++++++++++-- 8 files changed, 128 insertions(+), 48 deletions(-) diff --git a/common/src/main/java/jp/co/soramitsu/common/domain/AppVersion.kt b/common/src/main/java/jp/co/soramitsu/common/domain/AppVersion.kt index 3a25005619..ad5a95475c 100644 --- a/common/src/main/java/jp/co/soramitsu/common/domain/AppVersion.kt +++ b/common/src/main/java/jp/co/soramitsu/common/domain/AppVersion.kt @@ -2,15 +2,15 @@ package jp.co.soramitsu.common.domain import jp.co.soramitsu.common.BuildConfig -data class AppVersion(val major: Int, val minor: Int, val buildNum: Int) { +data class AppVersion(val major: Int, val minor: Int, val buildNum: Int) : Comparable { companion object { fun fromString(appVersionName: String): AppVersion { return appVersionName.split(".").let { AppVersion( - major = it[0].toIntOrNull() ?: 0, - minor = it[1].toIntOrNull() ?: 0, - buildNum = it[2].toIntOrNull() ?: 0 + major = it.getOrNull(0)?.toIntOrNull() ?: 0, + minor = it.getOrNull(1)?.toIntOrNull() ?: 0, + buildNum = it.getOrNull(2)?.toIntOrNull() ?: 0 ) } } @@ -30,6 +30,13 @@ data class AppVersion(val major: Int, val minor: Int, val buildNum: Int) { } } } + + override fun compareTo(other: AppVersion): Int = when { + major != other.major -> major - other.major + minor != other.minor -> minor - other.minor + buildNum != other.buildNum -> buildNum - other.buildNum + else -> 0 + } } // "2.0.1".before("2.0.1") false diff --git a/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/data/OnboardingFlow.kt b/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/data/OnboardingFlow.kt index 5eed58ffd0..5137e4f05a 100644 --- a/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/data/OnboardingFlow.kt +++ b/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/data/OnboardingFlow.kt @@ -1,17 +1,30 @@ package jp.co.soramitsu.onboarding.api.data data class OnboardingConfig( - val en_EN: Variants + val configs: List ) { - class Variants( - val new: List, - val regular: List + data class OnboardingConfigItem( + val minVersion: String, + val background: String, + val enEn: Variants ) { - class ScreenInfo( - val title: String, - val description: String, - val image: String + class Variants( + val new: List, + val regular: List ) { + class ScreenInfo( + val title: TitleInfo, + val description: String, + val image: String + ) { + class TitleInfo( + val text: String, + val color: String + ) { + companion object; + } + companion object; + } companion object; } companion object; diff --git a/feature-onboarding-impl/build.gradle b/feature-onboarding-impl/build.gradle index 2cf5813719..2181613ce9 100644 --- a/feature-onboarding-impl/build.gradle +++ b/feature-onboarding-impl/build.gradle @@ -18,7 +18,7 @@ android { buildTypes { debug { - buildConfigField "String", "ONBOARDING_CONFIG", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/develop-free/appConfigs/onboarding/mobile.json\"" + buildConfigField "String", "ONBOARDING_CONFIG", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/develop-free/appConfigs/onboarding/mobile%20v2.json\"" } release { diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/data/DeserializationExt.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/data/DeserializationExt.kt index dceabf0002..9cd8dcda79 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/data/DeserializationExt.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/data/DeserializationExt.kt @@ -8,37 +8,64 @@ val OnboardingConfig.Companion.deserializer: JsonDeserializer val jsonObj = json.asJsonObject return@JsonDeserializer OnboardingConfig( - en_EN = context.deserialize( - jsonObj.get("en-EN"), OnboardingConfig.Variants::class.java + configs = jsonObj.get("Android")?.asJsonArray?.mapNotNull { jsonElem -> + context?.deserialize( + jsonElem, OnboardingConfig.OnboardingConfigItem::class.java + ) + } ?: emptyList(), + ) + } + +val OnboardingConfig.OnboardingConfigItem.Companion.deserializer: JsonDeserializer + get() = JsonDeserializer { json, typeOfT, context -> + val jsonObj = json.asJsonObject + + return@JsonDeserializer OnboardingConfig.OnboardingConfigItem( + minVersion = jsonObj.get("minVersion").asString, + background = jsonObj.get("background").asString, + enEn = context.deserialize( + jsonObj.get("en-EN"), OnboardingConfig.OnboardingConfigItem.Variants::class.java ) ) } -val OnboardingConfig.Variants.Companion.deserializer: JsonDeserializer +val OnboardingConfig.OnboardingConfigItem.Variants.Companion.deserializer: JsonDeserializer get() = JsonDeserializer { json, typeOfT, context -> val jsonObj = json.asJsonObject - return@JsonDeserializer OnboardingConfig.Variants( + return@JsonDeserializer OnboardingConfig.OnboardingConfigItem.Variants( new = jsonObj.get("new")?.asJsonArray?.mapNotNull { jsonElem -> - context?.deserialize( - jsonElem, OnboardingConfig.Variants.ScreenInfo::class.java + context?.deserialize( + jsonElem, OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo::class.java ) } ?: emptyList(), regular = jsonObj.get("regular")?.asJsonArray?.mapNotNull { jsonElem -> - context?.deserialize( - jsonElem, OnboardingConfig.Variants.ScreenInfo::class.java + context?.deserialize( + jsonElem, OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo::class.java ) } ?: emptyList() ) } -val OnboardingConfig.Variants.ScreenInfo.Companion.deserializer: JsonDeserializer +val OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.Companion.deserializer: JsonDeserializer get() = JsonDeserializer { json, typeOfT, context -> val jsonObj = json.asJsonObject - return@JsonDeserializer OnboardingConfig.Variants.ScreenInfo( - title = jsonObj.get("title").asString, + return@JsonDeserializer OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo( + title = context.deserialize( + jsonObj.get("title"), OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo::class.java + ), description = jsonObj.get("description").asString, image = jsonObj.get("image").asString ) + } + +val OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo.Companion.deserializer: JsonDeserializer + get() = JsonDeserializer { json, typeOfT, context -> + val jsonObj = json.asJsonObject + + return@JsonDeserializer OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo( + text = jsonObj.get("text").asString, + color = jsonObj.get("color").asString + ) } \ No newline at end of file diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt index 5e9ecc04d2..a58fc7b799 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt @@ -29,8 +29,10 @@ class OnboardingFeatureModule { OnboardingConfigApi::class.java, typeAdapters = mapOf( OnboardingConfig::class.java to OnboardingConfig.deserializer, - OnboardingConfig.Variants::class.java to OnboardingConfig.Variants.deserializer, - OnboardingConfig.Variants.ScreenInfo::class.java to OnboardingConfig.Variants.ScreenInfo.deserializer, + OnboardingConfig.OnboardingConfigItem::class.java to OnboardingConfig.OnboardingConfigItem.deserializer, + OnboardingConfig.OnboardingConfigItem.Variants::class.java to OnboardingConfig.OnboardingConfigItem.Variants.deserializer, + OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo::class.java to OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.deserializer, + OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo::class.java to OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo.deserializer, ) ) diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt index 6b13496018..5dd8a17a2d 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt @@ -45,6 +45,7 @@ import jp.co.soramitsu.common.compose.component.MenuIconItem import jp.co.soramitsu.common.compose.component.Toolbar import jp.co.soramitsu.common.compose.component.ToolbarViewState import jp.co.soramitsu.common.compose.theme.FearlessAppTheme +import jp.co.soramitsu.common.compose.theme.colorFromHex import jp.co.soramitsu.common.compose.theme.white import jp.co.soramitsu.common.compose.theme.white20 import jp.co.soramitsu.common.compose.theme.white60 @@ -57,8 +58,8 @@ import kotlinx.coroutines.launch @Immutable @JvmInline value class OnboardingFlow( - private val flow: List -): List by flow + private val flow: List +): List by flow @Stable interface OnboardingScreenCallback { @@ -73,13 +74,16 @@ interface OnboardingScreenCallback { @Suppress("FunctionName") fun NavGraphBuilder.OnboardingScreen( + backgroundImageFlow: StateFlow, onboardingStateFlow: StateFlow, callback: OnboardingScreenCallback ) { composable(WelcomeEvent.Onboarding.PagerScreen.route) { val onboardingFlow by onboardingStateFlow.collectAsState() + val backgroundImageUrl by backgroundImageFlow.collectAsState() OnboardingScreenContent( + background = backgroundImageUrl, onboardingFlow = onboardingFlow, callback = callback ) @@ -89,6 +93,7 @@ fun NavGraphBuilder.OnboardingScreen( @OptIn(ExperimentalFoundationApi::class) @Composable private fun OnboardingScreenContent( + background: String?, onboardingFlow: OnboardingFlow?, callback: OnboardingScreenCallback ) { @@ -118,11 +123,17 @@ private fun OnboardingScreenContent( val coroutineScope = rememberCoroutineScope() + val backgroundPainter = if (background.isNullOrBlank()) { + painterResource(R.drawable.drawable_background_image) + } else { + rememberAsyncImagePainter(model = background) + } + Column( modifier = Modifier .fillMaxSize() .paint( - painter = painterResource(R.drawable.drawable_background_image), + painter = backgroundPainter, contentScale = ContentScale.FillWidth ), verticalArrangement = Arrangement.spacedBy(16.dp), @@ -155,8 +166,8 @@ private fun OnboardingScreenContent( horizontalAlignment = Alignment.CenterHorizontally ) { H1( - text = onboardingFlow[it].title, - color = white, + text = onboardingFlow[it].title.text, + color = runCatching { onboardingFlow[it].title.color.colorFromHex() }.getOrNull() ?: white, textAlign = TextAlign.Center ) @@ -232,30 +243,29 @@ private fun OnboardingScreenContent( private fun OnboardingScreenPreview() { FearlessAppTheme { OnboardingScreenContent( - OnboardingFlow( + background = "", + onboardingFlow = OnboardingFlow( listOf( - OnboardingConfig.Variants.ScreenInfo( - title = "Brand new network management", + OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo( + title = OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo("Brand new network management", "#ee0077"), description = "Navigate between All, Popular and your Favourite networks modes", image = "${R.drawable.drawable_background_image}" ), - OnboardingConfig.Variants.ScreenInfo( - title = "Title1", + OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo( + title = OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo("Title1", ""), description = "Description1", image = "image1" ), - OnboardingConfig.Variants.ScreenInfo( - title = "Title2", + OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo( + title = OnboardingConfig.OnboardingConfigItem.Variants.ScreenInfo.TitleInfo("Title2", ""), description = "Description2", image = "image2" ) ) ), - object : OnboardingScreenCallback { + callback = object : OnboardingScreenCallback { override fun onClose() = Unit - override fun onNext() = Unit - override fun onSkip() = Unit } ) diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt index 9f819e577d..34b9032e7a 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt @@ -74,8 +74,6 @@ class WelcomeFragment : BaseComposeFragment() { super.onViewCreated(view, savedInstanceState) observeBrowserEvents(viewModel) - - } private fun handleAuthorizeGoogleEvent() { @@ -122,6 +120,7 @@ class WelcomeFragment : BaseComposeFragment() { ) OnboardingScreen( + backgroundImageFlow = viewModel.onboardingBackground, onboardingStateFlow = viewModel.onboardingFlowState, callback = viewModel ) diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt index 8d578d6a1e..ad9a96172f 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt @@ -13,8 +13,10 @@ import jp.co.soramitsu.account.api.domain.model.ImportMode import jp.co.soramitsu.backup.BackupService import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.data.network.AppLinksProvider +import jp.co.soramitsu.common.domain.AppVersion import jp.co.soramitsu.common.mixin.api.Browserable import jp.co.soramitsu.common.utils.Event +import jp.co.soramitsu.onboarding.api.data.OnboardingConfig import jp.co.soramitsu.onboarding.api.domain.OnboardingInteractor import jp.co.soramitsu.onboarding.impl.OnboardingRouter import jp.co.soramitsu.onboarding.impl.welcome.WelcomeFragment.Companion.KEY_PAYLOAD @@ -44,6 +46,10 @@ class WelcomeViewModel @Inject constructor( private val payload = savedStateHandle.get(KEY_PAYLOAD)!! + private val _onboardingBackgroundState = MutableStateFlow(null) + val onboardingBackground = _onboardingBackgroundState + .stateIn(viewModelScope, SharingStarted.Eagerly, null) + private val _onboardingFlowState = MutableStateFlow?>(null) val onboardingFlowState = _onboardingFlowState.map { it?.getOrNull() } .stateIn(viewModelScope, SharingStarted.Eagerly, null) @@ -72,14 +78,30 @@ class WelcomeViewModel @Inject constructor( } viewModelScope.launch { - onboardingInteractor.getConfig() + val remoteOnboardingConfig: OnboardingConfig? = onboardingInteractor.getConfig() .onFailure { Log.e("OnboardingScreen", "onboardingInteractor.getConfig() failed: $it") showError(it) - } - .map { OnboardingFlow(it.en_EN.new) }.let { - _onboardingFlowState.value = it - } + }.getOrNull() + + val useConfig = getAppVersionSupportedConfig(remoteOnboardingConfig) + + if (useConfig == null) { + _onboardingFlowState.value = Result.failure(IllegalStateException("Onboarding config is empty")) + } else { + _onboardingFlowState.value = Result.success(OnboardingFlow(useConfig.enEn.new)) + _onboardingBackgroundState.value = useConfig.background + } + } + } + + private fun getAppVersionSupportedConfig(config: OnboardingConfig?): OnboardingConfig.OnboardingConfigItem? { + val current = AppVersion.current() + return config?.configs?.filter { + val configAppVersion = AppVersion.fromString(it.minVersion) + configAppVersion.major == current.major && configAppVersion.minor == current.minor + }?.maxByOrNull { + AppVersion.fromString(it.minVersion) } } From 9c897df663ffcd2df175389a7beb07a66a58781c Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 17 May 2024 17:25:45 +0500 Subject: [PATCH 061/100] show regular slides on update --- .../app/root/navigation/Navigator.kt | 25 ++++----- .../main/res/navigation/root_nav_graph.xml | 18 +----- .../api/domain/OnboardingInteractor.kt | 3 + .../onboarding/impl/OnboardingRouter.kt | 2 + .../impl/di/OnboardingFeatureModule.kt | 6 +- .../impl/domain/OnboardingInteractorImpl.kt | 19 ++++++- .../impl/welcome/OnboardingScreen.kt | 6 +- .../impl/welcome/WelcomeViewModel.kt | 55 +++++++++++++++---- feature-splash/build.gradle | 1 + .../jp/co/soramitsu/splash/SplashRouter.kt | 6 +- .../splash/presentation/SplashViewModel.kt | 13 +---- 11 files changed, 87 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt index d583a5781c..b2ad6d2055 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt @@ -162,10 +162,10 @@ import jp.co.soramitsu.wallet.impl.presentation.transaction.detail.reward.Reward import jp.co.soramitsu.wallet.impl.presentation.transaction.detail.reward.RewardDetailsPayload import jp.co.soramitsu.wallet.impl.presentation.transaction.detail.swap.SwapDetailFragment import jp.co.soramitsu.wallet.impl.presentation.transaction.detail.transfer.TransferDetailFragment -import jp.co.soramitsu.walletconnect.impl.presentation.sessionproposal.SessionProposalFragment import jp.co.soramitsu.walletconnect.impl.presentation.chainschooser.ChainChooseFragment import jp.co.soramitsu.walletconnect.impl.presentation.connectioninfo.ConnectionInfoFragment import jp.co.soramitsu.walletconnect.impl.presentation.requestpreview.RequestPreviewFragment +import jp.co.soramitsu.walletconnect.impl.presentation.sessionproposal.SessionProposalFragment import jp.co.soramitsu.walletconnect.impl.presentation.sessionrequest.SessionRequestFragment import jp.co.soramitsu.walletconnect.impl.presentation.transactionrawdata.RawDataFragment import kotlin.coroutines.coroutineContext @@ -216,14 +216,21 @@ class Navigator : activity = null } - override fun openAddFirstAccount() { + override fun openOnboarding() { navController?.navigate(R.id.action_to_onboarding, WelcomeFragment.getBundle(false)) } + override fun openCreatePincode() { + val action = PinCodeAction.Create(NavComponentDelayedNavigation(R.id.action_open_main)) + val bundle = PincodeFragment.getPinCodeBundle(action) + navController?.navigate(R.id.pincodeFragment, bundle) + } + + override fun openInitialCheckPincode() { val action = PinCodeAction.Check(NavComponentDelayedNavigation(R.id.action_open_main), ToolbarConfiguration()) val bundle = PincodeFragment.getPinCodeBundle(action) - navController?.navigateSafe(R.id.action_splash_to_pin, bundle) + navController?.navigateSafe(R.id.pincodeFragment, bundle) } private fun NavController.navigateSafe(@IdRes resId: Int, args: Bundle?) { @@ -335,12 +342,6 @@ class Navigator : navController?.navigate(delayedNavigation.globalActionId, delayedNavigation.extras, navOptions) } - override fun openCreatePincode() { - val bundle = buildCreatePinBundle() - - navController?.navigate(R.id.pincodeFragment, bundle) - } - override fun openConfirmMnemonicOnCreate(confirmMnemonicPayload: ConfirmMnemonicPayload) { val bundle = ConfirmMnemonicFragment.getBundle(confirmMnemonicPayload) @@ -1274,12 +1275,6 @@ class Navigator : navController?.navigate(R.id.root_nav_graph, bundle) } - private fun buildCreatePinBundle(): Bundle { - val delayedNavigation = NavComponentDelayedNavigation(R.id.action_open_main) - val action = PinCodeAction.Create(delayedNavigation) - return PincodeFragment.getPinCodeBundle(action) - } - override fun openSelectWallet() { navController?.navigate(R.id.selectWalletFragment) } diff --git a/app/src/main/res/navigation/root_nav_graph.xml b/app/src/main/res/navigation/root_nav_graph.xml index f4202cfdf6..684428aa9e 100644 --- a/app/src/main/res/navigation/root_nav_graph.xml +++ b/app/src/main/res/navigation/root_nav_graph.xml @@ -30,23 +30,7 @@ - - - - - - + tools:layout="@layout/fragment_splash"/> + fun getWelcomeSlidesShownVersion(): String? + fun saveWelcomeSlidesShownVersion(version: String) + fun shouldShowWelcomeSlides(version: String): Boolean } diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/OnboardingRouter.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/OnboardingRouter.kt index e07647a1b6..dad71a4e06 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/OnboardingRouter.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/OnboardingRouter.kt @@ -35,4 +35,6 @@ interface OnboardingRouter { fun openSelectImportModeForResult(): Flow fun openCreatePincode() + + fun openInitialCheckPincode() } diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt index a58fc7b799..b57c0faf55 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/di/OnboardingFeatureModule.kt @@ -47,10 +47,12 @@ class OnboardingFeatureModule { @Provides fun provideOnboardingInteractor( - onboardingRepository: OnboardingRepository + onboardingRepository: OnboardingRepository, + preferences: Preferences ): OnboardingInteractor { return OnboardingInteractorImpl( - onboardingRepository + onboardingRepository, + preferences ) } diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt index 6926af1b64..7ef5acc9bd 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt @@ -1,15 +1,32 @@ package jp.co.soramitsu.onboarding.impl.domain +import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.onboarding.api.data.OnboardingConfig import jp.co.soramitsu.onboarding.api.data.OnboardingRepository import jp.co.soramitsu.onboarding.api.domain.OnboardingInteractor class OnboardingInteractorImpl( - private val onboardingRepository: OnboardingRepository + private val onboardingRepository: OnboardingRepository, + private val preferences: Preferences ): OnboardingInteractor { + companion object { + private const val PREFS_SHOWN_WELCOME_VERSION = "prefs_shown_welcome_version" + } + override suspend fun getConfig(): Result { return onboardingRepository.getConfig() } + override fun getWelcomeSlidesShownVersion(): String? { + return preferences.getString(PREFS_SHOWN_WELCOME_VERSION) + } + + override fun saveWelcomeSlidesShownVersion(version: String) { + preferences.putString(PREFS_SHOWN_WELCOME_VERSION, version) + } + + override fun shouldShowWelcomeSlides(version: String): Boolean { + return version != getWelcomeSlidesShownVersion() + } } diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt index 5dd8a17a2d..cfafc00f7c 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingScreen.kt @@ -66,10 +66,7 @@ interface OnboardingScreenCallback { fun onClose() - fun onNext() - fun onSkip() - } @Suppress("FunctionName") @@ -216,7 +213,7 @@ private fun OnboardingScreenContent( val page = currentPageAsState.value if (page == onboardingFlow.lastIndex) - callback.onNext() + callback.onClose() else coroutineScope.launch { pagerState.animateScrollToPage(page + 1) } @@ -265,7 +262,6 @@ private fun OnboardingScreenPreview() { ), callback = object : OnboardingScreenCallback { override fun onClose() = Unit - override fun onNext() = Unit override fun onSkip() = Unit } ) diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt index ad9a96172f..3a3b522fe5 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario +import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.account.api.domain.model.ImportMode import jp.co.soramitsu.backup.BackupService import jp.co.soramitsu.common.base.BaseViewModel @@ -40,7 +41,8 @@ class WelcomeViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val backupService: BackupService, private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario, - private val onboardingInteractor: OnboardingInteractor + private val onboardingInteractor: OnboardingInteractor, + private val accountRepository: AccountRepository ) : BaseViewModel(), Browserable, WelcomeScreenInterface, OnboardingScreenCallback, OnboardingSplashScreenClickListener { @@ -68,6 +70,7 @@ class WelcomeViewModel @Inject constructor( val events = _events.receiveAsFlow() override val openBrowserEvent = MutableLiveData>() + private var currentOnboardingConfigVersion: String? = null init { payload.createChainAccount?.run { @@ -85,12 +88,28 @@ class WelcomeViewModel @Inject constructor( }.getOrNull() val useConfig = getAppVersionSupportedConfig(remoteOnboardingConfig) + val isAccountSelected = accountRepository.isAccountSelected() + val shouldShowSlides = useConfig != null + && (onboardingInteractor.shouldShowWelcomeSlides(useConfig.minVersion) || isAccountSelected.not()) - if (useConfig == null) { - _onboardingFlowState.value = Result.failure(IllegalStateException("Onboarding config is empty")) - } else { - _onboardingFlowState.value = Result.success(OnboardingFlow(useConfig.enEn.new)) - _onboardingBackgroundState.value = useConfig.background + currentOnboardingConfigVersion = useConfig?.minVersion + + when { + isAccountSelected && shouldShowSlides -> { + _onboardingFlowState.value = Result.success(OnboardingFlow(useConfig!!.enEn.regular)) + _onboardingBackgroundState.value = useConfig.background + _events.trySend(WelcomeEvent.Onboarding.PagerScreen) + } + isAccountSelected -> { + moveNextToPincode() + } + shouldShowSlides -> { + _onboardingFlowState.value = Result.success(OnboardingFlow(useConfig!!.enEn.new)) + _onboardingBackgroundState.value = useConfig.background + } + else -> { + _onboardingFlowState.value = Result.failure(IllegalStateException("Onboarding config is empty")) + } } } } @@ -194,14 +213,28 @@ class WelcomeViewModel @Inject constructor( } override fun onClose() { - _events.trySend(WelcomeEvent.Onboarding.WelcomeScreen) + viewModelScope.launch { + if (accountRepository.isAccountSelected()) { + moveNextToPincode() + } else { + _events.trySend(WelcomeEvent.Onboarding.WelcomeScreen) + } + } + currentOnboardingConfigVersion?.let { + onboardingInteractor.saveWelcomeSlidesShownVersion(it) + currentOnboardingConfigVersion = null + } } - override fun onNext() { - _events.trySend(WelcomeEvent.Onboarding.WelcomeScreen) + override fun onSkip() { + onClose() } - override fun onSkip() { - _events.trySend(WelcomeEvent.Onboarding.WelcomeScreen) + private suspend fun moveNextToPincode() { + if (accountRepository.isCodeSet()) { + router.openInitialCheckPincode() + } else { + router.openCreatePincode() + } } } diff --git a/feature-splash/build.gradle b/feature-splash/build.gradle index a1ea3845bd..ddfbcaf7bc 100644 --- a/feature-splash/build.gradle +++ b/feature-splash/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation projects.common implementation projects.featureAccountApi + implementation projects.featureOnboardingApi implementation libs.kotlin.stdlib.jdk7 diff --git a/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt b/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt index 2f19596067..1af70e8095 100644 --- a/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt +++ b/feature-splash/src/main/java/jp/co/soramitsu/splash/SplashRouter.kt @@ -4,9 +4,5 @@ import jp.co.soramitsu.common.navigation.SecureRouter interface SplashRouter : SecureRouter { - fun openAddFirstAccount() - - fun openCreatePincode() - - fun openInitialCheckPincode() + fun openOnboarding() } diff --git a/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt b/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt index 167c83e2b4..8f5d581158 100644 --- a/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt +++ b/feature-splash/src/main/java/jp/co/soramitsu/splash/presentation/SplashViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import jp.co.soramitsu.account.api.domain.PendulumPreInstalledAccountsScenario -import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.splash.SplashRouter import kotlinx.coroutines.launch @@ -12,22 +11,14 @@ import kotlinx.coroutines.launch @HiltViewModel class SplashViewModel @Inject constructor( private val router: SplashRouter, - private val repository: AccountRepository, private val pendulumPreInstalledAccountsScenario: PendulumPreInstalledAccountsScenario ) : BaseViewModel() { fun openInitialDestination() { viewModelScope.launch { pendulumPreInstalledAccountsScenario.fetchFeatureToggle() - if (repository.isAccountSelected()) { - if (repository.isCodeSet()) { - router.openInitialCheckPincode() - } else { - router.openCreatePincode() - } - } else { - router.openAddFirstAccount() - } + + router.openOnboarding() } } } From 9ffb273dd8adccf205a4b18dc854667f477bcc26 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 17 May 2024 17:30:07 +0500 Subject: [PATCH 062/100] version up -> 3.5.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 084b40c2bb..ee349c8475 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ apply plugin: "org.sonarqube" buildscript { ext { // App version - versionName = '3.4.5' + versionName = '3.5.1' versionCode = 174 // SDK and tools From 281da123c00817686c3643ea6331cc8eceaa9f1e Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 12:03:08 +0500 Subject: [PATCH 063/100] FLW-4598 Update onboarding logic based on a new configuration: PR review cleanup --- .../api/domain/OnboardingInteractor.kt | 1 + .../impl/domain/OnboardingInteractorImpl.kt | 14 ++++++++ .../impl/welcome/OnboardingSplashScreen.kt | 33 ++++++++++++------- .../impl/welcome/WelcomeFragment.kt | 1 + .../impl/welcome/WelcomeViewModel.kt | 22 ++++--------- 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/domain/OnboardingInteractor.kt b/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/domain/OnboardingInteractor.kt index 4ae4cb71aa..e7f3b39514 100644 --- a/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/domain/OnboardingInteractor.kt +++ b/feature-onboarding-api/src/main/java/jp/co/soramitsu/onboarding/api/domain/OnboardingInteractor.kt @@ -5,6 +5,7 @@ import jp.co.soramitsu.onboarding.api.data.OnboardingConfig interface OnboardingInteractor { suspend fun getConfig(): Result + suspend fun getAppVersionSupportedConfig(): Result fun getWelcomeSlidesShownVersion(): String? fun saveWelcomeSlidesShownVersion(version: String) diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt index 7ef5acc9bd..9665a2d72f 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/domain/OnboardingInteractorImpl.kt @@ -1,6 +1,7 @@ package jp.co.soramitsu.onboarding.impl.domain import jp.co.soramitsu.common.data.storage.Preferences +import jp.co.soramitsu.common.domain.AppVersion import jp.co.soramitsu.onboarding.api.data.OnboardingConfig import jp.co.soramitsu.onboarding.api.data.OnboardingRepository import jp.co.soramitsu.onboarding.api.domain.OnboardingInteractor @@ -18,6 +19,19 @@ class OnboardingInteractorImpl( return onboardingRepository.getConfig() } + override suspend fun getAppVersionSupportedConfig(): Result { + val appVersion = AppVersion.current() + + return onboardingRepository.getConfig().map { + it.configs.filter { + val configVersion = AppVersion.fromString(it.minVersion) + configVersion.major == appVersion.major && configVersion.minor == appVersion.minor + }.maxByOrNull { + AppVersion.fromString(it.minVersion) + } + } + } + override fun getWelcomeSlidesShownVersion(): String? { return preferences.getString(PREFS_SHOWN_WELCOME_VERSION) } diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingSplashScreen.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingSplashScreen.kt index 2dbaa1e6cd..213aabfeab 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingSplashScreen.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/OnboardingSplashScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.paint @@ -25,6 +26,7 @@ import jp.co.soramitsu.common.compose.component.AccentButton import jp.co.soramitsu.common.compose.component.MarginVertical import jp.co.soramitsu.common.compose.theme.FearlessAppTheme import jp.co.soramitsu.feature_onboarding_impl.R +import kotlinx.coroutines.flow.StateFlow @Stable fun interface OnboardingSplashScreenClickListener { @@ -33,15 +35,17 @@ fun interface OnboardingSplashScreenClickListener { @Suppress("FunctionName") fun NavGraphBuilder.OnboardingSplashScreen( + isAccountSelectedFlow: StateFlow, listener: OnboardingSplashScreenClickListener ) { composable(WelcomeEvent.Onboarding.SplashScreen.route) { - OnboardingSplashScreenContent(listener) + OnboardingSplashScreenContent(isAccountSelectedFlow.collectAsState().value, listener) } } @Composable private fun OnboardingSplashScreenContent( + isAccountSelected: Boolean, listener: OnboardingSplashScreenClickListener ) { Column( @@ -56,7 +60,9 @@ private fun OnboardingSplashScreenContent( ) { Column( - modifier = Modifier.weight(1f).width(IntrinsicSize.Max), + modifier = Modifier + .weight(1f) + .width(IntrinsicSize.Max), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -75,14 +81,16 @@ private fun OnboardingSplashScreenContent( } - AccentButton( - text = stringResource(id = R.string.common_start), - modifier = Modifier - .fillMaxWidth() - .height(48.dp) - .padding(horizontal = 16.dp), - onClick = listener::onStart - ) + if (isAccountSelected.not()) { + AccentButton( + text = stringResource(id = R.string.common_start), + modifier = Modifier + .fillMaxWidth() + .height(48.dp) + .padding(horizontal = 16.dp), + onClick = listener::onStart + ) + } MarginVertical(margin = 16.dp) } @@ -92,6 +100,9 @@ private fun OnboardingSplashScreenContent( @Preview private fun OnboardingSplashScreenPreview() { FearlessAppTheme { - OnboardingSplashScreenContent { Unit } + OnboardingSplashScreenContent( + isAccountSelected = true, + listener = {} + ) } } \ No newline at end of file diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt index 34b9032e7a..6ec443e5b9 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeFragment.kt @@ -116,6 +116,7 @@ class WelcomeFragment : BaseComposeFragment() { ) { OnboardingSplashScreen( + isAccountSelectedFlow = viewModel.isAccountSelectedFlow, listener = viewModel ) diff --git a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt index 3a3b522fe5..5ff8d4c6eb 100644 --- a/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt +++ b/feature-onboarding-impl/src/main/java/jp/co/soramitsu/onboarding/impl/welcome/WelcomeViewModel.kt @@ -14,10 +14,8 @@ import jp.co.soramitsu.account.api.domain.model.ImportMode import jp.co.soramitsu.backup.BackupService import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.data.network.AppLinksProvider -import jp.co.soramitsu.common.domain.AppVersion import jp.co.soramitsu.common.mixin.api.Browserable import jp.co.soramitsu.common.utils.Event -import jp.co.soramitsu.onboarding.api.data.OnboardingConfig import jp.co.soramitsu.onboarding.api.domain.OnboardingInteractor import jp.co.soramitsu.onboarding.impl.OnboardingRouter import jp.co.soramitsu.onboarding.impl.welcome.WelcomeFragment.Companion.KEY_PAYLOAD @@ -25,6 +23,7 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -48,6 +47,8 @@ class WelcomeViewModel @Inject constructor( private val payload = savedStateHandle.get(KEY_PAYLOAD)!! + private val _isAccountSelectedFlow = MutableStateFlow(true) + val isAccountSelectedFlow: StateFlow = _isAccountSelectedFlow private val _onboardingBackgroundState = MutableStateFlow(null) val onboardingBackground = _onboardingBackgroundState .stateIn(viewModelScope, SharingStarted.Eagerly, null) @@ -81,14 +82,15 @@ class WelcomeViewModel @Inject constructor( } viewModelScope.launch { - val remoteOnboardingConfig: OnboardingConfig? = onboardingInteractor.getConfig() + val isAccountSelected = accountRepository.isAccountSelected() + _isAccountSelectedFlow.value = isAccountSelected + + val useConfig = onboardingInteractor.getAppVersionSupportedConfig() .onFailure { Log.e("OnboardingScreen", "onboardingInteractor.getConfig() failed: $it") showError(it) }.getOrNull() - val useConfig = getAppVersionSupportedConfig(remoteOnboardingConfig) - val isAccountSelected = accountRepository.isAccountSelected() val shouldShowSlides = useConfig != null && (onboardingInteractor.shouldShowWelcomeSlides(useConfig.minVersion) || isAccountSelected.not()) @@ -114,16 +116,6 @@ class WelcomeViewModel @Inject constructor( } } - private fun getAppVersionSupportedConfig(config: OnboardingConfig?): OnboardingConfig.OnboardingConfigItem? { - val current = AppVersion.current() - return config?.configs?.filter { - val configAppVersion = AppVersion.fromString(it.minVersion) - configAppVersion.major == current.major && configAppVersion.minor == current.minor - }?.maxByOrNull { - AppVersion.fromString(it.minVersion) - } - } - override fun createAccountClicked() { router.openCreateAccountFromOnboarding() } From 80da8b3ce3eaab2dee3b4709ff84008af7f56c79 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 12:22:06 +0500 Subject: [PATCH 064/100] detekt fix --- .../nft/impl/domain/NFTTransferInteractorImpl.kt | 12 ++++++------ .../usecase/eth/estimateEthTransactionNetworkFee.kt | 2 +- .../nft/impl/domain/utils/Web3AdapterExt.kt | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt index 261972d8c1..b1af4ee4df 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/NFTTransferInteractorImpl.kt @@ -8,9 +8,9 @@ import jp.co.soramitsu.common.data.secrets.v2.MetaAccountSecrets import jp.co.soramitsu.nft.domain.NFTTransferInteractor import jp.co.soramitsu.nft.domain.models.NFT import jp.co.soramitsu.nft.impl.domain.usecase.eth.CreateRawEthTransaction -import jp.co.soramitsu.nft.impl.domain.usecase.eth.estimateEthTransactionNetworkFee import jp.co.soramitsu.nft.impl.domain.usecase.eth.ExecuteEthFunction import jp.co.soramitsu.nft.impl.domain.usecase.eth.SendRawEthTransaction +import jp.co.soramitsu.nft.impl.domain.usecase.eth.estimateEthTransactionNetworkFee import jp.co.soramitsu.nft.impl.domain.usecase.transfer.NFTAccountBalanceAdapter import jp.co.soramitsu.nft.impl.domain.usecase.transfer.NFTTransferAdapter import jp.co.soramitsu.nft.impl.domain.utils.nonNullWeb3j @@ -18,18 +18,18 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumChainConnection import jp.co.soramitsu.runtime.multiNetwork.connection.EthereumConnectionPool +import jp.co.soramitsu.wallet.impl.data.network.blockchain.subscribeBaseFeePerGas +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.transform +import kotlinx.coroutines.withContext import org.web3j.utils.Numeric import java.math.BigDecimal import java.math.BigInteger -import jp.co.soramitsu.wallet.impl.data.network.blockchain.subscribeBaseFeePerGas import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.withContext class NFTTransferInteractorImpl( private val accountRepository: AccountRepository, diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt index 55528fe013..6bd9c29b5a 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/usecase/eth/estimateEthTransactionNetworkFee.kt @@ -20,7 +20,7 @@ suspend fun EthereumChainConnection.estimateEthTransactionNetworkFee( ethConnection = this, call = call, baseFeePerGas = baseFeePerGas, - estimateGas = estimateGas + estimateGas = estimateGas ) } else { error( diff --git a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt index 511bcf0c6d..0c99c279b1 100644 --- a/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt +++ b/feature-nft-impl/src/main/java/jp/co/soramitsu/nft/impl/domain/utils/Web3AdapterExt.kt @@ -45,7 +45,7 @@ inline fun Response.map(crossinline transform: (T) -> K): K { } suspend fun Web3j.getNonce(address: String): BigInteger { - val response = ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).sendAsync().await() + val response = ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).sendAsync().await() return response.map { Numeric.decodeQuantity(it) } } @@ -74,4 +74,4 @@ suspend fun Web3j.getBaseFee(): BigInteger { ).sendAsync().await() return response.map { Numeric.decodeQuantity(it.baseFeePerGas) } -} \ No newline at end of file +} From 3527d9393e38a12e6fc4f18b50d4f6cea3daeb15 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 13:18:49 +0500 Subject: [PATCH 065/100] buildUp --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ee349c8475..a8be999240 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 174 + versionCode = 175 // SDK and tools compileSdkVersion = 34 From 72301a3e20a7ad16bcf6bd9143b7b26b72e7e35a Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 21 May 2024 18:04:27 +0700 Subject: [PATCH 066/100] FLW-4562 create pool alert fix --- .../staking/impl/presentation/StakingConfirmViewModel.kt | 5 ++++- .../confirm/pool/create/ConfirmCreatePoolViewModel.kt | 3 +++ .../impl/presentation/pools/edit/EditPoolConfirmViewModel.kt | 3 +++ .../staking/bond/confirm/ConfirmPoolBondMoreViewModel.kt | 3 +++ .../presentation/staking/claim/ConfirmPoolClaimViewModel.kt | 3 +++ .../staking/redeem/ConfirmPoolRedeemViewModel.kt | 3 +++ .../staking/unbond/confirm/ConfirmPoolUnbondViewModel.kt | 3 +++ .../validators/compose/ConfirmSelectValidatorsViewModel.kt | 3 +++ .../wallet/api/presentation/BaseConfirmViewModel.kt | 5 ++++- 9 files changed, 29 insertions(+), 2 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/StakingConfirmViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/StakingConfirmViewModel.kt index 7da47fe227..380140e4fb 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/StakingConfirmViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/StakingConfirmViewModel.kt @@ -7,10 +7,12 @@ import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase import jp.co.soramitsu.wallet.api.presentation.BaseConfirmViewModel +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import jp.co.soramitsu.wallet.impl.domain.model.Asset abstract class StakingConfirmViewModel( - private val existentialDepositUseCase: ExistentialDepositUseCase, + walletInteractor: WalletInteractor, + existentialDepositUseCase: ExistentialDepositUseCase, private val router: StakingRouter, resourceManager: ResourceManager, asset: Asset, @@ -26,6 +28,7 @@ abstract class StakingConfirmViewModel( private val onOperationSuccess: () -> Unit, private val customSuccessMessage: String? = null ) : BaseConfirmViewModel( + walletInteractor, existentialDepositUseCase, resourceManager, asset, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/pool/create/ConfirmCreatePoolViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/pool/create/ConfirmCreatePoolViewModel.kt index 614db5a76c..afdfb6e4a7 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/pool/create/ConfirmCreatePoolViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/confirm/pool/create/ConfirmCreatePoolViewModel.kt @@ -15,6 +15,7 @@ import jp.co.soramitsu.staking.impl.presentation.common.SelectValidatorFlowState import jp.co.soramitsu.staking.impl.presentation.common.StakingPoolSharedStateProvider import jp.co.soramitsu.staking.impl.scenarios.StakingPoolInteractor import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -22,6 +23,7 @@ import kotlinx.coroutines.flow.stateIn @HiltViewModel class ConfirmCreatePoolViewModel @Inject constructor( + walletInteractor: WalletInteractor, existentialDepositUseCase: ExistentialDepositUseCase, poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, @@ -29,6 +31,7 @@ class ConfirmCreatePoolViewModel @Inject constructor( private val router: StakingRouter, private val poolInteractor: StakingPoolInteractor ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/pools/edit/EditPoolConfirmViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/pools/edit/EditPoolConfirmViewModel.kt index 36e3d4e601..cac031a73c 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/pools/edit/EditPoolConfirmViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/pools/edit/EditPoolConfirmViewModel.kt @@ -17,15 +17,18 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import javax.inject.Inject +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor @HiltViewModel class EditPoolConfirmViewModel @Inject constructor( + walletInteractor: WalletInteractor, existentialDepositUseCase: ExistentialDepositUseCase, private val resourceManager: ResourceManager, private val poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, private val router: StakingRouter ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmPoolBondMoreViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmPoolBondMoreViewModel.kt index 48ece91cff..7ddb80cc21 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmPoolBondMoreViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/bond/confirm/ConfirmPoolBondMoreViewModel.kt @@ -9,17 +9,20 @@ import jp.co.soramitsu.staking.impl.presentation.StakingRouter import jp.co.soramitsu.staking.impl.presentation.common.StakingPoolSharedStateProvider import jp.co.soramitsu.staking.impl.scenarios.StakingPoolInteractor import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @HiltViewModel class ConfirmPoolBondMoreViewModel @Inject constructor( + walletInteractor: WalletInteractor, existentialDepositUseCase: ExistentialDepositUseCase, poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, resourceManager: ResourceManager, private val router: StakingRouter ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/claim/ConfirmPoolClaimViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/claim/ConfirmPoolClaimViewModel.kt index a28066c00b..7ab83942d7 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/claim/ConfirmPoolClaimViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/claim/ConfirmPoolClaimViewModel.kt @@ -12,15 +12,18 @@ import jp.co.soramitsu.staking.impl.presentation.StakingRouter import jp.co.soramitsu.staking.impl.presentation.common.StakingPoolSharedStateProvider import jp.co.soramitsu.staking.impl.scenarios.StakingPoolInteractor import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor @HiltViewModel class ConfirmPoolClaimViewModel @Inject constructor( + walletInteractor: WalletInteractor, private val existentialDepositUseCase: ExistentialDepositUseCase, poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, private val resourceManager: ResourceManager, private val router: StakingRouter ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/ConfirmPoolRedeemViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/ConfirmPoolRedeemViewModel.kt index 52604f0688..274edfe33a 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/ConfirmPoolRedeemViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/redeem/ConfirmPoolRedeemViewModel.kt @@ -12,15 +12,18 @@ import jp.co.soramitsu.staking.impl.presentation.StakingRouter import jp.co.soramitsu.staking.impl.presentation.common.StakingPoolSharedStateProvider import jp.co.soramitsu.staking.impl.scenarios.StakingPoolInteractor import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor @HiltViewModel class ConfirmPoolRedeemViewModel @Inject constructor( + walletInteractor: WalletInteractor, private val existentialDepositUseCase: ExistentialDepositUseCase, poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, private val resourceManager: ResourceManager, private val router: StakingRouter ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmPoolUnbondViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmPoolUnbondViewModel.kt index a937adfffc..1c2baff3c4 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmPoolUnbondViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/unbond/confirm/ConfirmPoolUnbondViewModel.kt @@ -12,15 +12,18 @@ import jp.co.soramitsu.staking.impl.presentation.StakingRouter import jp.co.soramitsu.staking.impl.presentation.common.StakingPoolSharedStateProvider import jp.co.soramitsu.staking.impl.scenarios.StakingPoolInteractor import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor @HiltViewModel class ConfirmPoolUnbondViewModel @Inject constructor( + walletInteractor: WalletInteractor, private val existentialDepositUseCase: ExistentialDepositUseCase, poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, private val resourceManager: ResourceManager, private val router: StakingRouter ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/compose/ConfirmSelectValidatorsViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/compose/ConfirmSelectValidatorsViewModel.kt index 65ebc25a24..86512fff86 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/compose/ConfirmSelectValidatorsViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/validators/compose/ConfirmSelectValidatorsViewModel.kt @@ -11,6 +11,7 @@ import jp.co.soramitsu.staking.impl.presentation.StakingRouter import jp.co.soramitsu.staking.impl.presentation.common.StakingPoolSharedStateProvider import jp.co.soramitsu.staking.impl.scenarios.StakingPoolInteractor import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -18,12 +19,14 @@ import kotlinx.coroutines.flow.stateIn @HiltViewModel class ConfirmSelectValidatorsViewModel @Inject constructor( + walletInteractor: WalletInteractor, existentialDepositUseCase: ExistentialDepositUseCase, poolSharedStateProvider: StakingPoolSharedStateProvider, private val stakingPoolInteractor: StakingPoolInteractor, resourceManager: ResourceManager, private val router: StakingRouter ) : StakingConfirmViewModel( + walletInteractor = walletInteractor, existentialDepositUseCase = existentialDepositUseCase, chain = poolSharedStateProvider.requireMainState.requireChain, router = router, diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseConfirmViewModel.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseConfirmViewModel.kt index 44f7c761b4..859f322a51 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseConfirmViewModel.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/wallet/api/presentation/BaseConfirmViewModel.kt @@ -23,6 +23,7 @@ import jp.co.soramitsu.common.validation.WaitForFeeCalculationException import jp.co.soramitsu.feature_wallet_api.R import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.wallet.api.domain.ExistentialDepositUseCase +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor import jp.co.soramitsu.wallet.impl.domain.model.Asset import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks import jp.co.soramitsu.wallet.impl.domain.model.planksFromAmount @@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch abstract class BaseConfirmViewModel( + walletInteractor: WalletInteractor, private val existentialDepositUseCase: ExistentialDepositUseCase, private val resourceManager: ResourceManager, protected val asset: Asset, @@ -56,6 +58,7 @@ abstract class BaseConfirmViewModel( private val amount = amountInPlanks?.let { asset.token.amountFromPlanks(it) } private val amountFormatted = amount?.formatCryptoDetail(asset.token.configuration.symbol) private val amountFiat = amount?.applyFiatRate(asset.token.fiatRate)?.formatFiat(asset.token.fiatSymbol) + private val assetFlow = walletInteractor.assetFlow(chain.id, asset.token.configuration.id).stateIn(viewModelScope, SharingStarted.Eagerly, asset) private val toolbarViewState = ToolbarViewState( resourceManager.getString(R.string.common_confirm), @@ -148,7 +151,7 @@ abstract class BaseConfirmViewModel( val chargesAmount = amountInPlanks.orZero() + fee val existentialDeposit = existentialDepositUseCase(asset.token.configuration) - val resultBalance = asset.transferableInPlanks - chargesAmount + val resultBalance = assetFlow.value.transferableInPlanks - chargesAmount if (resultBalance < existentialDeposit || resultBalance <= BigInteger.ZERO) { return Result.failure(FeeInsufficientBalanceException(resourceManager)) } From 624c4b8b658713bd43edf3951f1ecdcf96f3c0d2 Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 23 May 2024 17:51:00 +0700 Subject: [PATCH 067/100] fixes --- .../app/root/presentation/RootActivity.kt | 66 +++++++++++++- .../app/root/presentation/RootViewModel.kt | 1 + .../data/network/runtime/binding/Primitive.kt | 3 +- .../common/di/modules/NetworkModule.kt | 4 +- .../impl/data/PolkaswapRepositoryImpl.kt | 47 +++++++--- .../impl/di/PolkaswapFeatureBindModule.kt | 37 +++++--- .../impl/domain/PolkaswapInteractorImpl.kt | 66 +++++++------- gradle/libs.versions.toml | 2 +- .../runtime/multiNetwork/ChainRegistry.kt | 2 +- .../multiNetwork/connection/ConnectionPool.kt | 84 +++++++++--------- .../connection/EthereumConnectionPool.kt | 86 +++++++++++-------- 11 files changed, 258 insertions(+), 140 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt index 964e79e1f5..bd48324f1c 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootActivity.kt @@ -24,6 +24,9 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import dagger.hilt.android.AndroidEntryPoint +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL import javax.inject.Inject import jp.co.soramitsu.app.R import jp.co.soramitsu.app.root.navigation.Navigator @@ -35,6 +38,12 @@ import jp.co.soramitsu.common.utils.observe import jp.co.soramitsu.common.utils.showToast import jp.co.soramitsu.common.utils.updatePadding import jp.co.soramitsu.common.view.bottomSheet.AlertBottomSheet +import jp.co.soramitsu.runtime.BuildConfig +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @AndroidEntryPoint class RootActivity : BaseActivity(), LifecycleObserver { @@ -65,7 +74,10 @@ class RootActivity : BaseActivity(), LifecycleObserver { navigator.attach(navController, this) rootNetworkBar.setOnApplyWindowInsetsListener { view, insets -> - view.updatePadding(top = WindowInsetsCompat.toWindowInsetsCompat(insets, view).getInsets(WindowInsetsCompat.Type.systemBars()).top) + view.updatePadding( + top = WindowInsetsCompat.toWindowInsetsCompat(insets, view) + .getInsets(WindowInsetsCompat.Type.systemBars()).top + ) insets } @@ -87,6 +99,20 @@ class RootActivity : BaseActivity(), LifecycleObserver { super.onLost(network) viewModel.onConnectionLost() } + + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities + ) { + super.onCapabilitiesChanged(network, networkCapabilities) + val hasInternetCapability = + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + if (hasInternetCapability) { + viewModel.onNetworkAvailable() + } else { + viewModel.onConnectionLost() + } + } } val networkRequest = NetworkRequest.Builder() @@ -95,8 +121,44 @@ class RootActivity : BaseActivity(), LifecycleObserver { .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .build() - val connectivityManager = getSystemService(ConnectivityManager::class.java) as ConnectivityManager + val connectivityManager = + getSystemService(ConnectivityManager::class.java) as ConnectivityManager connectivityManager.registerNetworkCallback(networkRequest, networkCallback) + + lifecycleScope.launch { + while (isActive) { + delay(5_000) + val isConnected = checkNetworkStatus(connectivityManager) + val hasInternetAccess = hasInternetAccess() + withContext(Dispatchers.Main){ + if(isConnected || hasInternetAccess){ + viewModel.onNetworkAvailable() + } else { + viewModel.onConnectionLost() + } + } + } + } + } + + private suspend fun hasInternetAccess(): Boolean { + return withContext(Dispatchers.IO) { + try { + val url = URL(BuildConfig.CHAINS_URL) + val urlConnection = url.openConnection() as HttpURLConnection + urlConnection.connectTimeout = 1000 + urlConnection.connect() + urlConnection.responseCode == 200 + } catch (e: IOException) { + false + } + } + } + + private fun checkNetworkStatus(connectivityManager: ConnectivityManager): Boolean { + val activeNetwork = connectivityManager.activeNetwork + val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) + return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true } override fun onDestroy() { diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index 40b3426436..f574b11d01 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -188,6 +188,7 @@ class RootViewModel @Inject constructor( val showConnectingBar: StateFlow = _showConnectingBar fun onNetworkAvailable() { _showConnectingBar.update { false } + externalConnectionRequirementFlow.value = ChainConnection.ExternalRequirement.ALLOWED // todo this code triggers redundant requests and balance updates. Needs research // viewModelScope.launch { // checkAppVersion() diff --git a/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/Primitive.kt b/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/Primitive.kt index dcf75513cf..78f8a20c84 100644 --- a/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/Primitive.kt +++ b/common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/Primitive.kt @@ -1,9 +1,10 @@ package jp.co.soramitsu.common.data.network.runtime.binding import java.math.BigInteger +import jp.co.soramitsu.common.utils.orZero @HelperBinding -fun bindNumber(dynamicInstance: Any?): BigInteger = dynamicInstance.cast() +fun bindNumber(dynamicInstance: Any?): BigInteger = runCatching { dynamicInstance.cast() }.getOrNull().orZero() @HelperBinding fun bindString(dynamicInstance: Any?): String = dynamicInstance.cast().decodeToString() diff --git a/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt b/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt index 4af739ce87..64b7b7cdc2 100644 --- a/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt +++ b/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt @@ -86,7 +86,9 @@ class NetworkModule { @Provides @Singleton - fun provideSocketFactory() = WebSocketFactory() + fun provideSocketFactory() = WebSocketFactory().also { + it.connectionTimeout = 10000 + } @Provides @Singleton diff --git a/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/data/PolkaswapRepositoryImpl.kt b/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/data/PolkaswapRepositoryImpl.kt index f5b4e2617b..48c9e70271 100644 --- a/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/data/PolkaswapRepositoryImpl.kt +++ b/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/data/PolkaswapRepositoryImpl.kt @@ -11,9 +11,6 @@ import jp.co.soramitsu.common.utils.poolXYK import jp.co.soramitsu.common.utils.u32ArgumentFromStorageKey import jp.co.soramitsu.core.extrinsic.ExtrinsicService import jp.co.soramitsu.core.rpc.RpcCalls -import jp.co.soramitsu.core.rpc.calls.liquidityProxyIsPathAvailable -import jp.co.soramitsu.core.rpc.calls.liquidityProxyListEnabledSourcesForPath -import jp.co.soramitsu.core.rpc.calls.liquidityProxyQuote import jp.co.soramitsu.core.runtime.models.responses.QuoteResponse import jp.co.soramitsu.polkaswap.api.data.PolkaswapRepository import jp.co.soramitsu.polkaswap.api.models.Market @@ -32,6 +29,11 @@ import jp.co.soramitsu.shared_utils.runtime.definitions.types.composite.Struct import jp.co.soramitsu.shared_utils.runtime.metadata.storage import jp.co.soramitsu.shared_utils.runtime.metadata.storageKey import jp.co.soramitsu.shared_utils.wsrpc.exception.RpcException +import jp.co.soramitsu.shared_utils.wsrpc.executeAsync +import jp.co.soramitsu.shared_utils.wsrpc.mappers.nonNull +import jp.co.soramitsu.shared_utils.wsrpc.mappers.pojo +import jp.co.soramitsu.shared_utils.wsrpc.mappers.pojoList +import jp.co.soramitsu.shared_utils.wsrpc.request.runtime.RuntimeRequest import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow @@ -109,7 +111,16 @@ class PolkaswapRepositoryImpl @Inject constructor( tokenToId: String, dexId: Int ): Boolean { - return rpcCalls.liquidityProxyIsPathAvailable(chainId, tokenFromId, tokenToId, dexId) + val request = RuntimeRequest( + method = "liquidityProxy_isPathAvailable", + params = listOf( + dexId, + tokenFromId, + tokenToId + ) + ) + + return chainRegistry.awaitConnection(chainId).socketService.executeAsync(request, mapper = pojo().nonNull()) } override suspend fun getSwapQuote( @@ -122,16 +133,20 @@ class PolkaswapRepositoryImpl @Inject constructor( dexId: Int ): QuoteResponse? { return try { - rpcCalls.liquidityProxyQuote( - chainId, - tokenFromId, - tokenToId, - amount, - desired.backString, - curMarkets.backStrings(), - curMarkets.toFilters(), - dexId + val request = RuntimeRequest( + method = "liquidityProxy_quote", + params = listOf( + dexId, + tokenFromId, + tokenToId, + amount.toString(), + desired.backString, + curMarkets.backStrings(), + curMarkets.toFilters() + ) ) + + chainRegistry.awaitConnection(chainId).socketService.executeAsync(request, mapper = pojo()).result } catch (e: Exception) { null } @@ -180,7 +195,11 @@ class PolkaswapRepositoryImpl @Inject constructor( private suspend fun getEnabledMarkets(chainId: ChainId, dexId: Int, tokenId1: String, tokenId2: String): List { return try { - rpcCalls.liquidityProxyListEnabledSourcesForPath(chainId, dexId, tokenId1, tokenId2).toMarkets() + val request = RuntimeRequest( + "liquidityProxy_listEnabledSourcesForPath", + listOf(dexId, tokenId1, tokenId2) + ) + return chainRegistry.awaitConnection(chainId).socketService.executeAsync(request, mapper = pojoList().nonNull()).toMarkets() } catch (e: RpcException) { listOf() } diff --git a/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/di/PolkaswapFeatureBindModule.kt b/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/di/PolkaswapFeatureBindModule.kt index e22816d132..af6ad86882 100644 --- a/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/di/PolkaswapFeatureBindModule.kt +++ b/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/di/PolkaswapFeatureBindModule.kt @@ -9,6 +9,7 @@ import javax.inject.Named import javax.inject.Singleton import jp.co.soramitsu.account.api.domain.interfaces.AccountRepository import jp.co.soramitsu.common.data.network.config.RemoteConfigFetcher +import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.core.extrinsic.ExtrinsicService import jp.co.soramitsu.polkaswap.api.data.PolkaswapRepository import jp.co.soramitsu.polkaswap.api.domain.PolkaswapInteractor @@ -17,22 +18,12 @@ import jp.co.soramitsu.polkaswap.impl.domain.PolkaswapInteractorImpl import jp.co.soramitsu.runtime.di.REMOTE_STORAGE_SOURCE import jp.co.soramitsu.runtime.multiNetwork.ChainRegistry import jp.co.soramitsu.core.rpc.RpcCalls +import jp.co.soramitsu.runtime.multiNetwork.chain.ChainsRepository import jp.co.soramitsu.runtime.storage.source.StorageDataSource +import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository @InstallIn(SingletonComponent::class) @Module -interface PolkaswapFeatureBindModule { - - @Binds - @Singleton - fun bindsPolkaswapInteractor(polkaswapInteractor: PolkaswapInteractorImpl): PolkaswapInteractor - - @Binds - fun bindsPolkaswapRepository(polkaswapRepository: PolkaswapRepositoryImpl): PolkaswapRepository -} - -@InstallIn(SingletonComponent::class) -@Module(includes = [PolkaswapFeatureBindModule::class]) class PolkaswapFeatureModule { @Provides @@ -43,7 +34,27 @@ class PolkaswapFeatureModule { chainRegistry: ChainRegistry, rpcCalls: RpcCalls, accountRepository: AccountRepository - ): PolkaswapRepositoryImpl { + ): PolkaswapRepository { return PolkaswapRepositoryImpl(remoteConfigFetcher, remoteSource, extrinsicService, chainRegistry, rpcCalls, accountRepository) } + + @Provides + @Singleton + fun providePolkaswapInteractor( + chainRegistry: ChainRegistry, + walletRepository: WalletRepository, + accountRepository: AccountRepository, + polkaswapRepository: PolkaswapRepository, + sharedPreferences: Preferences, + chainsRepository: ChainsRepository, + ): PolkaswapInteractor { + return PolkaswapInteractorImpl( + chainRegistry, + walletRepository, + accountRepository, + polkaswapRepository, + sharedPreferences, + chainsRepository + ) + } } diff --git a/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/domain/PolkaswapInteractorImpl.kt b/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/domain/PolkaswapInteractorImpl.kt index d9b5834d63..6cf904310e 100644 --- a/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/domain/PolkaswapInteractorImpl.kt +++ b/feature-polkaswap-impl/src/main/kotlin/jp/co/soramitsu/polkaswap/impl/domain/PolkaswapInteractorImpl.kt @@ -1,5 +1,6 @@ package jp.co.soramitsu.polkaswap.impl.domain +import android.util.Log import java.math.BigDecimal import java.math.BigInteger import java.math.RoundingMode @@ -9,7 +10,6 @@ import jp.co.soramitsu.account.api.domain.model.accountId import jp.co.soramitsu.common.data.storage.Preferences import jp.co.soramitsu.common.presentation.LoadingState import jp.co.soramitsu.common.utils.isZero -import jp.co.soramitsu.common.utils.orZero import jp.co.soramitsu.core.runtime.models.responses.QuoteResponse import jp.co.soramitsu.core.utils.utilityAsset import jp.co.soramitsu.polkaswap.api.data.PolkaswapRepository @@ -30,14 +30,19 @@ import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository import jp.co.soramitsu.wallet.impl.domain.model.Asset import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks import jp.co.soramitsu.wallet.impl.domain.model.planksFromAmount +import kotlin.coroutines.CoroutineContext import kotlin.math.max +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.withContext class PolkaswapInteractorImpl @Inject constructor( private val chainRegistry: ChainRegistry, @@ -45,7 +50,8 @@ class PolkaswapInteractorImpl @Inject constructor( private val accountRepository: AccountRepository, private val polkaswapRepository: PolkaswapRepository, private val sharedPreferences: Preferences, - private val chainsRepository: ChainsRepository + private val chainsRepository: ChainsRepository, + private val coroutineContext: CoroutineContext = Dispatchers.Default + CoroutineExceptionHandler { _, throwable -> Log.e("PolkaswapInteractor", "$throwable ${throwable.message}") } ) : PolkaswapInteractor { override var polkaswapChainId = soraMainChainId @@ -65,16 +71,16 @@ class PolkaswapInteractorImpl @Inject constructor( chainId?.takeIf { it in listOf(soraMainChainId, soraTestChainId) }?.let { polkaswapChainId = chainId } } - override suspend fun getFeeAsset(): Asset? { + override suspend fun getFeeAsset(): Asset? = withContext(coroutineContext) { val chain = chainsRepository.getChain(polkaswapChainId) - return chain.utilityAsset?.id?.let { getAsset(it) } + chain.utilityAsset?.id?.let { getAsset(it) } } - override suspend fun getAsset(assetId: String): Asset? { + override suspend fun getAsset(assetId: String): Asset? = withContext(coroutineContext) { val metaAccount = accountRepository.getSelectedMetaAccount() val (chain, chainAsset) = chainsRepository.chainWithAsset(polkaswapChainId, assetId) - return walletRepository.getAsset(metaAccount.id, metaAccount.accountId(chain)!!, chainAsset, chain.minSupportedVersion) + walletRepository.getAsset(metaAccount.id, metaAccount.accountId(chain)!!, chainAsset, chain.minSupportedVersion) } @OptIn(ExperimentalCoroutinesApi::class) @@ -90,11 +96,11 @@ class PolkaswapInteractorImpl @Inject constructor( chainAsset, chain.minSupportedVersion ) - } + }.flowOn(Dispatchers.Default) } - override suspend fun getAvailableDexes(): List { - return polkaswapRepository.getAvailableDexes(polkaswapChainId) + override suspend fun getAvailableDexes(): List = withContext(coroutineContext) { + polkaswapRepository.getAvailableDexes(polkaswapChainId) } @OptIn(FlowPreview::class) @@ -113,7 +119,7 @@ class PolkaswapInteractorImpl @Inject constructor( flows.add(polkaswapRepository.observePoolTBCReserves(polkaswapChainId, fromTokenId)) flows.add(polkaswapRepository.observePoolTBCReserves(polkaswapChainId, toTokenId)) } - return flows.merge().debounce(500) + return flows.merge().debounce(500).flowOn(Dispatchers.Default) } override suspend fun calcDetails( @@ -124,7 +130,7 @@ class PolkaswapInteractorImpl @Inject constructor( desired: WithDesired, slippageTolerance: Double, market: Market - ): Result { + ): Result = withContext(coroutineContext) { val polkaswapUtilityAssetId = chainsRepository.getChain(polkaswapChainId).utilityAsset?.id val feeAsset = requireNotNull(polkaswapUtilityAssetId?.let { getAsset(it) }) @@ -136,7 +142,7 @@ class PolkaswapInteractorImpl @Inject constructor( val tokenToId = requireNotNull(tokenTo.token.configuration.currencyId) val previousBestDex = bestDexIdFlow.value - if (market !in availableMarkets.values.flatten().toSet()) return Result.success(null) + if (market !in availableMarkets.values.flatten().toSet()) return@withContext Result.success(null) bestDexIdFlow.emit(LoadingState.Loading()) val (bestDex, swapQuote) = getBestSwapQuote( dexes = availableDexPaths, @@ -147,11 +153,11 @@ class PolkaswapInteractorImpl @Inject constructor( curMarkets = curMarkets )?.let { it.first to it.second.toModel(feeAsset.token.configuration) } ?: run { bestDexIdFlow.emit(previousBestDex) - return Result.failure(InsufficientLiquidityException()) + return@withContext Result.failure(InsufficientLiquidityException()) } bestDexIdFlow.emit(LoadingState.Loaded(bestDex)) - if (swapQuote.amount.isZero()) return Result.success(null) + if (swapQuote.amount.isZero()) return@withContext Result.success(null) val minMax = (swapQuote.amount * BigDecimal.valueOf(slippageTolerance / 100)).let { @@ -164,7 +170,7 @@ class PolkaswapInteractorImpl @Inject constructor( val scale = max(swapQuote.amount.scale(), amount.scale()) - if (swapQuote.amount.isZero()) return Result.success(null) + if (swapQuote.amount.isZero()) return@withContext Result.success(null) val per1 = amount.divide(swapQuote.amount, scale, RoundingMode.HALF_EVEN) val per2 = swapQuote.amount.divide(amount, scale, RoundingMode.HALF_EVEN) @@ -187,7 +193,7 @@ class PolkaswapInteractorImpl @Inject constructor( bestDexId = bestDex, route = route ) - return Result.success(details) + return@withContext Result.success(details) } private suspend fun getBestSwapQuote( @@ -197,7 +203,7 @@ class PolkaswapInteractorImpl @Inject constructor( amount: BigInteger, desired: WithDesired, curMarkets: List - ): Pair? { + ): Pair? = withContext(coroutineContext) { val quotes = dexes.mapNotNull { dexId -> val quote = polkaswapRepository.getSwapQuote( chainId = polkaswapChainId, @@ -211,7 +217,7 @@ class PolkaswapInteractorImpl @Inject constructor( dexId to quote } - return if (quotes.isEmpty()) { + return@withContext if (quotes.isEmpty()) { null } else { when (desired) { @@ -228,9 +234,9 @@ class PolkaswapInteractorImpl @Inject constructor( amountInPlanks: BigInteger, market: Market, desired: WithDesired - ): BigInteger { + ): BigInteger = withContext(coroutineContext) { val curMarkets = if (market == Market.SMART) emptyList() else listOf(market) - return polkaswapRepository.estimateSwapFee( + return@withContext polkaswapRepository.estimateSwapFee( polkaswapChainId, bestDex, tokenFromId, @@ -243,7 +249,7 @@ class PolkaswapInteractorImpl @Inject constructor( ) } - override suspend fun fetchAvailableSources(tokenInput: Asset, tokenOutput: Asset, availableDexes: List): Set { + override suspend fun fetchAvailableSources(tokenInput: Asset, tokenOutput: Asset, availableDexes: List): Set = withContext(coroutineContext) { val tokenFromId = requireNotNull(tokenInput.token.configuration.currencyId) val tokenToId = requireNotNull(tokenOutput.token.configuration.currencyId) @@ -252,11 +258,11 @@ class PolkaswapInteractorImpl @Inject constructor( availableMarkets.clear() availableMarkets.putAll(sources) - return sources.values.flatten().toSet() + return@withContext sources.values.flatten().toSet() } - override suspend fun getAvailableDexesForPair(tokenFromId: String, tokenToId: String, dexes: List): List { - return dexes.map { + override suspend fun getAvailableDexesForPair(tokenFromId: String, tokenToId: String, dexes: List): List = withContext(coroutineContext) { + return@withContext dexes.map { val isAvailable = polkaswapRepository.isPairAvailable(polkaswapChainId, tokenFromId, tokenToId, it.toInt()) it.toInt() to isAvailable @@ -272,13 +278,13 @@ class PolkaswapInteractorImpl @Inject constructor( filter: String, markets: List, desired: WithDesired - ): Result { - return polkaswapRepository.swap(polkaswapChainId, dexId, inputAssetId, outputAssetId, amount, limit, filter, markets, desired) + ): Result = withContext(coroutineContext) { + polkaswapRepository.swap(polkaswapChainId, dexId, inputAssetId, outputAssetId, amount, limit, filter, markets, desired) } - override suspend fun calcFakeFee(): BigDecimal { - val feeAsset = getFeeAsset() ?: return BigDecimal.ZERO - val feeAssetId = feeAsset.token.configuration.currencyId ?: return BigDecimal.ZERO + override suspend fun calcFakeFee(): BigDecimal = withContext(coroutineContext) { + val feeAsset = getFeeAsset() ?: return@withContext BigDecimal.ZERO + val feeAssetId = feeAsset.token.configuration.currencyId ?: return@withContext BigDecimal.ZERO val markets = emptyList() val fee = polkaswapRepository.estimateSwapFee( @@ -292,6 +298,6 @@ class PolkaswapInteractorImpl @Inject constructor( markets.backStrings(), WithDesired.INPUT ) - return feeAsset.token.configuration.amountFromPlanks(fee) + return@withContext feeAsset.token.configuration.amountFromPlanks(fee) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8a4ebce241..906a0cd31c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ retrofit = "2.9.0" roomVersion = "2.6.1" rules = "1.5.0" runner = "1.5.2" -sharedFeaturesVersion = "1.1.1.30-FLW" +sharedFeaturesVersion = "1.1.1.31-FLW" shimmerVersion = "0.5.0" sonarqubeGradlePlugin = "3.3" soraUiCore = "0.2.22" diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index ebda156d6e..4e73f6687e 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -276,7 +276,7 @@ class ChainRegistry @Inject constructor( override fun getConnection(chainId: String) = connectionPool.getConnectionOrThrow(chainId) - suspend fun awaitConnection(chainId: ChainId) = connectionPool.awaitConnection(chainId) + override suspend fun awaitConnection(chainId: ChainId) = connectionPool.awaitConnection(chainId) @Deprecated( "Since we have ethereum chains, which don't have runtime, we must use the function with nullable return value", ReplaceWith("getRuntimeOrNull(chainId)") diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt index da17f7ac60..508e22f320 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/ConnectionPool.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.updateAndGet class ConnectionPool @Inject constructor( @@ -30,7 +31,7 @@ class ConnectionPool @Inject constructor( private val externalRequirementFlow: MutableStateFlow, private val nodesSettingsStorage: NodesSettingsStorage, private val networkStateService: NetworkStateService -) : CoroutineScope by CoroutineScope(Dispatchers.Default) { +) : CoroutineScope by CoroutineScope(Dispatchers.Default) { private val poolFlow = MutableStateFlow>(emptyMap()) private val connectionWatcher = MutableStateFlow(Event(Unit)) @@ -39,7 +40,7 @@ class ConnectionPool @Inject constructor( return poolFlow.map { it[chainId] }.filterNotNull().first() } - fun getConnectionOrNull(chainId: ChainId): ChainConnection? = poolFlow.value.getOrDefault(chainId, null) + fun getConnectionOrNull(chainId: ChainId): ChainConnection? = poolFlow.value[chainId] fun getConnectionOrThrow(chainId: ChainId): ChainConnection = poolFlow.value.getValue(chainId) fun setupConnection( @@ -48,65 +49,66 @@ class ConnectionPool @Inject constructor( ): ChainConnection { var isNew = false - val connection = poolFlow.value[chain.id] ?: run { - isNew = true + val connection = poolFlow.updateAndGet { currentPool -> + if (currentPool.containsKey(chain.id)) { + currentPool + } else { + isNew = true - val nodes = chain.nodes.map { - it.fillDwellirApiKey() - } + val nodes = chain.nodes.map { + it.fillDwellirApiKey() + } - ChainConnection( - chain = chain, - socketService = socketServiceProvider.get(), - initialNodes = nodes, - externalRequirementFlow = externalRequirementFlow, - onSelectedNodeChange = { onSelectedNodeChange(chain.id, clearDwellirApiKey(it)) }, - isAutoBalanceEnabled = { nodesSettingsStorage.getIsAutoSelectNodes(chain.id) } - ).also { connection -> - poolFlow.update { pool -> - pool + (chain.id to connection) + val newConnection = ChainConnection( + chain = chain, + socketService = socketServiceProvider.get(), + initialNodes = nodes, + externalRequirementFlow = externalRequirementFlow, + onSelectedNodeChange = { onSelectedNodeChange(chain.id, clearDwellirApiKey(it)) }, + isAutoBalanceEnabled = { nodesSettingsStorage.getIsAutoSelectNodes(chain.id) } + ).also { connection -> + connection.state.onEach { connectionState -> + val newState = when (connectionState) { + is SocketStateMachine.State.Connected -> ChainState.ConnectionStatus.Connected(connectionState.url) + is SocketStateMachine.State.Connecting -> ChainState.ConnectionStatus.Connecting(connectionState.url) + is SocketStateMachine.State.Disconnected -> ChainState.ConnectionStatus.Disconnected + is SocketStateMachine.State.Paused -> ChainState.ConnectionStatus.Paused(connectionState.url) + is SocketStateMachine.State.WaitingForReconnect -> ChainState.ConnectionStatus.Connecting(connectionState.url) + } + + ChainsStateTracker.updateState(chain) { it.copy(connectionStatus = newState) } + + when (connectionState) { + is SocketStateMachine.State.Connected -> networkStateService.notifyConnectionSuccess(chain.id) + is SocketStateMachine.State.WaitingForReconnect -> networkStateService.notifyConnectionProblem(chain.id) + else -> Unit + } + }.launchIn(this@ConnectionPool) } - connection.state.onEach {connectionState -> - val newState = when(connectionState) { - is SocketStateMachine.State.Connected -> ChainState.ConnectionStatus.Connected(connectionState.url) - is SocketStateMachine.State.Connecting -> ChainState.ConnectionStatus.Connecting(connectionState.url) - is SocketStateMachine.State.Disconnected -> ChainState.ConnectionStatus.Disconnected - is SocketStateMachine.State.Paused -> ChainState.ConnectionStatus.Paused(connectionState.url) - is SocketStateMachine.State.WaitingForReconnect -> ChainState.ConnectionStatus.Connecting(connectionState.url) - } - - ChainsStateTracker.updateState(chain) { it.copy(connectionStatus = newState) } - - when(connectionState) { - is SocketStateMachine.State.Connected -> networkStateService.notifyConnectionSuccess(chain.id) - is SocketStateMachine.State.WaitingForReconnect -> networkStateService.notifyConnectionProblem(chain.id) - else -> Unit - } - }.launchIn(this) + + currentPool + (chain.id to newConnection) } - } + }[chain.id]!! if (isNew) { connectionWatcher.tryEmit(Event(Unit)) } - connection.considerUpdateNodes(chain.nodes) + if (connection.chain.nodes != chain.nodes) { + connection.considerUpdateNodes(chain.nodes) + } return connection } fun removeConnection(chainId: ChainId) { val connection = getConnectionOrNull(chainId) - poolFlow.update { - - it.minus(chainId) - } + poolFlow.update { it - chainId } connection?.finish() connectionWatcher.tryEmit(Event(Unit)) networkStateService.notifyConnectionSuccess(chainId) } } - fun ChainNode.fillDwellirApiKey(): ChainNode { return copy(url = fillDwellirApiKey(url)) } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt index 6c5051282f..a0574c8617 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/connection/EthereumConnectionPool.kt @@ -2,9 +2,9 @@ package jp.co.soramitsu.runtime.multiNetwork.connection import android.util.Log import java.net.URI -import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger import jp.co.soramitsu.common.BuildConfig -import jp.co.soramitsu.common.data.network.runtime.binding.cast import jp.co.soramitsu.common.domain.NetworkStateService import jp.co.soramitsu.common.utils.cycle import jp.co.soramitsu.core.models.ChainNode @@ -21,14 +21,18 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope +import okhttp3.OkHttpClient import org.web3j.protocol.Web3j import org.web3j.protocol.Web3jService import org.web3j.protocol.http.HttpService @@ -40,37 +44,46 @@ private const val EVM_CONNECTION_TAG = "EVM Connection" class EthereumConnectionPool( private val networkStateService: NetworkStateService, ) { - private val poolStateFlow = - MutableStateFlow>(mutableMapOf()) + private val poolStateFlow = MutableStateFlow>(mutableMapOf()) + private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob()) suspend fun await(chainId: String): EthereumChainConnection { - return poolStateFlow.map { it.getOrDefault(chainId, null) }.first { it != null }.cast() + return poolStateFlow.map { it[chainId] }.filterNotNull().first() } fun getOrNull(chainId: String): EthereumChainConnection? { - return poolStateFlow.value.getOrDefault(chainId, null) + return poolStateFlow.value[chainId] } fun setupConnection(chain: Chain, onSelectedNodeChange: (chainId: ChainId, newNodeUrl: String) -> Unit): EthereumChainConnection { - if (poolStateFlow.value.containsKey(chain.id)) { - return poolStateFlow.value.getValue(chain.id) - } else { - poolStateFlow.update { prev -> - prev.also { - it[chain.id] = EthereumChainConnection( - chain, - onSelectedNodeChange = onSelectedNodeChange - ) { networkStateService.notifyChainSyncProblem(chain.id) } - } + val connection = poolStateFlow.updateAndGet { currentPool -> + if (currentPool.containsKey(chain.id)) { + currentPool + } else { + val newConnection = EthereumChainConnection( + chain, + onSelectedNodeChange = onSelectedNodeChange + ) { networkStateService.notifyConnectionProblem(chain.id) } + + newConnection.statusFlow.onEach { status -> + if (status is EvmConnectionStatus.Connected) { + networkStateService.notifyConnectionSuccess(chain.id) + } else { + networkStateService.notifyConnectionProblem(chain.id) + } + }.launchIn(scope) + + currentPool.toMutableMap().apply { put(chain.id, newConnection) } } - return poolStateFlow.value.getValue(chain.id) - } + }[chain.id]!! + + return connection } fun stop(chainId: String) { - poolStateFlow.update { prev -> - prev.also { - it.remove(chainId)?.apply { web3j?.shutdown() } + poolStateFlow.update { currentPool -> + currentPool.toMutableMap().apply { + remove(chainId)?.apply { web3j?.shutdown() } } } } @@ -94,7 +107,6 @@ class EthereumChainConnection( private const val WSS_NODE_PREFIX = "wss" } - private val nodes: List = formatWithApiKeys(chain) private var nodesCycle = nodes.cycle().iterator() @@ -104,11 +116,7 @@ class EthereumChainConnection( val statusFlow = MutableStateFlow(null) - private val nodesAttempts = ConcurrentHashMap().also { - nodes.forEach { node -> - it[node.url] = 0 - } - } + private val nodesAttempts = nodes.associate { it.url to AtomicInteger(0) } init { require(chain.isEthereumChain) @@ -124,13 +132,11 @@ class EthereumChainConnection( } onSelectedNodeChange(chain.id, url) } - is EvmConnectionStatus.Error, is EvmConnectionStatus.Closed, null -> { connectNextNode() } - else -> Unit } }.launchIn(scope) @@ -139,20 +145,20 @@ class EthereumChainConnection( private fun connectNextNode() { scope.launch { supervisorScope { - if (nodesAttempts.all { it.value >= 5 }) { + if (nodesAttempts.all { it.value.get() >= 5 }) { allNodesHaveFailed() return@supervisorScope } + delay(1000) // delay between reconnects + val nextNode = nodesCycle.next() - if (nodesAttempts.getOrDefault(nextNode.url, 0) >= 5) { + if ((nodesAttempts[nextNode.url]?.get() ?: 0) >= 5) { statusFlow.update { null } return@supervisorScope } - if (connection != null && connection is EVMConnection.WSS && (connection as EVMConnection.WSS).socket.isOpen) { - connection?.web3j?.shutdown() - } + connection?.web3j?.shutdown() // shutdown previous connection when { nextNode.url.startsWith("wss") -> { @@ -180,7 +186,7 @@ class EthereumChainConnection( } } - nodesAttempts[nextNode.url] = (nodesAttempts[nextNode.url] ?: 0) + 1 + nodesAttempts[nextNode.url]?.incrementAndGet() } } } @@ -218,7 +224,15 @@ sealed class EVMConnection( } class HTTP(url: String, onStatusChanged: (EvmConnectionStatus) -> Unit) : - EVMConnection(url, HttpService(url, false), onStatusChanged) { + EVMConnection( + url, HttpService( + url, OkHttpClient.Builder() + .connectTimeout(60L, TimeUnit.SECONDS) + .writeTimeout(60L, TimeUnit.SECONDS) + .readTimeout(60L, TimeUnit.SECONDS) + .retryOnConnectionFailure(true).build() + ), onStatusChanged + ) { init { onStatusChanged(EvmConnectionStatus.Connecting(url)) runCatching { web3j.ethBlockNumber().send() } From 1f924068439b4e5696cab7bd74f8a38f66858485 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 15:05:06 +0500 Subject: [PATCH 068/100] FLW-4569 There should be new "All done" screen --- .../java/jp/co/soramitsu/app/root/navigation/Navigator.kt | 4 ++-- common/src/main/res/values/strings.xml | 2 ++ .../co/soramitsu/success/presentation/SuccessContent.kt | 8 +++++--- .../co/soramitsu/success/presentation/SuccessFragment.kt | 7 ++++--- .../co/soramitsu/success/presentation/SuccessViewModel.kt | 5 +++-- .../soramitsu/walletconnect/domain/WalletConnectRouter.kt | 3 ++- .../connectioninfo/ConnectionInfoViewModel.kt | 3 ++- .../sessionproposal/SessionProposalViewModel.kt | 6 ++++-- 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt index b2ad6d2055..2ca6d0a52a 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/navigation/Navigator.kt @@ -1020,8 +1020,8 @@ class Navigator : } @SuppressLint("RestrictedApi") - override fun openOperationSuccessAndPopUpToNearestRelatedScreen(operationHash: String?, chainId: ChainId?, customMessage: String?) { - val bundle = SuccessFragment.getBundle(operationHash, chainId, customMessage) + override fun openOperationSuccessAndPopUpToNearestRelatedScreen(operationHash: String?, chainId: ChainId?, customMessage: String?, customTitle: String?) { + val bundle = SuccessFragment.getBundle(operationHash, chainId, customMessage, customTitle) val latestAvailableWalletConnectRelatedDestinationId = navController?.currentBackStack?.replayCache?.firstOrNull()?.last { diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 1e0565afff..bc91c64682 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -304,6 +304,7 @@ Total Transaction Transaction raw data + Transaction sent Transaction submitted Transferable: %s Try again @@ -1038,6 +1039,7 @@ Substrate keypair crypto type Substrate secret derivation path\n You can return to your browser now + Your transaction has been successfully sent to blockchain Support & Feedback Switch node Auto select nodes diff --git a/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessContent.kt b/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessContent.kt index 29f8aa7cb4..0904871225 100644 --- a/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessContent.kt +++ b/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessContent.kt @@ -42,13 +42,14 @@ import jp.co.soramitsu.common.compose.theme.white import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain data class SuccessViewState( + val title: String, val message: String, val tableItems: List, val explorer: Pair? ) { companion object { const val CODE_HASH_CLICK = 2 - val default = SuccessViewState("", emptyList(), null) + val default = SuccessViewState("", "", emptyList(), null) } } @@ -95,7 +96,7 @@ fun SuccessContent( ) MarginVertical(margin = 16.dp) H2( - text = stringResource(id = R.string.all_done), + text = state.title, modifier = Modifier.align(Alignment.CenterHorizontally) ) MarginVertical(margin = 8.dp) @@ -152,7 +153,8 @@ fun SuccessContent( @Composable private fun SuccessPreview() { val state = SuccessViewState( - message = "You can now back to your app and do that you're usually do", + title = "Title HERE", + message = "Your transaction has been successfully sent to blockchain", tableItems = listOf( TitleValueViewState( title = "Hash", diff --git a/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessFragment.kt b/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessFragment.kt index c1ae8792f0..4cd85d75d0 100644 --- a/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessFragment.kt +++ b/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessFragment.kt @@ -31,17 +31,18 @@ class SuccessFragment : BaseComposeBottomSheetDialogFragment() const val KEY_OPERATION_HASH = "KEY_OPERATION_HASH" const val KEY_CHAIN_ID = "KEY_CHAIN_ID" const val KEY_CUSTOM_MESSAGE = "KEY_CUSTOM_MESSAGE" - const val KEY_HAS_SUCCESS_RESULT = "KEY_HAS_SUCCESS_RESULT" + const val KEY_CUSTOM_TITLE = "KEY_CUSTOM_TITLE" fun getBundle( operationHash: String?, chainId: ChainId?, customMessage: String?, - hasSuccessResult: Boolean = true + customTitle: String? = null ) = bundleOf( KEY_OPERATION_HASH to operationHash, KEY_CHAIN_ID to chainId, - KEY_CUSTOM_MESSAGE to customMessage + KEY_CUSTOM_MESSAGE to customMessage, + KEY_CUSTOM_TITLE to customTitle ) } diff --git a/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessViewModel.kt b/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessViewModel.kt index 7f6f01598f..42931011f5 100644 --- a/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessViewModel.kt +++ b/feature-success-impl/src/main/kotlin/jp/co/soramitsu/success/presentation/SuccessViewModel.kt @@ -44,7 +44,7 @@ class SuccessViewModel @Inject constructor( val operationHash = savedStateHandle.get(SuccessFragment.KEY_OPERATION_HASH) val chainId = savedStateHandle.get(SuccessFragment.KEY_CHAIN_ID) private val customMessage: String? = savedStateHandle[SuccessFragment.KEY_CUSTOM_MESSAGE] - private val hasSuccessResult: Boolean = savedStateHandle[SuccessFragment.KEY_HAS_SUCCESS_RESULT] ?: true + private val customTitle: String? = savedStateHandle[SuccessFragment.KEY_CUSTOM_TITLE] private val _showHashActions = MutableLiveData>() val showHashActions: LiveData> = _showHashActions @@ -82,7 +82,8 @@ class SuccessViewModel @Inject constructor( val state: StateFlow = explorerPairFlow.map { explorer -> SuccessViewState( - message = customMessage ?: resourceManager.getString(R.string.return_to_app_message), + title = customTitle ?: resourceManager.getString(R.string.common_transaction_sent), + message = customMessage ?: resourceManager.getString(R.string.success_message_transaction_sent), tableItems = getInfoTableItems(), explorer = explorer ) diff --git a/feature-walletconnect-api/src/main/java/co/jp/soramitsu/walletconnect/domain/WalletConnectRouter.kt b/feature-walletconnect-api/src/main/java/co/jp/soramitsu/walletconnect/domain/WalletConnectRouter.kt index c3b788496e..e9771463e5 100644 --- a/feature-walletconnect-api/src/main/java/co/jp/soramitsu/walletconnect/domain/WalletConnectRouter.kt +++ b/feature-walletconnect-api/src/main/java/co/jp/soramitsu/walletconnect/domain/WalletConnectRouter.kt @@ -18,7 +18,8 @@ interface WalletConnectRouter { fun openOperationSuccessAndPopUpToNearestRelatedScreen( operationHash: String?, chainId: ChainId?, - customMessage: String? + customMessage: String?, + customTitle: String? = null ) fun openSelectMultipleChains( diff --git a/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/connectioninfo/ConnectionInfoViewModel.kt b/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/connectioninfo/ConnectionInfoViewModel.kt index a7c0e62f60..0eadb47c28 100644 --- a/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/connectioninfo/ConnectionInfoViewModel.kt +++ b/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/connectioninfo/ConnectionInfoViewModel.kt @@ -135,7 +135,8 @@ class ConnectionInfoViewModel @Inject constructor( walletConnectRouter.openOperationSuccessAndPopUpToNearestRelatedScreen( null, null, - resourceManager.getString(R.string.connection_disconnect_success_message, dappName) + resourceManager.getString(R.string.connection_disconnect_success_message, dappName), + resourceManager.getString(R.string.all_done) ) } }, diff --git a/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/sessionproposal/SessionProposalViewModel.kt b/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/sessionproposal/SessionProposalViewModel.kt index 86c078b1af..ef3079dd37 100644 --- a/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/sessionproposal/SessionProposalViewModel.kt +++ b/feature-walletconnect-impl/src/main/java/jp/co/soramitsu/walletconnect/impl/presentation/sessionproposal/SessionProposalViewModel.kt @@ -229,7 +229,8 @@ class SessionProposalViewModel @Inject constructor( walletConnectRouter.openOperationSuccessAndPopUpToNearestRelatedScreen( null, null, - resourceManager.getString(R.string.connection_approve_success_message, proposal.name) + resourceManager.getString(R.string.connection_approve_success_message, proposal.name), + resourceManager.getString(R.string.all_done) ) } isApproving.value = false @@ -273,7 +274,8 @@ class SessionProposalViewModel @Inject constructor( walletConnectRouter.openOperationSuccessAndPopUpToNearestRelatedScreen( null, null, - resourceManager.getString(R.string.common_rejected) + resourceManager.getString(R.string.common_rejected), + resourceManager.getString(R.string.all_done) ) } } From b13fcc4673288116408ddf47299986d803cc07a3 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 15:36:41 +0500 Subject: [PATCH 069/100] update strings from lokalise --- common/src/main/res/values-in/strings.xml | 6 ++++-- common/src/main/res/values-ja/strings.xml | 2 ++ common/src/main/res/values-pt/strings.xml | 2 ++ common/src/main/res/values-ru/strings.xml | 7 +++++++ common/src/main/res/values-tr/strings.xml | 2 ++ common/src/main/res/values-vi/strings.xml | 13 ++++++++++++- common/src/main/res/values-zh/strings.xml | 6 ++++++ common/src/main/res/values/strings.xml | 10 +++++++--- 8 files changed, 42 insertions(+), 6 deletions(-) diff --git a/common/src/main/res/values-in/strings.xml b/common/src/main/res/values-in/strings.xml index c05bf3e97e..cd5f2ee784 100644 --- a/common/src/main/res/values-in/strings.xml +++ b/common/src/main/res/values-in/strings.xml @@ -390,7 +390,7 @@ Pelajari crowdloan %s Pelajari tentang bonus %s Jangka waktu sewa - Pilih terjun payung untuk menyumbangkan %s. Anda akan menerima hadiah jika parachain memperoleh slot pada akhir lelang. + Pilih terjun payung untuk menyumbangkan. Anda akan menerima hadiah jika parachain memperoleh slot pada akhir lelang. Crowdfloans aktif \n akan muncul di sini Saya sudah membaca dan setuju atas Ketentuan dan Kebijakan Privasi Dibesarkan @@ -742,11 +742,12 @@ Kirim ke Mengirim… Atur jumlah maximal + Sembunyikan saldo Nol Bagikan kode rujukan Sosial media Jumlah yang Anda coba transfer tidak cukup untuk menutupi biaya transaksi di %s jaringan. Meskipun transaksi tidak dapat diproses, Anda tetap akan dikenakan biaya di jaringan Sora. Saat ini,ada jumlah minimal%suntuk menjembatani guna menjamin stabilitas dan keamanan jaringan SORA. Kami menghargai pengertian anda. - 0 € biaya layanan tahunan + €0 biaya layanan tahunan Tidak termasuk aplikasi negara tertentu
tidak dapat mengajukan untuk Kartu SORA saat ini
See the list]]>
Aktifkan kartu @@ -1072,6 +1073,7 @@ Vesting Terkunci Lihat di %s Lihat dompet + Kamu sudah menyembunyikan semua aset Beli dengan Menerima Terima %s diff --git a/common/src/main/res/values-ja/strings.xml b/common/src/main/res/values-ja/strings.xml index e6779ed622..4081b7217f 100644 --- a/common/src/main/res/values-ja/strings.xml +++ b/common/src/main/res/values-ja/strings.xml @@ -725,6 +725,7 @@ 資金を送る 送信先 送信中 + 残高ゼロを隠す 紹介コードを共有 ソーシャル・メディア 現在、SORAネットワークの安定性と安全性を確保するため、ブリッジングには最低%sが設定されています。ご理解のほどよろしくお願いいたします。 @@ -1049,6 +1050,7 @@ バリデーターが見つかりません %sで見る ウォレットを見る + 全アセットを非表示にしました で購入 受け取る 受信 %s diff --git a/common/src/main/res/values-pt/strings.xml b/common/src/main/res/values-pt/strings.xml index 00d7a24176..9c2cdb7660 100644 --- a/common/src/main/res/values-pt/strings.xml +++ b/common/src/main/res/values-pt/strings.xml @@ -723,6 +723,7 @@ Enviar fundos Enviar para Enviando... + Ocultar saldos nulos Partilhar código de referência Mídia social €0 de taxa de serviço anual @@ -1047,6 +1048,7 @@ Não foram encontrados validadores Ver em %s Ver carteira + Você escondeu todos os ativos Comprar %s com Receber Receber %s diff --git a/common/src/main/res/values-ru/strings.xml b/common/src/main/res/values-ru/strings.xml index a4580dacb9..edc09f5d79 100644 --- a/common/src/main/res/values-ru/strings.xml +++ b/common/src/main/res/values-ru/strings.xml @@ -205,6 +205,12 @@ Модуль Сеть Комиссия сети + + %d сеть + %d сети + %d сетей + %d сеть + Далее Нет Не делайте скриншоты, которые могут быть собраны сторонними вредоносными программами @@ -608,6 +614,7 @@ DISCLAIMER: Предложения алгоритмических валидаторов не являются финансовыми консультациями или советами. Стейкинг - это деятельность с высоким риском, и предложения алгоритмических валидаторов не обязательно снижают этот риск. Валидатор, предложенный алгоритмом, все равно может быть слэширован. Валидатор, предложенный алгоритмом, также может изменить свои параметры (например, размер комиссии и т.д.) в любое время после того, как он был предложен и/или выбран. Вы можете потерять токены или вознаграждения по этим или другим причинам. Ставьте токены и используйте предложения валидаторов только по своему усмотрению, после проведения должной проверки и тщательного рассмотрения сопутствующих рисков. Отправить Средства Отправка + Скрыть нулевые балансы Поделиться реферальным кодом Социальные сети/медиа 0 € годовой сервисный сбор diff --git a/common/src/main/res/values-tr/strings.xml b/common/src/main/res/values-tr/strings.xml index 8b420e8424..50de2725a1 100644 --- a/common/src/main/res/values-tr/strings.xml +++ b/common/src/main/res/values-tr/strings.xml @@ -743,6 +743,7 @@ Gönderildi Gönderiliyor… Maksimum tutarı ayarla + Sıfır bakiyeleri gizleyin Referans kodunuzu paylaşın Sosyal medya Aktarmaya çalıştığınız tutar, işlem ücretlerini karşılamaya yetmiyor. %sAğ İşlem gerçekleşmese de Sora ağındaki ücretler sizden yine de tahsil edilecektir. @@ -1074,6 +1075,7 @@ Vesting Kilitli %s\'da görüntüle Cüzdanı görün + Değiştirilebilir jetonlar %s ile al Al Alın%s diff --git a/common/src/main/res/values-vi/strings.xml b/common/src/main/res/values-vi/strings.xml index 485556dfc8..85397407cd 100644 --- a/common/src/main/res/values-vi/strings.xml +++ b/common/src/main/res/values-vi/strings.xml @@ -91,6 +91,7 @@ Hiển thị cụm từ ghi nhớ Hiển thị Raw Seed Nếu bạn mất quyền truy cập vào thiết bị này, tiền của bạn sẽ bị mất, trừ khi bạn sao lưu! + Bị chặn Quản trị Pool thanh khoản Nhóm Nomination @@ -244,6 +245,9 @@ Mạng Phí mạng Quản lý mạng + + mạng + Kế tiếp Không Không chụp ảnh màn hình vì có thể bị phần mềm độc hại của bên thứ ba thu thập @@ -287,6 +291,7 @@ Bỏ qua quá trình Sắp xếp theo Staking + Bắt đầu cho đến %s Thời gian còn lại Chuỗi chéo @@ -506,6 +511,7 @@ Thêm tài khoản Chuyển node Thêm tài khoản + Lỗi kết nối: Không thể kết nối mạng. Vui lòng thử lại. Mạng không khả dụng Node không khả dụng Sự cố mạng @@ -742,10 +748,11 @@ Gửi đến Đang gửi Số tiền tối đa + Ẩn số dư bằng không Chia sẻ mã giới thiệu Truyền thông xã hội Số tiền bạn đang cố chuyển không đủ để trả phí giao dịch trên mạng %s . Mặc dù giao dịch không được xử lý nhưng bạn vẫn sẽ bị tính phí trên mạng Sora. - Hiện tại, có một phút. số tiền %s để bắc cầu nhằm đảm bảo tính ổn định và bảo mật của Mạng SORA. Chúng tôi đánh giá cao sự hiểu biết của bạn. + Hiện tại, cần số tiền tối thiếu là %s để bắc cầu nhằm đảm bảo tính ổn định và bảo mật của Mạng SORA. Cảm ơn vì sự thông cảm của bạn. 0 € phí dịch vụ hàng năm Không bao gồm ứng dụng một số quốc gia
không thể đăng ký Thẻ SORA tại thời điểm
nàyXem danh sách]]>
@@ -1066,6 +1073,8 @@ Tên này chỉ hiển thị với bạn và lưu trên điện thoại cá nhân. Tạo một ví mới Sứ mệnh + Lượng token tối thiểu để stake ở nominator này là %s . Để nhận được phần thưởng, bạn phải stake nhiều hơn. + Lượng token tối thiểu để stake với những nominator đang hoạt động Tài khoản này không được mạng (network) đề cử ở era hiện tại Không tìm thấy validator nào Do lịch trình trao quyền duy nhất của mỗi parachain, ứng dụng của chúng tôi không thể hiển thị số lượng mã thông báo bị khóa đủ điều kiện để yêu cầu. Xin lưu ý rằng việc bắt đầu yêu cầu bồi thường có thể không thực tế nếu số tiền tiềm năng là rất nhỏ và có thể so sánh được với phí giao dịch liên quan. Để có thông tin chi tiết toàn diện về số dư bị khóa của bạn, hãy tham khảo Subscan blockexplorer. Chúng tôi khuyên bạn nên đánh giá thông tin một cách cẩn thận và tiến hành theo ý riêng của bạn. @@ -1073,6 +1082,7 @@ Vesting Locked Xem trong %s Xem ví + Bạn đã ẩn tất cả nội dung Mua %s với Nhận Nhận %s @@ -1106,6 +1116,7 @@ Số dư tối thiểu Xác nhận chuyển khoản Giao dịch chuyển tiền của bạn sẽ không thành công vì số tiền cuối cùng trên tài khoản đích sẽ ít hơn số dư tối thiểu. Hãy cố gắng tăng số lượng. + Chuyển khoản của bạn sẽ không thành công vì số tiền cuối cùng (%s) trên tài khoản đích sẽ nhỏ hơn số dư tối thiểu (%s). Vui lòng tăng thêm %s Số dư Ethereum không đủ trong tài khoản của người nhận sẽ ngăn cản việc hoàn tất chuyển mã thông báo ERC20. Vui lòng đảm bảo người nhận có đủ Ethereum để tiến hành chuyển khoản. Chuyển khoản của bạn sẽ xóa tài khoản khỏi blockstore vì nó sẽ làm cho tổng số dư còn lại thấp hơn số dư tối thiểu. Chuyển khoản sẽ xóa tài khoản diff --git a/common/src/main/res/values-zh/strings.xml b/common/src/main/res/values-zh/strings.xml index bb3e547c32..113c1f43ea 100644 --- a/common/src/main/res/values-zh/strings.xml +++ b/common/src/main/res/values-zh/strings.xml @@ -155,6 +155,7 @@ 数量 金额太低 + %s和其他 已应用 应用 法币余额 @@ -176,6 +177,7 @@ 领取 可领取 关闭 + 佣金 已完成 已完成( %s ) 确认 @@ -184,6 +186,7 @@ 已确认 连接 连接问题 + 联系人 继续 复制到剪贴板 复制 @@ -342,6 +345,7 @@ 按连接搜索 联系地址 联系人姓名 + 未找到联系人 贡献类型 直接DOT lcDOT @@ -738,6 +742,7 @@ 发送至 发送 设置最大金额 + 隐藏零余额 分享推荐码 社交媒体 您尝试转账的金额不足以支付%s网络上的交易费用。尽管交易无法完成,您仍将在Sora网络上被收取费用。 @@ -1068,6 +1073,7 @@ 归属锁定 在%s中查看 查看钱包 + 你已经隐藏所有资产 购买%s用 接收 接收%s diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index bc91c64682..c9900f0d0d 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -91,6 +91,7 @@ Show mnemonic phrase Show Raw Seed If you loose access to this device, your funds will be lost, unless you back up! + Blocked Governance Liquidity Pools Nomination Pools @@ -244,12 +245,12 @@ Module My networks Network + Network fee + Network management %d network %d networks - Network fee - Network management Next No Do not take screenshots, they may be collected by third-party malware @@ -753,11 +754,12 @@ Send to Sending Set max amount + Hide zero balances Share referral code Social Media The amount you\'re trying to transfer is insufficient to cover the transaction fees on the %s network. Although the transaction won\'t process, you\'ll still be charged the fees on the Sora network. Currently, there\'s a min. amount %s for bridging to ensure the stability and security of the SORA Network. We appreciate your understanding. - 0 € annual service fee + €0 annual service fee Excluded of application certain countries
can not apply for SORA Card at this moment
See the list]]>
Enable card @@ -1078,6 +1080,8 @@ This name will be displayed only to you and stored locally on your mobile device. Create a new wallet Comission + Minimum stake among active nominators is %s. To get rewards you have to stake more. + Minimum stake among active nominators This account has not been elected by the network to participate in the current era No validators found Due to the unique vesting schedules of each parachain, our app is unable to display the quantity of locked tokens eligible for claiming. Please be advised that initiating a claim may be impractical if the prospective amount is marginal and comparable to the transaction fee involved. For comprehensive insights into your locked balances, consult the Subscan blockexplorer. We urge you to assess the information carefully and proceed at your own discretion. From 31ea505a98e8df010a98dfc2eff66e43d75c67b8 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 21:47:32 +0500 Subject: [PATCH 070/100] FLW-4603 Updating. There is not onboarding --- feature-onboarding-impl/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-onboarding-impl/build.gradle b/feature-onboarding-impl/build.gradle index 2181613ce9..b4d6ce7984 100644 --- a/feature-onboarding-impl/build.gradle +++ b/feature-onboarding-impl/build.gradle @@ -22,7 +22,7 @@ android { } release { - buildConfigField "String", "ONBOARDING_CONFIG", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/develop-free/appConfigs/onboarding/mobile.json\"" + buildConfigField "String", "ONBOARDING_CONFIG", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/develop-free/appConfigs/onboarding/mobile%20v2.json\"" } } From c9bac979fcd851755d6c6154a2ef71e1dfc12471 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 21 May 2024 21:57:27 +0500 Subject: [PATCH 071/100] FLW-4605 Import wallet by JSON. There should be Upload file --- .../presentation/importing/source/view/JsonPasteOptionsSheet.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/source/view/JsonPasteOptionsSheet.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/source/view/JsonPasteOptionsSheet.kt index 526acfafde..b57efac648 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/source/view/JsonPasteOptionsSheet.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/source/view/JsonPasteOptionsSheet.kt @@ -21,7 +21,7 @@ class JsonPasteOptionsSheet( onPaste() } - item(icon = R.drawable.ic_file_upload, titleRes = R.string.recover_json_hint) { + item(icon = R.drawable.ic_file_upload, titleRes = R.string.common_choose_file) { onOpenFile() } } From 8c05f54d5377225ae524902751a15986939a2504 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 22 May 2024 11:23:25 +0500 Subject: [PATCH 072/100] FLW-4604 We have imported only Substrate. There is no alert icon on the Settings --- .../details/AccountDetailsInteractor.kt | 21 +++++++++++++++++++ .../presentation/profile/ProfileFragment.kt | 2 +- .../presentation/profile/ProfileViewModel.kt | 11 ++++------ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/account/details/AccountDetailsInteractor.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/account/details/AccountDetailsInteractor.kt index a92dc3531a..cf90f5662b 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/account/details/AccountDetailsInteractor.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/domain/account/details/AccountDetailsInteractor.kt @@ -18,8 +18,10 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.runtime.multiNetwork.chain.model.defaultChainSort import jp.co.soramitsu.shared_utils.scale.EncodableStruct +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map class AccountDetailsInteractor( @@ -56,6 +58,25 @@ class AccountDetailsInteractor( } } + @OptIn(ExperimentalCoroutinesApi::class) + fun hasChainsWithNoAccount() = accountRepository.selectedMetaAccountFlow() + .flatMapLatest { metaAccount -> + combine( + chainRegistry.currentChains.map { it.sortedWith(chainSort()) }, + assetNotNeedAccountUseCase.getAssetsMarkedNotNeedFlow(metaAccount.id) + ) { chains, assetsMarkedNotNeed -> + chains.any { chain -> + chain.assets.any { chainAsset -> + val markedNotNeed = assetsMarkedNotNeed.contains( + AssetKey(metaAccount.id, chain.id, emptyAccountIdValue, chainAsset.id) + ) + val hasAccount = !chain.isEthereumBased || metaAccount.ethereumPublicKey != null || metaAccount.hasChainAccount(chain.id) + hasAccount.not() && markedNotNeed.not() + } + } + } + } + private fun createAccountInChain(metaAccount: MetaAccount, chain: Chain, markedNotNeed: Boolean): AccountInChain { val address = metaAccount.address(chain) val accountId = metaAccount.accountId(chain) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt index 3368ffd84a..875259f5d4 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileFragment.kt @@ -52,7 +52,7 @@ class ProfileFragment : BaseFragment() { profileSoraCard.setOnClickListener { viewModel.onSoraCardClicked() } profileWalletConnect.setOnClickListener { viewModel.onWalletConnectClick() } - viewModel.hasMissingAccountsFlow.observe { + viewModel.hasChainsWithNoAccountFlow.observe { missingAccountsIcon.isVisible = it } } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt index d89fdbaad5..541a07fba9 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/profile/ProfileViewModel.kt @@ -11,6 +11,7 @@ import jp.co.soramitsu.account.api.domain.interfaces.AccountInteractor import jp.co.soramitsu.account.api.domain.interfaces.TotalBalanceUseCase import jp.co.soramitsu.account.api.domain.model.MetaAccount import jp.co.soramitsu.account.api.presentation.actions.ExternalAccountActions +import jp.co.soramitsu.account.impl.domain.account.details.AccountDetailsInteractor import jp.co.soramitsu.account.impl.presentation.AccountRouter import jp.co.soramitsu.account.impl.presentation.language.mapper.mapLanguageToLanguageModel import jp.co.soramitsu.common.address.AddressIconGenerator @@ -22,14 +23,11 @@ import jp.co.soramitsu.common.data.network.coingecko.FiatCurrency import jp.co.soramitsu.common.domain.GetAvailableFiatCurrencies import jp.co.soramitsu.common.domain.SelectedFiat import jp.co.soramitsu.common.resources.ResourceManager -import jp.co.soramitsu.common.utils.Event import jp.co.soramitsu.common.utils.formatFiat import jp.co.soramitsu.common.view.bottomSheet.list.dynamic.DynamicListBottomSheet -import jp.co.soramitsu.feature_account_impl.R import jp.co.soramitsu.soracard.api.domain.SoraCardInteractor import jp.co.soramitsu.soracard.impl.presentation.SoraCardItemViewState import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletInteractor -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.firstOrNull @@ -43,7 +41,7 @@ private const val AVATAR_SIZE_DP = 32 @HiltViewModel class ProfileViewModel @Inject constructor( private val interactor: AccountInteractor, - private val walletInteractor: WalletInteractor, + private val accountDetailsInteractor: AccountDetailsInteractor, private val soraCardInteractor: SoraCardInteractor, private val router: AccountRouter, private val addressIconGenerator: AddressIconGenerator, @@ -76,9 +74,8 @@ class ProfileViewModel @Inject constructor( val selectedFiatLiveData: LiveData = selectedFiat.flow().asLiveData().map { it.uppercase() } - val hasMissingAccountsFlow = walletInteractor.assetsFlow().map { - it.any { it.hasAccount.not() } - }.stateIn(this, SharingStarted.Eagerly, false) + val hasChainsWithNoAccountFlow = accountDetailsInteractor.hasChainsWithNoAccount() + .stateIn(this, SharingStarted.Eagerly, false) // private val soraCardState = soraCardInteractor.subscribeSoraCardInfo().map { // val kycStatus = it?.kycStatus?.let(::mapKycStatus) From 078140d5056ca241097d2d02fc6f3258f61661d0 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 22 May 2024 13:51:58 +0500 Subject: [PATCH 073/100] fix google backup crash --- .../remote_backup/ImportRemoteWalletDialog.kt | 24 +++++++++++++++++++ .../ImportRemoteWalletViewModel.kt | 20 +++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/remote_backup/ImportRemoteWalletDialog.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/remote_backup/ImportRemoteWalletDialog.kt index 92aeaf2666..a0dbde9a37 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/remote_backup/ImportRemoteWalletDialog.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/remote_backup/ImportRemoteWalletDialog.kt @@ -1,23 +1,43 @@ package jp.co.soramitsu.account.impl.presentation.importing.remote_backup +import android.app.Activity +import android.content.Intent import android.os.Bundle import android.view.View import android.widget.FrameLayout +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.core.os.bundleOf import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import com.google.android.material.bottomsheet.BottomSheetBehavior import dagger.hilt.android.AndroidEntryPoint import jp.co.soramitsu.common.base.BaseComposeBottomSheetDialogFragment +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach @AndroidEntryPoint class ImportRemoteWalletDialog : BaseComposeBottomSheetDialogFragment() { override val viewModel: ImportRemoteWalletViewModel by viewModels() + private val launcher: ActivityResultLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + when (result.resultCode) { + Activity.RESULT_OK -> viewModel.loadRemoteWallets() + Activity.RESULT_CANCELED -> { /* no action */ } + else -> { + val googleSignInStatus = result.data?.extras?.get("googleSignInStatus") + viewModel.onGoogleLoginError(googleSignInStatus.toString()) + } + } + } + @Composable override fun Content(padding: PaddingValues) { val state by viewModel.state.collectAsState() @@ -40,5 +60,9 @@ class ImportRemoteWalletDialog : BaseComposeBottomSheetDialogFragment>() + private val defaultTextInputViewState = TextInputViewState( text = "", hint = resourceManager.getString(R.string.import_remote_wallet_hint_enter_password), @@ -156,7 +160,13 @@ class ImportRemoteWalletViewModel @Inject constructor( override fun loadRemoteWallets() { viewModelScope.launch { - val backupAccounts = backupService.getBackupAccounts().map(::getWrapped) + val backupAccounts = try { + backupService.getBackupAccounts().map(::getWrapped) + } catch (e: AuthConsentException) { + requestGoogleAuth.emit(Event(e.intent)) + return@launch + } + val webBackupAccounts = backupService.getWebBackupAccounts() .distinctBy { it.address } .map { getWrapped(it, origin = BackupOrigin.WEB) } @@ -168,6 +178,10 @@ class ImportRemoteWalletViewModel @Inject constructor( } } + fun onGoogleLoginError(message: String) { + showError("GoogleLoginError\n$message") + } + private fun getWrapped(backupMeta: BackupAccountMeta, origin: BackupOrigin = BackupOrigin.APP): WrappedBackupAccountMeta { return WrappedBackupAccountMeta(backupMeta, origin) } From cc34049784401076f303e40a33a1b5a75b7d470f Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 22 May 2024 14:14:25 +0500 Subject: [PATCH 074/100] FLW-4607 Create new wallet. There are 2 alerts --- .../impl/presentation/importing/ImportAccountFragment.kt | 4 ---- .../impl/presentation/importing/ImportAccountViewModel.kt | 8 +++----- .../mnemonic/backup/BackupMnemonicFragment.kt | 4 ---- .../mnemonic/backup/BackupMnemonicViewModel.kt | 5 ----- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountFragment.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountFragment.kt index 31c4ae718c..6c2c884b1f 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountFragment.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountFragment.kt @@ -129,10 +129,6 @@ class ImportAccountFragment : BaseFragment() { setupAdvancedBlock(blockchainType, sourceType, isChainAccount) } }.observe { } - - viewModel.showInvalidSubstrateDerivationPathError.observeEvent { - showError(resources.getString(R.string.common_invalid_hard_soft_numeric_password_message)) - } } private fun buildSourceTypesViews(blockchainType: ImportAccountType) = viewModel.sourceTypes.map { diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountViewModel.kt index 2400692638..1c334f03bb 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/importing/ImportAccountViewModel.kt @@ -28,6 +28,7 @@ import jp.co.soramitsu.account.impl.presentation.importing.source.model.ImportSo import jp.co.soramitsu.account.impl.presentation.importing.source.model.JsonImportSource import jp.co.soramitsu.account.impl.presentation.importing.source.model.MnemonicImportSource import jp.co.soramitsu.account.impl.presentation.importing.source.model.RawSeedImportSource +import jp.co.soramitsu.account.impl.presentation.mnemonic.backup.exceptions.NotValidDerivationPath import jp.co.soramitsu.common.base.BaseViewModel import jp.co.soramitsu.common.resources.ClipboardManager import jp.co.soramitsu.common.resources.ResourceManager @@ -79,9 +80,6 @@ class ImportAccountViewModel @Inject constructor( val substrateDerivationPathLiveData = MutableLiveData() val ethereumDerivationPathLiveData = MutableLiveData() - private val _showInvalidSubstrateDerivationPathError = MutableLiveData>() - val showInvalidSubstrateDerivationPathError: LiveData> = _showInvalidSubstrateDerivationPathError - private val substrateDerivationPathRegex = Regex("(//?[^/]+)*(///[^/]+)?") val sourceTypes = provideSourceType() @@ -126,7 +124,7 @@ class ImportAccountViewModel @Inject constructor( _blockchainTypeLiveData.value = importAccountType } initialBlockchainType.value != null -> _blockchainTypeLiveData.value = initialBlockchainType.value?.let { int -> - ImportAccountType.values().getOrNull(int) + ImportAccountType.entries.getOrNull(int) }!! } } @@ -148,7 +146,7 @@ class ImportAccountViewModel @Inject constructor( fun nextClicked() { val isSubstrateDerivationPathValid = substrateDerivationPathLiveData.value?.matches(substrateDerivationPathRegex) if (isSubstrateDerivationPathValid == false) { - _showInvalidSubstrateDerivationPathError.value = Event(Unit) + showError(NotValidDerivationPath(resourceManager)) return } val source = _selectedSourceTypeLiveData.value diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicFragment.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicFragment.kt index eb544cf33a..b1625d280e 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicFragment.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicFragment.kt @@ -108,10 +108,6 @@ class BackupMnemonicFragment : BaseFragment(R.layout.fr showMnemonicInfoDialog() } - viewModel.showInvalidSubstrateDerivationPathError.observeEvent { - showError(resources.getString(R.string.common_invalid_hard_soft_numeric_password_message)) - } - viewModel.chainAccountImportType.observe(binding.advancedBlockView::configureForMnemonic) } diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicViewModel.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicViewModel.kt index 5f56f93c4f..a18dafb0f3 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicViewModel.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/account/impl/presentation/mnemonic/backup/BackupMnemonicViewModel.kt @@ -94,9 +94,6 @@ class BackupMnemonicViewModel @Inject constructor( private val _showInfoEvent = MutableLiveData>() val showInfoEvent: LiveData> = _showInfoEvent - private val _showInvalidSubstrateDerivationPathError = MutableLiveData>() - val showInvalidSubstrateDerivationPathError: LiveData> = _showInvalidSubstrateDerivationPathError - fun homeButtonClicked() { router.backToCreateAccountScreen() } @@ -164,7 +161,6 @@ class BackupMnemonicViewModel @Inject constructor( val isSubstrateDerivationPathValid = substrateDerivationPath.matches(substrateDerivationPathRegex) if (isSubstrateDerivationPathValid.not()) { - _showInvalidSubstrateDerivationPathError.value = Event(Unit) showError(NotValidDerivationPath(resourceManager)) return } @@ -212,7 +208,6 @@ class BackupMnemonicViewModel @Inject constructor( ) { val isSubstrateDerivationPathValid = substrateDerivationPath.matches(substrateDerivationPathRegex) if (isSubstrateDerivationPathValid.not()) { - _showInvalidSubstrateDerivationPathError.value = Event(Unit) showError(NotValidDerivationPath(resourceManager)) return } From feb2736231e9ddf7ad89187ea2cdea30e6f0c505 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 23 May 2024 16:34:18 +0500 Subject: [PATCH 075/100] buildUp --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a8be999240..490f75cfd2 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 175 + versionCode = 176 // SDK and tools compileSdkVersion = 34 From 3542a7eafa33e41f81490551c892e659a86e5651 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 23 May 2024 17:15:47 +0500 Subject: [PATCH 076/100] FLW-4614 DOT. XCM. There is not possibility to delete the address --- .../presentation/cross_chain/setup/CrossChainSetupViewModel.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/setup/CrossChainSetupViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/setup/CrossChainSetupViewModel.kt index 1241970b4c..a3c17b9da7 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/setup/CrossChainSetupViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/presentation/cross_chain/setup/CrossChainSetupViewModel.kt @@ -403,8 +403,7 @@ class CrossChainSetupViewModel @Inject constructor( } else { R.drawable.ic_address_placeholder }, - editable = false, - showClear = false + editable = false ), originChainSelectorState = originChainSelectorState, destinationChainSelectorState = destinationChainSelectorState, From cfcce99483158fcf14767d8f1f35037db4fa9d64 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Thu, 23 May 2024 21:56:56 +0500 Subject: [PATCH 077/100] fix SQL max variables limit exceeded crash --- .../co/soramitsu/runtime/storage/DbStorageCache.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/storage/DbStorageCache.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/storage/DbStorageCache.kt index 4bcf8cb685..df83f0a214 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/storage/DbStorageCache.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/storage/DbStorageCache.kt @@ -16,6 +16,8 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext +private const val SQLITE_MAX_VARIABLE_NUMBER = 900 + class DbStorageCache( private val storageDao: StorageDao ) : StorageCache { @@ -66,10 +68,12 @@ class DbStorageCache( } override suspend fun getEntries(fullKeys: List, chainId: String): List { - return storageDao.observeEntries(chainId, fullKeys) - .filter { it.size == fullKeys.size } - .mapList { mapStorageEntryFromLocal(it) } - .first() + return fullKeys.chunked(SQLITE_MAX_VARIABLE_NUMBER).map { chunkKeys -> + storageDao.observeEntries(chainId, chunkKeys) + .filter { it.size == chunkKeys.size } + .mapList { mapStorageEntryFromLocal(it) } + .first() + }.flatten() } } From e649aaed47b84c14757d64eb092057ff21bef34e Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 24 May 2024 16:05:21 +0700 Subject: [PATCH 078/100] FLW-4617 fixed bug on staking --- .../app/root/presentation/RootViewModel.kt | 1 - .../common/di/modules/NetworkModule.kt | 4 +- .../updaters/StakingLedgerUpdater.kt | 2 +- .../StakingRelaychainScenarioViewModel.kt | 57 +++++++++---------- .../impl/scenarios/StakingPoolInteractor.kt | 2 +- 5 files changed, 29 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index f574b11d01..40b3426436 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -188,7 +188,6 @@ class RootViewModel @Inject constructor( val showConnectingBar: StateFlow = _showConnectingBar fun onNetworkAvailable() { _showConnectingBar.update { false } - externalConnectionRequirementFlow.value = ChainConnection.ExternalRequirement.ALLOWED // todo this code triggers redundant requests and balance updates. Needs research // viewModelScope.launch { // checkAppVersion() diff --git a/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt b/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt index 64b7b7cdc2..4af739ce87 100644 --- a/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt +++ b/common/src/main/java/jp/co/soramitsu/common/di/modules/NetworkModule.kt @@ -86,9 +86,7 @@ class NetworkModule { @Provides @Singleton - fun provideSocketFactory() = WebSocketFactory().also { - it.connectionTimeout = 10000 - } + fun provideSocketFactory() = WebSocketFactory() @Provides @Singleton diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt index b7aacb56f2..6c61c2f2c9 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/updaters/StakingLedgerUpdater.kt @@ -63,7 +63,7 @@ class StakingLedgerUpdater( val currentAccountId = scope.getAccount().accountId(chain)!! // TODO ethereum val key = runtime.metadata.staking().storage("Bonded").storageKey(runtime, currentAccountId) - runtime.metadata.staking().calls?.get("setController")?.arguments + updatesMixin.startUpdateAsset(scope.getAccount().id, chain.id, currentAccountId, chainAsset.id) return storageSubscriptionBuilder.subscribe(key) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt index ecee429d4f..0a643b6ffe 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/staking/main/scenarios/StakingRelaychainScenarioViewModel.kt @@ -77,46 +77,41 @@ class StakingRelaychainScenarioViewModel( ) ) - private val viewStatesCash: MutableMap = mutableMapOf() - override val stakingStateFlow: Flow = scenarioInteractor.stakingStateFlow().shareIn(baseViewModel.stakingStateScope, SharingStarted.Eagerly, 1) override val stakingViewStateFlowOld: Flow = stakingStateFlow.distinctUntilChanged().map { stakingState -> - val key = "${stakingState.accountId.toHexString()}:${stakingState.chain.id}" - viewStatesCash.getOrPut(key) { - when (stakingState) { - is StakingState.Stash.Nominator -> stakingViewStateFactory.createNominatorViewState( - stakingState, - stakingInteractor.currentAssetFlow(), - baseViewModel.stakingStateScope, - baseViewModel::showError - ) + when (stakingState) { + is StakingState.Stash.Nominator -> stakingViewStateFactory.createNominatorViewState( + stakingState, + stakingInteractor.currentAssetFlow(), + baseViewModel.stakingStateScope, + baseViewModel::showError + ) - is StakingState.Stash.None -> stakingViewStateFactory.createStashNoneState( - stakingInteractor.currentAssetFlow(), - stakingState, - baseViewModel.stakingStateScope, - baseViewModel::showError - ) + is StakingState.Stash.None -> stakingViewStateFactory.createStashNoneState( + stakingInteractor.currentAssetFlow(), + stakingState, + baseViewModel.stakingStateScope, + baseViewModel::showError + ) - is StakingState.NonStash -> stakingViewStateFactory.createRelayChainWelcomeViewState( - stakingInteractor.currentAssetFlow(), - baseViewModel.stakingStateScope, - welcomeStakingValidationSystem = welcomeStakingValidationSystem, - baseViewModel::showError - ) + is StakingState.NonStash -> stakingViewStateFactory.createRelayChainWelcomeViewState( + stakingInteractor.currentAssetFlow(), + baseViewModel.stakingStateScope, + welcomeStakingValidationSystem = welcomeStakingValidationSystem, + baseViewModel::showError + ) - is StakingState.Stash.Validator -> stakingViewStateFactory.createValidatorViewState( - stakingState, - stakingInteractor.currentAssetFlow(), - baseViewModel.stakingStateScope, - baseViewModel::showError - ) + is StakingState.Stash.Validator -> stakingViewStateFactory.createValidatorViewState( + stakingState, + stakingInteractor.currentAssetFlow(), + baseViewModel.stakingStateScope, + baseViewModel::showError + ) - else -> error("Wrong state") - } + else -> error("Wrong state") } }.shareIn(baseViewModel.stakingStateScope, SharingStarted.Eagerly, 1) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/StakingPoolInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/StakingPoolInteractor.kt index dbd0bf2910..fa6185c8eb 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/StakingPoolInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/StakingPoolInteractor.kt @@ -61,7 +61,7 @@ class StakingPoolInteractor( private val currentValidatorsInteractor: CurrentValidatorsInteractor ) { - @OptIn(FlowPreview::class) + @OptIn(ExperimentalCoroutinesApi::class) fun stakingStateFlow(): Flow { val currentChainFlow = stakingInteractor.selectedChainFlow().filter { it.supportStakingPool } val selectedAccountFlow = accountRepository.selectedMetaAccountFlow() From d66044621a027daa9a843c37e6240539e57ed8fa Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 24 May 2024 17:43:22 +0700 Subject: [PATCH 079/100] revert back shared-features --- gradle/libs.versions.toml | 2 +- .../java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 906a0cd31c..8a4ebce241 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ retrofit = "2.9.0" roomVersion = "2.6.1" rules = "1.5.0" runner = "1.5.2" -sharedFeaturesVersion = "1.1.1.31-FLW" +sharedFeaturesVersion = "1.1.1.30-FLW" shimmerVersion = "0.5.0" sonarqubeGradlePlugin = "3.3" soraUiCore = "0.2.22" diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index 4e73f6687e..ebda156d6e 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -276,7 +276,7 @@ class ChainRegistry @Inject constructor( override fun getConnection(chainId: String) = connectionPool.getConnectionOrThrow(chainId) - override suspend fun awaitConnection(chainId: ChainId) = connectionPool.awaitConnection(chainId) + suspend fun awaitConnection(chainId: ChainId) = connectionPool.awaitConnection(chainId) @Deprecated( "Since we have ethereum chains, which don't have runtime, we must use the function with nullable return value", ReplaceWith("getRuntimeOrNull(chainId)") From 1ef986dec3f8465ed57391df5b867a0791025858 Mon Sep 17 00:00:00 2001 From: Deneath Date: Fri, 24 May 2024 17:44:48 +0700 Subject: [PATCH 080/100] add identity filter --- .../recommendations/settings/RecommendationSettingsProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/recommendations/settings/RecommendationSettingsProvider.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/recommendations/settings/RecommendationSettingsProvider.kt index 54d6b630c2..e2307cbac3 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/recommendations/settings/RecommendationSettingsProvider.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/recommendations/settings/RecommendationSettingsProvider.kt @@ -68,7 +68,7 @@ abstract class RecommendationSettingsProvider { override fun defaultSettings(): RecommendationSettings { return RecommendationSettings( alwaysEnabledFilters = listOf(BlockProducerFilters.ValidatorFilter.HasBlocked), - customEnabledFilters = listOf(BlockProducerFilters.ValidatorFilter.HundredPercentCommissionFilter, BlockProducerFilters.ValidatorFilter.NotOverSubscribedFilter(maximumRewardedNominators), BlockProducerFilters.ValidatorFilter.ElectedFilter), + customEnabledFilters = listOf(BlockProducerFilters.ValidatorFilter.HundredPercentCommissionFilter, BlockProducerFilters.ValidatorFilter.NotOverSubscribedFilter(maximumRewardedNominators), BlockProducerFilters.ValidatorFilter.ElectedFilter, BlockProducerFilters.ValidatorFilter.HasIdentity), sorting = BlockProducersSorting.ValidatorSorting.APYSorting, postProcessors = allPostProcessors, limit = maximumValidatorsPerNominator From 94c5a396974bcf9aa64fe968afce2223120f5a8b Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Fri, 24 May 2024 17:03:06 +0500 Subject: [PATCH 081/100] buildUp --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 490f75cfd2..39dd7a365a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 176 + versionCode = 177 // SDK and tools compileSdkVersion = 34 From 323e2f11ec6ae937bd5fb8b694001dccc34cc33f Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 27 May 2024 13:34:37 +0700 Subject: [PATCH 082/100] FLW-4618 fixed parachain staking states --- .../StakingParachainScenarioInteractor.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/parachain/StakingParachainScenarioInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/parachain/StakingParachainScenarioInteractor.kt index 1dbddcf6aa..8bbd8b8cef 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/parachain/StakingParachainScenarioInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/parachain/StakingParachainScenarioInteractor.kt @@ -93,6 +93,7 @@ import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks import jp.co.soramitsu.wallet.impl.domain.model.planksFromAmount import jp.co.soramitsu.wallet.impl.domain.validation.EnoughToPayFeesValidation import jp.co.soramitsu.wallet.impl.domain.validation.assetBalanceProducer +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.combine @@ -144,8 +145,12 @@ class StakingParachainScenarioInteractor( "91bc6e169807aaa54802737e1c504b2577d4fafedd5a02c10293b1cd60e39527" to 2 // moonbase ) + @OptIn(ExperimentalCoroutinesApi::class) override fun stakingStateFlow(): Flow { - return stakingInteractor.selectedChainFlow().flatMapLatest { chain -> + return combine( + stakingInteractor.selectedChainFlow(), + accountRepository.selectedMetaAccountFlow() + ) { chain, metaAccount -> val availableStakingSelection = stakingSharedState.availableToSelect() val isSelectedChainAvailable = availableStakingSelection.any { it.chainId == chain.id } @@ -155,14 +160,17 @@ class StakingParachainScenarioInteractor( val chainId = with(availableStakingSelection) { firstOrNull { it.chainId == polkadotChainId } ?: first() }.chainId - availableStakingSelection.firstOrNull { it.chainId == chainId }?.let { newSelection -> - stakingSharedState.update(newSelection) - } + availableStakingSelection.firstOrNull { it.chainId == chainId } + ?.let { newSelection -> + stakingSharedState.update(newSelection) + } val availableChain = stakingInteractor.getChain(chainId) availableChain } - val accountId = accountRepository.getSelectedMetaAccount().accountId(useChain) ?: error("cannot find accountId") - stakingParachainScenarioRepository.stakingStateFlow(useChain, accountId) + val accountId = metaAccount.accountId(useChain) ?: error("cannot find accountId") + useChain to accountId + }.flatMapLatest { (chain, accountId) -> + stakingParachainScenarioRepository.stakingStateFlow(chain, accountId) } } From 6c107881455df41a8a773da25fd1c704cfbbecf2 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 27 May 2024 16:53:31 +0700 Subject: [PATCH 083/100] FLW-4611 fixed alerts on staking --- .../impl/domain/alerts/AlertsInteractor.kt | 59 ++++++++++++------- .../StakingRelayChainScenarioRepository.kt | 8 +-- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt index 6e4489700e..60083b2575 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt @@ -21,6 +21,7 @@ import java.math.BigDecimal import java.math.BigInteger import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.staking.api.domain.model.LegacyExposure +import kotlinx.coroutines.ExperimentalCoroutinesApi import jp.co.soramitsu.core.models.Asset as CoreAsset private const val NOMINATIONS_ACTIVE_MEMO = "NOMINATIONS_ACTIVE_MEMO" @@ -40,7 +41,8 @@ class AlertsInteractor( val minimumNominatorBond: BigInteger, val activeEra: BigInteger, val asset: Asset, - val maxNominators: Int? + val maxNominators: Int?, + val isLegacyErasStakersSchema: Boolean ) { val memo = mutableMapOf() @@ -94,30 +96,43 @@ class AlertsInteractor( } } - private suspend fun produceMinStakeAlert(context: AlertContext) = requireState(context.stakingState) { state: StakingState.Stash -> - with(context) { - val minimalStakeInPlanks = (if(exposures != null) minimumStake(exposures.values, minimumNominatorBond) else stakingRepository.minimumActiveStake(stakingState.chain.id)).orZero() - - if ( + private suspend fun produceMinStakeAlert(context: AlertContext) = + requireState(context.stakingState) { state: StakingState.Stash -> + with(context) { + val minimalStakeInPlanks = ( + when { + !context.isLegacyErasStakersSchema -> { + stakingRepository.minimumNominatorBond(context.asset.token.configuration) + } + + exposures != null -> { + minimumStake(exposures.values, minimumNominatorBond) + } + + else -> { + stakingRepository.minimumActiveStake(stakingState.chain.id) + } + } + ).orZero() + if ( // do not show alert for validators - state !is StakingState.Stash.Validator && - asset.bondedInPlanks.orZero() < minimalStakeInPlanks && - // prevent alert for situation where all tokens are being unbounded - asset.bondedInPlanks.orZero() > BigInteger.ZERO - ) { - val minimalStake = asset.token.amountFromPlanks(minimalStakeInPlanks) - - Alert.BondMoreTokens(minimalStake, asset.token) - } else { - null + state !is StakingState.Stash.Validator && + asset.bondedInPlanks.orZero() < minimalStakeInPlanks && + // prevent alert for situation where all tokens are being unbounded + asset.bondedInPlanks.orZero() > BigInteger.ZERO + ) { + val minimalStake = asset.token.amountFromPlanks(minimalStakeInPlanks) + + Alert.BondMoreTokens(minimalStake, asset.token) + } else { + null + } } } - } private fun produceWaitingNextEraAlert(context: AlertContext) = requireState(context.stakingState) { nominatorState: StakingState.Stash.Nominator -> Alert.WaitingForNextEra.takeIf { - val isStakingActive = context.isStakingActive(nominatorState.stashId) - + val isStakingActive = if(context.isLegacyErasStakersSchema) context.isStakingActive(nominatorState.stashId) else true // staking is inactive and there is pending change isStakingActive.not() && nominatorState.nominations.isWaiting(context.activeEra) } @@ -132,6 +147,7 @@ class AlertsInteractor( private val suspendableAlertProducers = listOf(::produceMinStakeAlert) + @OptIn(ExperimentalCoroutinesApi::class) fun getAlertsFlow(stakingState: StakingState): Flow> = sharedState.assetWithChain.flatMapLatest { (chain, chainAsset) -> if (chainAsset.staking != CoreAsset.StakingType.RELAYCHAIN) { return@flatMapLatest flowOf(emptyList()) @@ -148,7 +164,7 @@ class AlertsInteractor( walletRepository.assetFlow(meta.id, stakingState.accountId, chainAsset, chain.minSupportedVersion), stakingRepository.observeActiveEraIndex(chain.id) ) { exposures, asset, activeEra -> - + val isLegacyErasStakersSchema = stakingRepository.isLegacyErasStakersSchema(chain.id) val context = AlertContext( exposures = exposures, stakingState = stakingState, @@ -156,7 +172,8 @@ class AlertsInteractor( minimumNominatorBond = minimumNominatorBond, asset = asset, activeEra = activeEra, - maxNominators = maxNominators + maxNominators = maxNominators, + isLegacyErasStakersSchema = isLegacyErasStakersSchema ) alertProducers.mapNotNull { it.invoke(context) } + suspendableAlertProducers.mapNotNull { it.invoke(context) } } diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt index caae0aecfa..c41b1bac3b 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt @@ -1,6 +1,5 @@ package jp.co.soramitsu.staking.impl.scenarios.relaychain -import android.util.Log import java.math.BigInteger import jp.co.soramitsu.common.data.network.runtime.binding.BinderWithType import jp.co.soramitsu.common.data.network.runtime.binding.NonNullBinderWithType @@ -81,6 +80,7 @@ import kotlin.math.max import kotlin.time.DurationUnit import kotlin.time.toDuration import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.Flow @@ -88,11 +88,9 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext @@ -206,6 +204,7 @@ class StakingRelayChainScenarioRepository( ) } + @OptIn(ExperimentalCoroutinesApi::class) fun legacyElectedExposuresInActiveEra(chainId: ChainId): Flow> = observeActiveEraIndex(chainId) .filter { it.isNotZero() } @@ -461,6 +460,7 @@ class StakingRelayChainScenarioRepository( } } + @OptIn(ExperimentalCoroutinesApi::class) fun stakingStateFlow( chain: Chain, chainAsset: Asset, @@ -612,7 +612,7 @@ class StakingRelayChainScenarioRepository( return getLegacyElectedValidatorsExposure(chainId, era) } - private suspend fun isLegacyErasStakersSchema(chainId: ChainId): Boolean { + suspend fun isLegacyErasStakersSchema(chainId: ChainId): Boolean { val runtime = chainRegistry.getRuntime(chainId) return runtime.metadata.stakingOrNull()?.storageOrNull("ErasStakersPaged") == null } From 732fba87a9113c987d6508e7f3b5a8f2699470c7 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 27 May 2024 18:06:52 +0700 Subject: [PATCH 084/100] fixed identity on kusama --- .../java/jp/co/soramitsu/coredb/migrations/Migrations.kt | 1 + .../jp/co/soramitsu/coredb/model/chain/ChainLocal.kt | 3 ++- .../impl/data/network/blockhain/bindings/Identity.kt | 2 +- .../impl/data/repository/IdentityRepositoryImpl.kt | 9 +++++++-- .../co/soramitsu/runtime/multiNetwork/ChainRegistry.kt | 6 +++++- .../co/soramitsu/runtime/multiNetwork/chain/Mappers.kt | 9 ++++++--- .../soramitsu/runtime/multiNetwork/chain/model/Chain.kt | 3 ++- .../multiNetwork/chain/remote/model/ChainRemote.kt | 3 ++- 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt index db60656743..2a2394acbc 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/migrations/Migrations.kt @@ -8,6 +8,7 @@ val Migration_65_66 = object : Migration(65, 66) { db.execSQL("ALTER TABLE meta_accounts ADD COLUMN `initialized` INTEGER NOT NULL DEFAULT 0") db.execSQL("ALTER TABLE chain_accounts ADD COLUMN `initialized` INTEGER NOT NULL DEFAULT 0") db.execSQL("DELETE FROM assets") + db.execSQL("ALTER TABLE chains ADD COLUMN `identityChain` TEXT NULL DEFAULT NULL") } } diff --git a/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/ChainLocal.kt b/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/ChainLocal.kt index fcb7c13f08..dfaf154c4e 100644 --- a/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/ChainLocal.kt +++ b/core-db/src/main/java/jp/co/soramitsu/coredb/model/chain/ChainLocal.kt @@ -23,7 +23,8 @@ class ChainLocal( val isEthereumChain: Boolean, val isChainlinkProvider: Boolean, val supportNft: Boolean, - val isUsesAppId: Boolean + val isUsesAppId: Boolean, + val identityChain: String? ) { class ExternalApi( diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/bindings/Identity.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/bindings/Identity.kt index 52f007f5bb..611202766e 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/bindings/Identity.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/network/blockhain/bindings/Identity.kt @@ -81,7 +81,7 @@ fun bindSuperOf( @HelperBinding fun bindIdentityData(identityInfo: Struct.Instance, field: String): String? { - val value = identityInfo.get(field) ?: incompatible() + val value = identityInfo.get(field) ?: return null return bindData(value).asString() } diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt index 9d570b23eb..06db76dd43 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/data/repository/IdentityRepositoryImpl.kt @@ -33,8 +33,13 @@ class IdentityRepositoryImpl( chain: Chain, accountIdsHex: List ): AccountIdMap = withContext(Dispatchers.Default) { - val socketService = chainRegistry.awaitConnection(chain.id).socketService - val runtime = chainRegistry.getRuntime(chain.id) + val (socketService, runtime) = if( chain.identityChain != null ) { + chainRegistry.awaitConnection(chain.identityChain!!).socketService to + chainRegistry.getRuntime(chain.identityChain!!) + } else { + chainRegistry.awaitConnection(chain.id).socketService to + chainRegistry.getRuntime(chain.id) + } val identityModule = runtime.metadata.module("Identity") diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt index ebda156d6e..dc4beaa127 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/ChainRegistry.kt @@ -94,7 +94,11 @@ class ChainRegistry @Inject constructor( val chainsWithStaking = chains.filter { it.assets.any { asset -> asset.staking == Asset.StakingType.PARACHAIN || asset.staking == Asset.StakingType.RELAYCHAIN || asset.supportStakingPool } } - (popularChains + enabledChains + chainsWithCrowdloans + chainsWithStaking).toSet() + val identityHolders = + chains.filter { chain -> chain.identityChain != null }.map { it.identityChain } + .mapNotNull { identityChain -> chains.find { it.id == identityChain } } + + (popularChains + enabledChains + chainsWithCrowdloans + chainsWithStaking + identityHolders).toSet() .filter { /*it.disabled*/ it.nodes.isNotEmpty() } } .diffed() diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt index 7e4f44f358..e932926c3d 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt @@ -155,7 +155,8 @@ fun ChainRemote.toChain(): Chain { chainlinkProvider = CHAINLINK_PROVIDER_OPTION in optionsOrEmpty, supportNft = NFT_OPTION in optionsOrEmpty, paraId = this.paraId, - isUsesAppId = USES_APP_ID_OPTION in optionsOrEmpty + isUsesAppId = USES_APP_ID_OPTION in optionsOrEmpty, + identityChain = identityChain ) } @@ -264,7 +265,8 @@ fun mapChainLocalToChain(chainLocal: JoinedChainInfo): Chain { paraId = paraId, chainlinkProvider = isChainlinkProvider, supportNft = supportNft, - isUsesAppId = isUsesAppId + isUsesAppId = isUsesAppId, + identityChain = identityChain ) } } @@ -337,7 +339,8 @@ fun mapChainToChainLocal(chain: Chain): JoinedChainInfo { paraId = paraId, isChainlinkProvider = chainlinkProvider, supportNft = supportNft, - isUsesAppId = isUsesAppId + isUsesAppId = isUsesAppId, + identityChain = identityChain ) } diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt index 63e5b4913c..15d11dc2f3 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt @@ -52,7 +52,8 @@ data class Chain( val isEthereumChain: Boolean, val chainlinkProvider: Boolean, val supportNft: Boolean, - val isUsesAppId: Boolean + val isUsesAppId: Boolean, + val identityChain: String? ) : IChain { val assetsById = assets.associateBy(CoreAsset::id) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/remote/model/ChainRemote.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/remote/model/ChainRemote.kt index 46581cc64d..5aced5b2a1 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/remote/model/ChainRemote.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/remote/model/ChainRemote.kt @@ -13,5 +13,6 @@ data class ChainRemote( val addressPrefix: Int, val options: List?, val parentId: String?, - val disabled: Boolean = false + val disabled: Boolean = false, + val identityChain: String? = null ) From a6c9d7935ae2a399a787a904ae42be10bd9274d1 Mon Sep 17 00:00:00 2001 From: Deneath Date: Mon, 27 May 2024 18:39:25 +0700 Subject: [PATCH 085/100] fixed new chain missing asset --- .../multiNetwork/chain/ChainSyncService.kt | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt index d52aa1a90e..90a7036b63 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/ChainSyncService.kt @@ -8,8 +8,6 @@ import jp.co.soramitsu.coredb.model.chain.JoinedChainInfo import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.runtime.multiNetwork.chain.remote.ChainFetcher import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class ChainSyncService( @@ -46,40 +44,37 @@ class ChainSyncService( } }.map(::mapChainToChainLocal) - coroutineScope { - launch { - val removed = localChainsJoinedInfo.filter { it.chain.id !in remoteMapping } - .map(JoinedChainInfo::chain) - dao.update(removed, newOrUpdated) - } - launch { - val metaAccounts = metaAccountDao.getMetaAccounts() - if(metaAccounts.isEmpty()) return@launch - val newAssets = - newOrUpdated.filter { it.chain.id !in localMapping.keys }.map { it.assets } - .flatten() + val removed = localChainsJoinedInfo.filter { it.chain.id !in remoteMapping } + .map(JoinedChainInfo::chain) + dao.update(removed, newOrUpdated) + val metaAccounts = metaAccountDao.getMetaAccounts() + + if (metaAccounts.isEmpty()) return@runCatching Unit + val newAssets = + newOrUpdated.filter { it.chain.id !in localMapping.keys }.map { it.assets } + .flatten() - val newLocalAssets = metaAccounts.map { metaAccount -> - newAssets.mapNotNull { - val chain = remoteMapping[it.chainId] - val accountId = if(chain?.isEthereumBased == true) { - metaAccount.ethereumAddress - } else { - metaAccount.substrateAccountId - } ?: return@mapNotNull null - AssetLocal( - accountId = accountId, - id = it.id, - chainId = it.chainId, - metaId = metaAccount.id, - tokenPriceId = it.priceId, - enabled = remoteMapping[it.chainId]?.rank != null && it.isUtility == true - ) - } - }.flatten() - assetsDao.insertAssets(newLocalAssets) + val newLocalAssets = metaAccounts.map { metaAccount -> + newAssets.mapNotNull { + val chain = remoteMapping[it.chainId] + val accountId = if (chain?.isEthereumBased == true) { + metaAccount.ethereumAddress + } else { + metaAccount.substrateAccountId + } ?: return@mapNotNull null + AssetLocal( + accountId = accountId, + id = it.id, + chainId = it.chainId, + metaId = metaAccount.id, + tokenPriceId = it.priceId, + enabled = false + ) } - }.join() + }.flatten() + runCatching { assetsDao.insertAssets(newLocalAssets) }.onFailure { + it.printStackTrace() + } } } } From f754b4b9612e6ccad3526853b7220b570dd503a4 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Mon, 27 May 2024 18:29:08 +0500 Subject: [PATCH 086/100] clean --- feature-wallet-impl/build.gradle | 5 --- .../historySource/HistorySourceProvider.kt | 9 +---- .../data/historySource/SoraHistorySource.kt | 5 +-- .../wallet/impl/di/WalletFeatureModule.kt | 40 +------------------ 4 files changed, 5 insertions(+), 54 deletions(-) diff --git a/feature-wallet-impl/build.gradle b/feature-wallet-impl/build.gradle index ca2cd08318..f7c00c4f73 100644 --- a/feature-wallet-impl/build.gradle +++ b/feature-wallet-impl/build.gradle @@ -22,11 +22,6 @@ android { buildConfigField "String", "MOONPAY_HOST", "\"buy-staging.moonpay.com\"" buildConfigField "String", "MOONPAY_PUBLIC_KEY", "\"pk_test_DMRuyL6Nf1qc9OzjPBmCFBeCGkFwiZs0\"" - buildConfigField "String", "SORA_CONFIG_COMMON_STAGE", "\"https://config.polkaswap2.io/stage/common.json\"" - buildConfigField "String", "SORA_CONFIG_MOBILE_STAGE", "\"https://config.polkaswap2.io/stage/mobile.json\"" - buildConfigField "String", "SORA_CONFIG_COMMON_PROD", "\"https://config.polkaswap2.io/prod/common.json\"" - buildConfigField "String", "SORA_CONFIG_MOBILE_PROD", "\"https://config.polkaswap2.io/prod/mobile.json\"" - buildConfigField "String", "SCAM_DETECTION_CONFIG", "\"https://raw.githubusercontent.com/soramitsu/shared-features-utils/master/scamDetection/Polkadot_Hot_Wallet_Attributions.csv\"" } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/HistorySourceProvider.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/HistorySourceProvider.kt index c560d874a7..95abfdc8f0 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/HistorySourceProvider.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/HistorySourceProvider.kt @@ -5,22 +5,17 @@ import jp.co.soramitsu.runtime.multiNetwork.chain.model.Chain import jp.co.soramitsu.wallet.impl.data.network.subquery.OperationsHistoryApi import jp.co.soramitsu.xnetworking.basic.networkclient.SoramitsuNetworkClient import jp.co.soramitsu.xnetworking.fearlesswallet.txhistory.client.TxHistoryClientForFearlessWalletFactory -import jp.co.soramitsu.xnetworking.sorawallet.mainconfig.SoraRemoteConfigBuilder class HistorySourceProvider( private val walletOperationsApi: OperationsHistoryApi, private val chainRegistry: ChainRegistry, private val soramitsuNetworkClient: SoramitsuNetworkClient, - private val soraTxHistoryFactory: TxHistoryClientForFearlessWalletFactory, - private val soraProdRemoteConfigBuilder: SoraRemoteConfigBuilder, - private val soraStageRemoteConfigBuilder: SoraRemoteConfigBuilder + private val soraTxHistoryFactory: TxHistoryClientForFearlessWalletFactory ) { operator fun invoke(historyUrl: String, historyType: Chain.ExternalApi.Section.Type): HistorySource? { return when (historyType) { Chain.ExternalApi.Section.Type.SUBQUERY -> SubqueryHistorySource(walletOperationsApi, chainRegistry, historyUrl) - Chain.ExternalApi.Section.Type.SORA -> { - SoraHistorySource(soramitsuNetworkClient, soraTxHistoryFactory, soraProdRemoteConfigBuilder, soraStageRemoteConfigBuilder) - } + Chain.ExternalApi.Section.Type.SORA -> SoraHistorySource(soramitsuNetworkClient, soraTxHistoryFactory) Chain.ExternalApi.Section.Type.SUBSQUID -> SubsquidHistorySource(walletOperationsApi, historyUrl) Chain.ExternalApi.Section.Type.GIANTSQUID -> GiantsquidHistorySource(walletOperationsApi, historyUrl) Chain.ExternalApi.Section.Type.ETHERSCAN -> EtherscanHistorySource(walletOperationsApi, historyUrl) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt index 2987cfa2c3..f836158f94 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt @@ -10,13 +10,10 @@ import jp.co.soramitsu.wallet.impl.domain.model.Operation import jp.co.soramitsu.xnetworking.basic.networkclient.SoramitsuNetworkClient import jp.co.soramitsu.xnetworking.basic.txhistory.TxHistoryItem import jp.co.soramitsu.xnetworking.fearlesswallet.txhistory.client.TxHistoryClientForFearlessWalletFactory -import jp.co.soramitsu.xnetworking.sorawallet.mainconfig.SoraRemoteConfigBuilder class SoraHistorySource( soramitsuNetworkClient: SoramitsuNetworkClient, - soraTxHistoryFactory: TxHistoryClientForFearlessWalletFactory, - private val soraProdRemoteConfigBuilder: SoraRemoteConfigBuilder, - private val soraStageRemoteConfigBuilder: SoraRemoteConfigBuilder + soraTxHistoryFactory: TxHistoryClientForFearlessWalletFactory ) : HistorySource { private val client = soraTxHistoryFactory.createSubSquid(soramitsuNetworkClient, 100) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt index fb479e9f0a..cd1077424d 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/di/WalletFeatureModule.kt @@ -93,8 +93,6 @@ import jp.co.soramitsu.xcm.XcmService import jp.co.soramitsu.xcm.domain.XcmEntitiesFetcher import jp.co.soramitsu.xnetworking.basic.networkclient.SoramitsuNetworkClient import jp.co.soramitsu.xnetworking.fearlesswallet.txhistory.client.TxHistoryClientForFearlessWalletFactory -import jp.co.soramitsu.xnetworking.sorawallet.mainconfig.SoraRemoteConfigBuilder -import jp.co.soramitsu.xnetworking.sorawallet.mainconfig.SoraRemoteConfigProvider @InstallIn(SingletonComponent::class) @Module @@ -235,16 +233,12 @@ class WalletFeatureModule { walletOperationsHistoryApi: OperationsHistoryApi, chainRegistry: ChainRegistry, soramitsuNetworkClient: SoramitsuNetworkClient, - txHistoryClientForFearlessWalletFactory: TxHistoryClientForFearlessWalletFactory, - @Named("prod") soraProdRemoteConfigBuilder: SoraRemoteConfigBuilder, - @Named("stage") soraStageRemoteConfigBuilder: SoraRemoteConfigBuilder + txHistoryClientForFearlessWalletFactory: TxHistoryClientForFearlessWalletFactory ) = HistorySourceProvider( walletOperationsHistoryApi, chainRegistry, soramitsuNetworkClient, - txHistoryClientForFearlessWalletFactory, - soraProdRemoteConfigBuilder, - soraStageRemoteConfigBuilder + txHistoryClientForFearlessWalletFactory ) @Provides @@ -465,36 +459,6 @@ class WalletFeatureModule { @ApplicationContext context: Context ): TxHistoryClientForFearlessWalletFactory = TxHistoryClientForFearlessWalletFactory(context) - @Singleton - @Provides - @Named("prod") - fun provideProdSoraRemoteConfigBuilder( - client: SoramitsuNetworkClient, - @ApplicationContext context: Context - ): SoraRemoteConfigBuilder { - return SoraRemoteConfigProvider( - context = context, - client = client, - commonUrl = BuildConfig.SORA_CONFIG_COMMON_PROD, - mobileUrl = BuildConfig.SORA_CONFIG_MOBILE_PROD - ).provide() - } - - @Singleton - @Provides - @Named("stage") - fun provideStageSoraRemoteConfigBuilder( - client: SoramitsuNetworkClient, - @ApplicationContext context: Context - ): SoraRemoteConfigBuilder { - return SoraRemoteConfigProvider( - context = context, - client = client, - commonUrl = BuildConfig.SORA_CONFIG_COMMON_STAGE, - mobileUrl = BuildConfig.SORA_CONFIG_MOBILE_STAGE - ).provide() - } - @Provides fun provideAccountListingMixin( interactor: AccountInteractor, From 7e0b896735882c9cba5bdeecc5fa5266c42e9205 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Mon, 27 May 2024 18:48:21 +0500 Subject: [PATCH 087/100] buildUp --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 39dd7a365a..3d1a2e96dc 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 177 + versionCode = 178 // SDK and tools compileSdkVersion = 34 From 7b292644dd56874f551cc061ee91ad30312e7067 Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 28 May 2024 13:11:16 +0700 Subject: [PATCH 088/100] fixed alerts on staking --- .../staking/impl/domain/alerts/AlertsInteractor.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt index 60083b2575..702587cdb2 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt @@ -132,9 +132,12 @@ class AlertsInteractor( private fun produceWaitingNextEraAlert(context: AlertContext) = requireState(context.stakingState) { nominatorState: StakingState.Stash.Nominator -> Alert.WaitingForNextEra.takeIf { - val isStakingActive = if(context.isLegacyErasStakersSchema) context.isStakingActive(nominatorState.stashId) else true - // staking is inactive and there is pending change - isStakingActive.not() && nominatorState.nominations.isWaiting(context.activeEra) + return@takeIf if(context.isLegacyErasStakersSchema) { + // staking is inactive and there is pending change + context.isStakingActive(nominatorState.stashId).not() && nominatorState.nominations.isWaiting(context.activeEra) + } else { + nominatorState.nominations.isWaiting(context.activeEra) + } } } From a20bc4af77103f1e7e653e22fb68ae22332c1052 Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 28 May 2024 17:08:31 +0700 Subject: [PATCH 089/100] fixed alerts on staking one more time --- .../impl/domain/StakingInteractorExt.kt | 27 +++------- .../staking/impl/domain/alerts/Alert.kt | 8 +-- .../impl/domain/alerts/AlertsInteractor.kt | 53 ++++++++++--------- .../StakingRelayChainScenarioInteractor.kt | 42 +++++++-------- .../StakingRelayChainScenarioRepository.kt | 2 +- 5 files changed, 60 insertions(+), 72 deletions(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractorExt.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractorExt.kt index bb1d88b2c5..b0de13f6dd 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractorExt.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/StakingInteractorExt.kt @@ -1,6 +1,5 @@ package jp.co.soramitsu.staking.impl.domain -import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.shared_utils.runtime.AccountId import jp.co.soramitsu.staking.api.domain.model.IndividualExposure import kotlinx.coroutines.flow.first @@ -29,7 +28,11 @@ fun LegacyExposure.willAccountBeRewarded( accountId: AccountId, rewardedNominatorsPerValidator: Int? ): Boolean { - if(rewardedNominatorsPerValidator == null) return true + if(rewardedNominatorsPerValidator == null) { + return others.any { + it.who.contentEquals(accountId) + } + } val indexInRewardedList = others.sortedByDescending(IndividualExposure::value).indexOfFirst { it.who.contentEquals(accountId) } @@ -43,22 +46,4 @@ fun LegacyExposure.willAccountBeRewarded( return numberInRewardedList <= rewardedNominatorsPerValidator } -fun minimumStake( - exposures: Collection, - minimumNominatorBond: BigInteger -): BigInteger { - val stakeByNominator = exposures - .map(LegacyExposure::others) - .flatten() - .fold(mutableMapOf()) { acc, individualExposure -> - val currentExposure = acc.getOrDefault(individualExposure.who.toHexString(), BigInteger.ZERO) - - acc[individualExposure.who.toHexString()] = currentExposure + individualExposure.value - - acc - } - - return stakeByNominator.values.minOrZero().coerceAtLeast(minimumNominatorBond) -} - -private fun Iterable.minOrZero(): BigInteger = this.minOrNull() ?: BigInteger.ZERO +fun Iterable.minOrZero(): BigInteger = this.minOrNull() ?: BigInteger.ZERO diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/Alert.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/Alert.kt index 7fe0a2a0d0..7b3f6b9ff4 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/Alert.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/Alert.kt @@ -11,12 +11,12 @@ sealed class Alert { class BondMoreTokens(val minimalStake: BigDecimal, val token: Token) : Alert() - object ChangeValidators : Alert() - object AllValidatorsAreOversubscribed : Alert() + data object ChangeValidators : Alert() + data object AllValidatorsAreOversubscribed : Alert() - object WaitingForNextEra : Alert() + data object WaitingForNextEra : Alert() - object SetValidators : Alert() + data object SetValidators : Alert() class ChangeCollators(val collatorIdHex: String, val amountToStakeMore: String) : Alert() class CollatorLeaving(val delegation: CollatorDelegation, val collatorName: String) : Alert() diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt index 702587cdb2..44b0bb8a23 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/alerts/AlertsInteractor.kt @@ -8,7 +8,6 @@ import jp.co.soramitsu.staking.api.domain.model.StakingState import jp.co.soramitsu.staking.impl.data.repository.StakingConstantsRepository import jp.co.soramitsu.staking.impl.domain.common.isWaiting import jp.co.soramitsu.staking.impl.domain.isNominationActive -import jp.co.soramitsu.staking.impl.domain.minimumStake import jp.co.soramitsu.staking.impl.scenarios.relaychain.StakingRelayChainScenarioRepository import jp.co.soramitsu.wallet.impl.domain.interfaces.WalletRepository import jp.co.soramitsu.wallet.impl.domain.model.Asset @@ -19,6 +18,7 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import java.math.BigDecimal import java.math.BigInteger +import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.staking.api.domain.model.LegacyExposure import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -35,7 +35,7 @@ class AlertsInteractor( ) { class AlertContext( - val exposures: Map?, + val exposures: Map, val stakingState: StakingState, val maxRewardedNominatorsPerValidator: Int?, val minimumNominatorBond: BigInteger, @@ -56,9 +56,6 @@ class AlertsInteractor( } private fun AlertContext.isStakingActive(stashId: AccountId) = useMemo(NOMINATIONS_ACTIVE_MEMO) { - if(exposures == null) { - return true - } isNominationActive(stashId, exposures.values, maxRewardedNominatorsPerValidator) } @@ -68,9 +65,18 @@ class AlertsInteractor( } } - private fun produceChangeValidatorsAlert(context: AlertContext): Alert? { + private suspend fun produceChangeValidatorsAlert(context: AlertContext): Alert? { return requireState(context.stakingState) { nominatorState: StakingState.Stash.Nominator -> - val allValidatorsAreOversubscribed = if (context.exposures == null || context.maxNominators == null) { + if (context.isLegacyErasStakersSchema.not()) { + val myValidators = stakingRepository.getRemoteAccountNominations( + context.stakingState.chain.id, + nominatorState.accountId + )?.targets.orEmpty() + + return@requireState if (context.exposures.any { it.key.fromHex() in myValidators }) Alert.ChangeValidators else null + } + + val allValidatorsAreOversubscribed = if (context.maxNominators == null) { false } else { nominatorState.nominations.targets.mapNotNull { context.exposures[it.toHexString()] } @@ -99,21 +105,12 @@ class AlertsInteractor( private suspend fun produceMinStakeAlert(context: AlertContext) = requireState(context.stakingState) { state: StakingState.Stash -> with(context) { - val minimalStakeInPlanks = ( - when { - !context.isLegacyErasStakersSchema -> { - stakingRepository.minimumNominatorBond(context.asset.token.configuration) - } - - exposures != null -> { - minimumStake(exposures.values, minimumNominatorBond) - } - - else -> { - stakingRepository.minimumActiveStake(stakingState.chain.id) - } - } - ).orZero() + val minActiveStake = stakingRepository.minimumActiveStake(stakingState.chain.id) + ?: context.exposures.values.minOf { exposure -> exposure.others.minOf { it.value } } + + val minimalStakeInPlanks = + minActiveStake.coerceAtLeast(stakingRepository.minimumNominatorBond(context.asset.token.configuration)) + if ( // do not show alert for validators state !is StakingState.Stash.Validator && @@ -136,19 +133,19 @@ class AlertsInteractor( // staking is inactive and there is pending change context.isStakingActive(nominatorState.stashId).not() && nominatorState.nominations.isWaiting(context.activeEra) } else { + nominatorState.nominations.isWaiting(context.activeEra) } } } private val alertProducers = listOf( - ::produceChangeValidatorsAlert, ::produceRedeemableAlert, ::produceWaitingNextEraAlert, ::produceSetValidatorsAlert ) - private val suspendableAlertProducers = listOf(::produceMinStakeAlert) + private val suspendableAlertProducers = listOf(::produceChangeValidatorsAlert,::produceMinStakeAlert) @OptIn(ExperimentalCoroutinesApi::class) fun getAlertsFlow(stakingState: StakingState): Flow> = sharedState.assetWithChain.flatMapLatest { (chain, chainAsset) -> @@ -178,7 +175,13 @@ class AlertsInteractor( maxNominators = maxNominators, isLegacyErasStakersSchema = isLegacyErasStakersSchema ) - alertProducers.mapNotNull { it.invoke(context) } + suspendableAlertProducers.mapNotNull { it.invoke(context) } + val alerts = (alertProducers.mapNotNull { it.invoke(context) } + suspendableAlertProducers.mapNotNull { it.invoke(context) }).toMutableList() + + if(alerts.contains(Alert.WaitingForNextEra) && alerts.any { it is Alert.BondMoreTokens }) { + alerts.remove(Alert.WaitingForNextEra) + } + + alerts } alertsFlow diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt index 8a78574d31..1955070b48 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioInteractor.kt @@ -1,6 +1,5 @@ package jp.co.soramitsu.staking.impl.scenarios.relaychain -import android.util.Log import java.math.BigDecimal import java.math.BigInteger import java.util.Optional @@ -47,7 +46,6 @@ import jp.co.soramitsu.staking.impl.domain.EraTimeCalculatorFactory import jp.co.soramitsu.staking.impl.domain.StakingInteractor import jp.co.soramitsu.staking.impl.domain.common.isWaiting import jp.co.soramitsu.staking.impl.domain.isNominationActive -import jp.co.soramitsu.staking.impl.domain.minimumStake import jp.co.soramitsu.staking.impl.domain.model.NetworkInfo import jp.co.soramitsu.staking.impl.domain.model.NominatorStatus import jp.co.soramitsu.staking.impl.domain.model.PendingPayout @@ -98,7 +96,6 @@ import jp.co.soramitsu.wallet.impl.domain.validation.EnoughToPayFeesValidation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emitAll @@ -112,7 +109,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext import jp.co.soramitsu.core.models.Asset as CoreAsset @@ -202,6 +198,7 @@ class StakingRelayChainScenarioInteractor( } } + @OptIn(ExperimentalCoroutinesApi::class) fun stakingStateFlow(chainId: ChainId): Flow { return jp.co.soramitsu.common.utils.flowOf { val chain = stakingInteractor.getChain(chainId) @@ -237,7 +234,7 @@ class StakingRelayChainScenarioInteractor( return observeStakeSummary(nominatorState) { val eraStakers = it.eraStakers.values val chainId = nominatorState.chain.id - + val utilityAsset = nominatorState.chain.utilityAsset when { isNominationActive( nominatorState.stashId, @@ -245,30 +242,34 @@ class StakingRelayChainScenarioInteractor( it.rewardedNominatorsPerValidator ) -> NominatorStatus.Active + utilityAsset != null && it.asset.bondedInPlanks.orZero() < minimumStake( + nominatorState.chain.id, + eraStakers, + stakingRelayChainScenarioRepository.minimumNominatorBond(utilityAsset) + ) -> { + NominatorStatus.Inactive(NominatorStatus.Inactive.Reason.MIN_STAKE) + } + nominatorState.nominations.isWaiting(it.activeEraIndex) -> NominatorStatus.Waiting( timeLeft = getCalculator(chainId).calculate(nominatorState.nominations.submittedInEra + ERA_OFFSET) .toLong() ) - else -> { - val utilityAsset = nominatorState.chain.utilityAsset - val inactiveReason = when { - utilityAsset != null && it.asset.bondedInPlanks.orZero() < minimumStake( - eraStakers, - stakingRelayChainScenarioRepository.minimumNominatorBond(utilityAsset) - ) -> { - NominatorStatus.Inactive.Reason.MIN_STAKE - } - - else -> NominatorStatus.Inactive.Reason.NO_ACTIVE_VALIDATOR - } - - NominatorStatus.Inactive(inactiveReason) - } + else -> NominatorStatus.Inactive(NominatorStatus.Inactive.Reason.NO_ACTIVE_VALIDATOR) } } } + suspend fun minimumStake( + chainId: ChainId, + exposures: Collection, + minimumNominatorBond: BigInteger + ): BigInteger { + val minActiveStake = stakingRelayChainScenarioRepository.minimumActiveStake(chainId) + ?: kotlin.runCatching { exposures.minOf { exposure -> exposure.others.minOf { it.value } } }.getOrNull() ?: BigInteger.ZERO + return minActiveStake.coerceAtLeast(minimumNominatorBond) + } + private fun observeStakeSummary( state: StakingState.Stash, statusResolver: suspend (StatusResolutionContext) -> S @@ -291,7 +292,6 @@ class StakingRelayChainScenarioInteractor( asset, rewardedNominatorsPerValidator ) - val status = statusResolver(statusResolutionContext) StakeSummary( status = status, diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt index c41b1bac3b..69045bcbe2 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/scenarios/relaychain/StakingRelayChainScenarioRepository.kt @@ -444,7 +444,7 @@ class StakingRelayChainScenarioRepository( val runtime = runtimeFor(chainId) return runtime.metadata.staking().storageOrNull(storageName)?.let { storageEntry -> - localStorage.query( + remoteStorage.query( keyBuilder = { storageEntry.storageKey() }, binding = { scale, _ -> scale?.let { From c3026696b372fbb6dfca0542d6b180c4817b122a Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 28 May 2024 18:08:42 +0700 Subject: [PATCH 090/100] fixed sora transactions history --- .../data/historySource/SoraHistorySource.kt | 19 ++++++++++--------- .../impl/data/mappers/OperationMappers.kt | 15 ++++++++++----- .../impl/data/repository/HistoryRepository.kt | 1 + 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt index f836158f94..995de35b4f 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/historySource/SoraHistorySource.kt @@ -41,16 +41,17 @@ class SoraHistorySource( }.getOrNull() val soraHistoryItems: List = soraHistory?.items.orEmpty() - val soraOperations = runCatching { - soraHistoryItems.mapNotNull { - it.toOperation( - chain, - chainAsset, - accountAddress, - filters - ) + val soraOperations = + soraHistoryItems.mapNotNull { item -> + runCatching { + item.toOperation( + chain, + chainAsset, + accountAddress, + filters + ) + }.getOrNull() } - }.getOrNull() ?: emptyList() val nextCursor = if (soraHistory?.endReached == true) null else page.inc().toString() return CursorPage(nextCursor, soraOperations) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/mappers/OperationMappers.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/mappers/OperationMappers.kt index df0c45ef97..0fbefe5770 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/mappers/OperationMappers.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/mappers/OperationMappers.kt @@ -1,5 +1,6 @@ package jp.co.soramitsu.wallet.impl.data.mappers +import java.math.BigDecimal import java.math.BigInteger import jp.co.soramitsu.account.api.presentation.account.AddressDisplayUseCase import jp.co.soramitsu.common.address.AddressIconGenerator @@ -212,7 +213,7 @@ fun TxHistoryItem.toOperation( hash = id, myAddress = data?.firstOrNull { it.paramName == "from" }?.paramValue.orEmpty(), amount = chainAsset.planksFromAmount( - data?.firstOrNull { it.paramName == "amount" }?.paramValue?.toBigDecimal() + data?.firstOrNull { it.paramName == "amount" }?.paramValue?.toBigDecimalOrNull() .orZero() ), receiver = data?.firstOrNull { it.paramName == "to" }?.paramValue.orEmpty(), @@ -231,16 +232,16 @@ fun TxHistoryItem.toOperation( val baseAsset = chain.assets.firstOrNull { it.currencyId == baseCurrencyId } ?: return null val baseAssetAmount = - data?.firstOrNull { it.paramName == "baseAssetAmount" }?.paramValue?.toBigDecimal() + data?.firstOrNull { it.paramName == "baseAssetAmount" }?.paramValue?.toBigDecimalOrNull() .orZero() val targetAsset = chain.assets.firstOrNull { it.currencyId == targetCurrencyId } val targetAssetAmount = - data?.firstOrNull { it.paramName == "targetAssetAmount" }?.paramValue?.toBigDecimal() + data?.firstOrNull { it.paramName == "targetAssetAmount" }?.paramValue?.toBigDecimalOrNull() .orZero() val liquidityProviderFee = - data?.firstOrNull { it.paramName == "liquidityProviderFee" }?.paramValue?.toBigDecimal() + data?.firstOrNull { it.paramName == "liquidityProviderFee" }?.paramValue?.toBigDecimalOrNull() .orZero() Operation( @@ -271,7 +272,7 @@ fun TxHistoryItem.toOperation( chainAsset = chainAsset, type = Operation.Type.Reward( amount = chainAsset.planksFromAmount( - data?.firstOrNull { it.paramName == "amount" }?.paramValue?.toBigDecimal() + data?.firstOrNull { it.paramName == "amount" }?.paramValue?.toBigDecimalOrNull() .orZero() ), isReward = true, @@ -580,3 +581,7 @@ fun mapOperationToParcel( } } } + +fun String.toBigDecimalOrNull(): BigDecimal? { + return runCatching { toBigDecimal() }.getOrNull() +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/HistoryRepository.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/HistoryRepository.kt index 8f31f675e2..3a5f7c87b4 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/HistoryRepository.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/wallet/impl/data/repository/HistoryRepository.kt @@ -1,5 +1,6 @@ package jp.co.soramitsu.wallet.impl.data.repository +import android.util.Log import jp.co.soramitsu.common.data.model.CursorPage import jp.co.soramitsu.common.utils.mapList import jp.co.soramitsu.core.models.Asset From 1dd9c67b7a948b0a4a2edd7772b7c84d0d5ae1fd Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 28 May 2024 16:32:25 +0500 Subject: [PATCH 091/100] walletConnect deps update --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8a4ebce241..8e832ea416 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,7 +54,7 @@ shimmerVersion = "0.5.0" sonarqubeGradlePlugin = "3.3" soraUiCore = "0.2.22" storiesVersion = "3.0.1" -walletconnectBom = "1.18.0" +walletconnectBom = "1.31.4" web3j = "4.8.8-android" wsVersion = "2.14" xNetworking = "0.2.5-temp7" From 999dd408731977e2e871790e4131c901f1bcce73 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 28 May 2024 16:33:20 +0500 Subject: [PATCH 092/100] buildUp --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3d1a2e96dc..4ef75fcccf 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 178 + versionCode = 179 // SDK and tools compileSdkVersion = 34 From fa59d86d169fe615d9d27f70cd1b3f4f5009992e Mon Sep 17 00:00:00 2001 From: Deneath Date: Tue, 28 May 2024 21:37:10 +0700 Subject: [PATCH 093/100] fixed possible crash on staking --- .../domain/validators/current/CurrentValidatorsInteractor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validators/current/CurrentValidatorsInteractor.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validators/current/CurrentValidatorsInteractor.kt index 16e6c3475b..d8db130aae 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validators/current/CurrentValidatorsInteractor.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validators/current/CurrentValidatorsInteractor.kt @@ -64,7 +64,7 @@ class CurrentValidatorsInteractor( val userIndividualExposure = activeNominations[validator.accountIdHex] val status = when { - userIndividualExposure != null -> { + userIndividualExposure != null && validator.electedInfo != null -> { // safe to !! here since non null nomination means that validator is elected val userNominationIndex = validator.electedInfo!!.nominatorStakes .sortedByDescending(IndividualExposure::value) From c929dc5be96c2a05fe1748411aa2d1a08db7a925 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 28 May 2024 19:24:59 +0500 Subject: [PATCH 094/100] identityChain update fix --- .../jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt index 15d11dc2f3..990cb0b25d 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt @@ -107,6 +107,7 @@ data class Chain( if (chainlinkProvider != other.chainlinkProvider) return false if (supportNft != other.supportNft) return false if (isUsesAppId != other.isUsesAppId) return false + if (identityChain != other.identityChain) return false // custom comparison logic val defaultNodes = nodes.filter { it.isDefault } @@ -140,6 +141,7 @@ data class Chain( result = 31 * result + chainlinkProvider.hashCode() result = 31 * result + supportNft.hashCode() result = 31 * result + isUsesAppId.hashCode() + result = 31 * result + (identityChain?.hashCode() ?: 0) return result } } From 3a8b3f54185dde92c159359f6458da906b547162 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 28 May 2024 19:59:23 +0500 Subject: [PATCH 095/100] OKLINK quick fix to show "okx explorer" title --- .../jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt | 1 + .../co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt index e932926c3d..537f0351ca 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/Mappers.kt @@ -42,6 +42,7 @@ private fun mapExplorerTypeRemoteToExplorerType(explorer: String) = when (explor "subscan" -> Chain.Explorer.Type.SUBSCAN "etherscan" -> Chain.Explorer.Type.ETHERSCAN "oklink" -> Chain.Explorer.Type.OKLINK + "okx explorer" -> Chain.Explorer.Type.OKLINK "zeta" -> Chain.Explorer.Type.ZETA "reef" -> Chain.Explorer.Type.REEF else -> Chain.Explorer.Type.UNKNOWN diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt index 990cb0b25d..8afe1e8365 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt @@ -78,7 +78,11 @@ data class Chain( enum class Type { POLKASCAN, SUBSCAN, ETHERSCAN, OKLINK, ZETA, REEF, UNKNOWN; - val capitalizedName: String = name.lowercase().replaceFirstChar { it.titlecase() } + val capitalizedName: String + get() = when (this) { + OKLINK -> "okx explorer" + else -> name + }.lowercase().replaceFirstChar { it.titlecase() } } } From 1f82357a3c4469e561d42533779323c4f354a5df Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 28 May 2024 19:59:47 +0500 Subject: [PATCH 096/100] buildUp --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4ef75fcccf..58dd11a811 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 179 + versionCode = 180 // SDK and tools compileSdkVersion = 34 From 409a3e460d291a2762f678459ef29cfd9a165514 Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Tue, 28 May 2024 21:24:19 +0500 Subject: [PATCH 097/100] OKX title fix + buildUp --- build.gradle | 2 +- .../soramitsu/runtime/multiNetwork/chain/model/Chain.kt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 58dd11a811..0a730dcd85 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 180 + versionCode = 181 // SDK and tools compileSdkVersion = 34 diff --git a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt index 8afe1e8365..3a4d021b03 100644 --- a/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt +++ b/runtime/src/main/java/jp/co/soramitsu/runtime/multiNetwork/chain/model/Chain.kt @@ -79,10 +79,11 @@ data class Chain( POLKASCAN, SUBSCAN, ETHERSCAN, OKLINK, ZETA, REEF, UNKNOWN; val capitalizedName: String - get() = when (this) { - OKLINK -> "okx explorer" - else -> name - }.lowercase().replaceFirstChar { it.titlecase() } + get() = if (this == OKLINK) { + "OKX explorer" + } else { + name.lowercase().replaceFirstChar { it.titlecase() } + } } } From 333dc6bbc23da3169b0f226c0f6bd54fb5d463cd Mon Sep 17 00:00:00 2001 From: Sergey Pankratov Date: Wed, 29 May 2024 15:17:01 +0500 Subject: [PATCH 098/100] update R8 rules --- app/proguard-rules.pro | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 095be24e50..2b5dfd5fee 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -158,6 +158,7 @@ -dontwarn org.jetbrains.kotlin.diagnostics.rendering.Renderers -dontwarn org.jetbrains.kotlin.diagnostics.rendering.SmartDescriptorRenderer -dontwarn org.jetbrains.kotlin.diagnostics.rendering.SmartTypeRenderer +-dontwarn org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar -dontwarn javax.naming.InvalidNameException -dontwarn javax.naming.NamingException @@ -250,7 +251,3 @@ # Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken - --keepclassmembers class * { - @com.google.api.client.util.Key ; -} \ No newline at end of file From 1bb1e42d8da14f8f1db90a7db1fa5b633c8bdf2b Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 30 May 2024 15:04:57 +0700 Subject: [PATCH 099/100] Fixed reward calculator crash, fixed sora pending payouts 3.5.1(182) build up --- build.gradle | 2 +- .../MakePayoutValidationsModule.kt | 2 +- .../domain/rewards/ReefRewardCalculator.kt | 19 ++++++++------ .../domain/rewards/SoraRewardCalculator.kt | 15 ++++++++--- .../validations/payout/MakePayoutPayload.kt | 17 ++++++++++--- .../payout/ProfitablePayoutValidation.kt | 25 ++++++++++++++++--- .../payouts/confirm/ConfirmPayoutViewModel.kt | 14 ++++++++--- 7 files changed, 71 insertions(+), 23 deletions(-) diff --git a/build.gradle b/build.gradle index 0a730dcd85..6540826c3d 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { ext { // App version versionName = '3.5.1' - versionCode = 181 + versionCode = 182 // SDK and tools compileSdkVersion = 34 diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/di/validations/MakePayoutValidationsModule.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/di/validations/MakePayoutValidationsModule.kt index 0f807d7c06..63dca1967a 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/di/validations/MakePayoutValidationsModule.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/di/validations/MakePayoutValidationsModule.kt @@ -34,7 +34,7 @@ class MakePayoutValidationsModule { accountRepository, walletRepository, originAddressExtractor = { it.originAddress }, - chainAssetExtractor = { it.chainAsset }, + chainAssetExtractor = { it.token.configuration }, chainProducer = { stakingSharedState.chain() } ), errorProducer = { PayoutValidationFailure.CannotPayFee } diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/ReefRewardCalculator.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/ReefRewardCalculator.kt index 87f76fbf15..75e143947f 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/ReefRewardCalculator.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/ReefRewardCalculator.kt @@ -9,11 +9,9 @@ import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.shared_utils.extensions.toHexString -import jp.co.soramitsu.shared_utils.ss58.SS58Encoder.toAccountId import jp.co.soramitsu.staking.impl.data.network.blockhain.bindings.EraRewardPoints import jp.co.soramitsu.staking.impl.data.repository.HistoricalMapping import jp.co.soramitsu.wallet.impl.domain.model.amountFromPlanks -import kotlin.math.pow import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -35,7 +33,7 @@ class ReefRewardCalculator( calculationTargets.associateWith { apyByValidator[it] }.filterValues { it != null } .cast>() - private val maxAPY = apyByCalculationTargets.values.maxOrNull() ?: 0.0 + private val maxAPY = apyByCalculationTargets.values.filter { it.isNaN().not() }.maxOrNull() ?: 0.0 private val expectedAPY = calculateExpectedAPY() private fun calculateExpectedAPY(): Double { @@ -51,11 +49,16 @@ class ReefRewardCalculator( private fun calculateValidatorAPY(validator: RewardCalculationTarget): Double { return runCatching { - val averageValidatorRewardPoints = - historicalRewardDistribution.values.asSequence().map { it.individual } - .flatten() - .filter { it.accountId.contentEquals(validator.accountIdHex.fromHex()) } - .map { it.rewardPoints.toDouble() }.average() + val validatorHistoricalRewardDistribution = historicalRewardDistribution.values.asSequence().map { it.individual } + .flatten() + .filter { it.accountId.contentEquals(validator.accountIdHex.fromHex()) } + .toList() + + val averageValidatorRewardPoints = if(validatorHistoricalRewardDistribution.isEmpty()){ + 0.0 + } else { + validatorHistoricalRewardDistribution.map { it.rewardPoints.toDouble() }.average() + } val rewardDistributionForLastEra = historicalRewardDistribution.maxBy { it.key }.value diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/SoraRewardCalculator.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/SoraRewardCalculator.kt index 55914a56be..07c67d7273 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/SoraRewardCalculator.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/rewards/SoraRewardCalculator.kt @@ -6,6 +6,7 @@ import jp.co.soramitsu.common.utils.fractionToPercentage import jp.co.soramitsu.common.utils.median import jp.co.soramitsu.core.models.Asset import jp.co.soramitsu.runtime.multiNetwork.chain.model.ChainId +import jp.co.soramitsu.shared_utils.extensions.fromHex import jp.co.soramitsu.shared_utils.extensions.toHexString import jp.co.soramitsu.staking.impl.data.network.blockhain.bindings.EraRewardPoints import jp.co.soramitsu.staking.impl.data.repository.HistoricalMapping @@ -52,10 +53,16 @@ class SoraRewardCalculator( } private fun calculateValidatorAPY(validator: RewardCalculationTarget): Double { - val averageValidatorRewardPoints = - historicalRewardDistribution.values.asSequence().map { it.individual }.flatten() - .filter { it.accountId.toHexString(false) == validator.accountIdHex } - .map { it.rewardPoints.toDouble() }.average() + val validatorHistoricalRewardDistribution = historicalRewardDistribution.values.asSequence().map { it.individual } + .flatten() + .filter { it.accountId.contentEquals(validator.accountIdHex.fromHex()) } + .toList() + + val averageValidatorRewardPoints = if(validatorHistoricalRewardDistribution.isEmpty()){ + 0.0 + } else { + validatorHistoricalRewardDistribution.map { it.rewardPoints.toDouble() }.average() + } val validatorOwnStake = asset.amountFromPlanks(validator.totalStake).toDouble() diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/MakePayoutPayload.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/MakePayoutPayload.kt index f1927f6ad7..aacfef2328 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/MakePayoutPayload.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/MakePayoutPayload.kt @@ -1,15 +1,26 @@ package jp.co.soramitsu.staking.impl.domain.validations.payout -import jp.co.soramitsu.core.models.Asset import java.math.BigDecimal import java.math.BigInteger +import jp.co.soramitsu.wallet.impl.domain.model.Token -class MakePayoutPayload( +open class MakePayoutPayload( val originAddress: String, val fee: BigDecimal, val totalReward: BigDecimal, - val chainAsset: Asset, + val token: Token, val payoutStakersCalls: List ) { data class PayoutStakersPayload(val era: BigInteger, val validatorAddress: String) } + +class SoraPayoutsPayload( + originAddress: String, + fee: BigDecimal, + totalReward: BigDecimal, + utilityToken: Token, + val rewardToken: Token, + payoutStakersCalls: List +): MakePayoutPayload(originAddress, fee, totalReward, utilityToken, payoutStakersCalls) { + +} \ No newline at end of file diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/ProfitablePayoutValidation.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/ProfitablePayoutValidation.kt index 0ccce60c5c..1d940b7148 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/ProfitablePayoutValidation.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/domain/validations/payout/ProfitablePayoutValidation.kt @@ -7,10 +7,29 @@ import jp.co.soramitsu.common.validation.ValidationStatus class ProfitablePayoutValidation : Validation { override suspend fun validate(value: MakePayoutPayload): ValidationStatus { - return if (value.fee < value.totalReward) { - ValidationStatus.Valid() + return if (value is SoraPayoutsPayload) { + val feeFiat = value.token.fiatAmount(value.fee) + val totalRewardFiat = value.rewardToken.fiatAmount(value.totalReward) + if (feeFiat == null || totalRewardFiat == null) { + return ValidationStatus.Valid() + } + if (feeFiat < totalRewardFiat) { + ValidationStatus.Valid() + } else { + ValidationStatus.NotValid( + DefaultFailureLevel.WARNING, + reason = PayoutValidationFailure.UnprofitablePayout + ) + } } else { - ValidationStatus.NotValid(DefaultFailureLevel.WARNING, reason = PayoutValidationFailure.UnprofitablePayout) + if (value.fee < value.totalReward) { + ValidationStatus.Valid() + } else { + ValidationStatus.NotValid( + DefaultFailureLevel.WARNING, + reason = PayoutValidationFailure.UnprofitablePayout + ) + } } } } diff --git a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt index 5c9c5655ce..0eb2395fc3 100644 --- a/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt +++ b/feature-staking-impl/src/main/java/jp/co/soramitsu/staking/impl/presentation/payouts/confirm/ConfirmPayoutViewModel.kt @@ -37,6 +37,7 @@ import jp.co.soramitsu.staking.impl.domain.payout.PayoutInteractor import jp.co.soramitsu.staking.impl.domain.rewards.SoraStakingRewardsScenario import jp.co.soramitsu.staking.impl.domain.validations.payout.MakePayoutPayload import jp.co.soramitsu.staking.impl.domain.validations.payout.PayoutValidationFailure +import jp.co.soramitsu.staking.impl.domain.validations.payout.SoraPayoutsPayload import jp.co.soramitsu.staking.impl.presentation.StakingRouter import jp.co.soramitsu.staking.impl.presentation.payouts.confirm.model.ConfirmPayoutPayload import jp.co.soramitsu.staking.impl.scenarios.relaychain.StakingRelayChainScenarioInteractor @@ -71,7 +72,7 @@ class ConfirmPayoutViewModel @Inject constructor( private val payload = savedStateHandle.get(ConfirmPayoutFragment.KEY_PAYOUTS)!! private val tokenFlow = interactor.currentAssetFlow().map { - if(it.token.configuration.syntheticStakingType() == SyntheticStakingType.SORA){ + if(it.token.configuration.syntheticStakingType() == SyntheticStakingType.SORA) { soraRewardScenario.getRewardAsset() } else { it.token @@ -145,13 +146,20 @@ class ConfirmPayoutViewModel @Inject constructor( private fun sendTransactionIfValid() = feeLoaderMixin.requireFee(this) { fee -> launch { - val tokenType = interactor.currentAssetFlow().first().token.configuration + val asset = interactor.currentAssetFlow().first() + val tokenType = asset.token.configuration val accountAddress = stakingStateFlow.first().accountAddress val amount = tokenType.amountFromPlanks(payload.totalRewardInPlanks) val payoutStakersPayloads = payouts.map { MakePayoutPayload.PayoutStakersPayload(it.era, it.validatorAddress) } - val makePayoutPayload = MakePayoutPayload(accountAddress, fee, amount, tokenType, payoutStakersPayloads) + val makePayoutPayload = if(tokenType.syntheticStakingType() == SyntheticStakingType.SORA ) { + val rewardToken = soraRewardScenario.getRewardAsset() + SoraPayoutsPayload(accountAddress, fee, amount, asset.token, rewardToken, payoutStakersPayloads) + } else { + MakePayoutPayload(accountAddress, fee, amount, asset.token, payoutStakersPayloads) + } + validationExecutor.requireValid( validationSystem = validationSystem, From 70a2f6032bcb8bdcbe455f425ca55eb2829cc232 Mon Sep 17 00:00:00 2001 From: Deneath Date: Thu, 30 May 2024 16:45:56 +0700 Subject: [PATCH 100/100] Increase version --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6540826c3d..1f3ecdc940 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ apply plugin: "org.sonarqube" buildscript { ext { // App version - versionName = '3.5.1' - versionCode = 182 + versionName = '3.5.2' + versionCode = 183 // SDK and tools compileSdkVersion = 34