Updated on 2026-08-14
This commit is contained in:
parent
cd9e96c526
commit
25bae0ed98
10 changed files with 243 additions and 15 deletions
|
|
@ -23,4 +23,5 @@ data class ActionButtonConfig(
|
|||
val onLongClick: (() -> TextReference?)? = null,
|
||||
val enabled: Boolean = true,
|
||||
val dimContent: Boolean = false,
|
||||
val isInProgress: Boolean = false,
|
||||
)
|
||||
|
|
@ -8,8 +8,9 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -77,7 +78,6 @@ fun ActionButton(
|
|||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun Button(
|
||||
config: ActionButtonConfig,
|
||||
|
|
@ -89,12 +89,33 @@ private fun Button(
|
|||
targetValue = if (config.enabled) color else TangemTheme.colors.button.disabled,
|
||||
label = "Update background color",
|
||||
)
|
||||
val context = LocalContext.current
|
||||
Row(
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.heightIn(min = TangemTheme.dimens.size36)
|
||||
.clip(shape)
|
||||
.background(color = backgroundColor)
|
||||
.background(color = backgroundColor),
|
||||
) {
|
||||
Content(config = config)
|
||||
|
||||
if (config.isInProgress) {
|
||||
Loading(
|
||||
backgroundColor = backgroundColor,
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.align(alignment = Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun Content(config: ActionButtonConfig) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.combinedClickable(
|
||||
enabled = config.enabled,
|
||||
onClick = config.onClick,
|
||||
|
|
@ -149,6 +170,19 @@ private fun Button(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Loading(backgroundColor: Color, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(color = backgroundColor),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size24),
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(group = "RoundedActionButton", showBackground = true)
|
||||
@Preview(group = "RoundedActionButton", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
@ -188,5 +222,12 @@ private class ActionStateProvider : CollectionPreviewParameterProvider<ActionBut
|
|||
enabled = false,
|
||||
onClick = {},
|
||||
),
|
||||
ActionButtonConfig(
|
||||
text = TextReference.Str(value = "Loading"),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
enabled = false,
|
||||
onClick = {},
|
||||
isInProgress = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -10,10 +11,12 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class MultiWalletContentLoader(
|
||||
|
|
@ -28,10 +31,12 @@ internal class MultiWalletContentLoader(
|
|||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
return listOf(
|
||||
return listOfNotNull(
|
||||
MultiWalletTokenListSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
|
|
@ -50,6 +55,16 @@ internal class MultiWalletContentLoader(
|
|||
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
),
|
||||
createMultiWalletButtonsSubscriber(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMultiWalletButtonsSubscriber(): MultiWalletActionsSubscriber? {
|
||||
return MultiWalletActionsSubscriber(
|
||||
userWallet = userWallet,
|
||||
rampStateManager = rampStateManager,
|
||||
stateController = stateHolder,
|
||||
)
|
||||
.takeIf { walletFeatureToggles.isMainActionButtonsEnabled }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
||||
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -11,6 +12,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenList
|
|||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -26,6 +28,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): WalletContentLoader {
|
||||
|
|
@ -41,6 +45,8 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
rampStateManager = rampStateManager,
|
||||
walletFeatureToggles = walletFeatureToggles,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
val isInProgress: Boolean = false,
|
||||
) :
|
||||
WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
|
|
@ -46,6 +47,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
isInProgress = isInProgress,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -85,10 +87,10 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_receive),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -117,6 +119,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
val isInProgress: Boolean = false,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_sell),
|
||||
|
|
@ -124,6 +127,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
isInProgress = isInProgress,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -138,6 +142,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
val isInProgress: Boolean = false,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.swapping_swap_action),
|
||||
|
|
@ -145,6 +150,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
isInProgress = isInProgress,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
||||
internal class UpdateMultiWalletActionsTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val buyStatus: Lce<Throwable, Any>,
|
||||
private val sellStatus: Lce<Throwable, Any>,
|
||||
private val swapStatus: Lce<Throwable, Any>,
|
||||
) : WalletStateTransformer(userWalletId = userWalletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.MultiCurrency.Content -> {
|
||||
prevState.copy(buttons = prevState.buttons.updateButtons())
|
||||
}
|
||||
is WalletState.MultiCurrency.Locked -> {
|
||||
Timber.w("Impossible to load primary currency status for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
Timber.w("Impossible to load crypto currency actions for multi-currency wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load crypto currency actions for VISA wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PersistentList<WalletManageButton>.updateButtons(): PersistentList<WalletManageButton> {
|
||||
return map {
|
||||
when (it) {
|
||||
is WalletManageButton.Buy -> {
|
||||
it.copy(
|
||||
enabled = buyStatus.isContent(),
|
||||
dimContent = !buyStatus.isContent(),
|
||||
)
|
||||
}
|
||||
is WalletManageButton.Sell -> {
|
||||
it.copy(
|
||||
enabled = sellStatus.isContent(),
|
||||
dimContent = !sellStatus.isContent(),
|
||||
)
|
||||
}
|
||||
is WalletManageButton.Swap -> {
|
||||
it.copy(
|
||||
enabled = swapStatus.isContent(),
|
||||
dimContent = !swapStatus.isContent(),
|
||||
)
|
||||
}
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
.toPersistentList()
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ internal class WalletLoadingStateFactory(
|
|||
return WalletState.MultiCurrency.Content(
|
||||
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||
walletCardState = userWallet.toLoadingWalletCardState(),
|
||||
buttons = createMultiWalletActions(),
|
||||
buttons = createMultiWalletActions(userWallet),
|
||||
warnings = persistentListOf(),
|
||||
bottomSheetConfig = null,
|
||||
tokensListState = WalletTokensListState.ContentState.Loading,
|
||||
|
|
@ -65,7 +65,7 @@ internal class WalletLoadingStateFactory(
|
|||
return WalletState.Visa.Content(
|
||||
pullToRefreshConfig = createPullToRefreshConfig(),
|
||||
walletCardState = userWallet.toLoadingWalletCardState(),
|
||||
buttons = createMultiWalletActions(),
|
||||
buttons = createMultiWalletActions(userWallet),
|
||||
warnings = persistentListOf(),
|
||||
bottomSheetConfig = null,
|
||||
balancesAndLimitBlockState = BalancesAndLimitsBlockState.Loading,
|
||||
|
|
@ -93,13 +93,25 @@ internal class WalletLoadingStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createMultiWalletActions(): PersistentList<WalletManageButton> {
|
||||
private fun createMultiWalletActions(userWallet: UserWallet): PersistentList<WalletManageButton> {
|
||||
if (!walletFeatureToggles.isMainActionButtonsEnabled) return persistentListOf()
|
||||
|
||||
return persistentListOf(
|
||||
WalletManageButton.Buy(enabled = false, dimContent = true, onClick = { }),
|
||||
WalletManageButton.Swap(enabled = false, dimContent = true, onClick = { }),
|
||||
WalletManageButton.Sell(enabled = false, dimContent = true, onClick = { }),
|
||||
WalletManageButton.Buy(
|
||||
enabled = false,
|
||||
dimContent = true,
|
||||
onClick = { clickIntents.onMultiWalletBuyClick(userWalletId = userWallet.walletId) },
|
||||
),
|
||||
WalletManageButton.Swap(
|
||||
enabled = false,
|
||||
dimContent = true,
|
||||
onClick = { clickIntents.onMultiWalletSwapClick(userWalletId = userWallet.walletId) },
|
||||
),
|
||||
WalletManageButton.Sell(
|
||||
enabled = false,
|
||||
dimContent = true,
|
||||
onClick = { clickIntents.onMultiWalletSellClick(userWalletId = userWallet.walletId) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.UpdateMultiWalletActionsTransformer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
/**
|
||||
* Multi-currency wallet subscriber for actions state updating
|
||||
*
|
||||
* @property userWallet user wallet
|
||||
* @property rampStateManager ramp state manager
|
||||
* @property stateController state controller
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class MultiWalletActionsSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val stateController: WalletStateController,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = rampStateManager.getBuyInitializationStatus(),
|
||||
flow2 = rampStateManager.getSellInitializationStatus(),
|
||||
flow3 = rampStateManager.getSwapInitializationStatus(userWalletId = userWallet.walletId),
|
||||
transform = ::RampStatuses,
|
||||
)
|
||||
.onEach { statuses ->
|
||||
stateController.update(
|
||||
UpdateMultiWalletActionsTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
buyStatus = statuses.buy,
|
||||
sellStatus = statuses.sell,
|
||||
swapStatus = statuses.swap,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class RampStatuses(
|
||||
val buy: Lce<Throwable, Any>,
|
||||
val sell: Lce<Throwable, Any>,
|
||||
val swap: Lce<Throwable, Any>,
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.settings.NeverToShowWalletsScrollPreview
|
||||
import com.tangem.domain.tokens.FetchCardTokenListUseCase
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
|
|
@ -20,6 +21,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetToken
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -41,6 +44,7 @@ internal class WalletClickIntents @Inject constructor(
|
|||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val neverToShowWalletsScrollPreview: NeverToShowWalletsScrollPreview,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : BaseWalletClickIntents(),
|
||||
WalletCardClickIntents by walletCardClickIntentsImplementor,
|
||||
|
|
@ -116,6 +120,12 @@ internal class WalletClickIntents @Inject constructor(
|
|||
fetchTokenListUseCase(userWalletId = userWallet.walletId, mode = RefreshMode.FULL)
|
||||
}
|
||||
|
||||
listOf(
|
||||
async { rampStateManager.fetchBuyServiceData() },
|
||||
async { rampStateManager.fetchSellServiceData() },
|
||||
)
|
||||
.awaitAll()
|
||||
|
||||
maybeFetchResult.onLeft {
|
||||
stateHolder.update(
|
||||
SetTokenListErrorTransformer(
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ interface WalletCurrencyActionsClickIntents {
|
|||
fun onExploreClick()
|
||||
|
||||
fun onAnalyticsClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onMultiWalletBuyClick(userWalletId: UserWalletId)
|
||||
|
||||
fun onMultiWalletSellClick(userWalletId: UserWalletId)
|
||||
|
||||
fun onMultiWalletSwapClick(userWalletId: UserWalletId)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -432,6 +438,21 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onMultiWalletSellClick(userWalletId: UserWalletId) {
|
||||
// TODO [REDACTED_JIRA]
|
||||
appRouter.push(route = AppRoute.SellCrypto(userWalletId = userWalletId))
|
||||
}
|
||||
|
||||
override fun onMultiWalletSwapClick(userWalletId: UserWalletId) {
|
||||
// TODO [REDACTED_JIRA]
|
||||
// appRouter.push(route = AppRoute.SwapCrypto(userWalletId = userWalletId))
|
||||
}
|
||||
|
||||
override fun onMultiWalletBuyClick(userWalletId: UserWalletId) {
|
||||
// TODO [REDACTED_JIRA]
|
||||
appRouter.push(route = AppRoute.BuyCrypto(userWalletId = userWalletId))
|
||||
}
|
||||
|
||||
private fun openExplorer() {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue