Updated on 2026-08-14
This commit is contained in:
parent
33909502ed
commit
72a1365157
8 changed files with 312 additions and 54 deletions
|
|
@ -26,21 +26,34 @@ dependencies {
|
|||
implementation(deps.compose.accompanist.systemUiController)
|
||||
implementation(deps.compose.coil)
|
||||
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.tangem.blockchain)
|
||||
implementation(deps.tangem.card.core)
|
||||
implementation(deps.timber)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.common)
|
||||
implementation(projects.core.featuretoggles)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
/** Domain modules */
|
||||
implementation(projects.domain.appCurrency)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.txhistory)
|
||||
implementation(projects.domain.txhistory.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
/** Feature Apis */
|
||||
implementation(projects.features.tokendetails.api)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
|
|
@ -38,6 +39,7 @@ internal class TokenDetailsFragment : Fragment() {
|
|||
|
||||
val viewModel = hiltViewModel<TokenDetailsViewModel>()
|
||||
viewModel.router = this@TokenDetailsFragment.internalTokenDetailsRouter
|
||||
LocalLifecycleOwner.current.lifecycle.addObserver(viewModel)
|
||||
TokenDetailsScreen(state = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails
|
|||
|
||||
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
|
|
@ -41,7 +40,8 @@ internal object TokenDetailsPreviewData {
|
|||
),
|
||||
)
|
||||
|
||||
private val actionButtons = persistentListOf(
|
||||
// TODO: [REDACTED_JIRA]
|
||||
val actionButtons = persistentListOf(
|
||||
ActionButtonConfig(
|
||||
text = TextReference.Str(value = "Buy"),
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
|
|
@ -64,7 +64,8 @@ internal object TokenDetailsPreviewData {
|
|||
),
|
||||
)
|
||||
|
||||
private val disabledActionButtons = actionButtons.map { it.copy(enabled = false) }.toPersistentList()
|
||||
// TODO: [REDACTED_JIRA]
|
||||
val disabledActionButtons = actionButtons.map { it.copy(enabled = false) }.toPersistentList()
|
||||
|
||||
val balanceLoading = TokenDetailsBalanceBlockState.Loading(actionButtons = disabledActionButtons)
|
||||
val balanceContent = TokenDetailsBalanceBlockState.Content(
|
||||
|
|
@ -74,15 +75,6 @@ internal object TokenDetailsPreviewData {
|
|||
)
|
||||
val balanceError = TokenDetailsBalanceBlockState.Error(actionButtons = disabledActionButtons)
|
||||
|
||||
val marketPriceContent = MarketPriceBlockState.Content(
|
||||
currencyName = "USDT",
|
||||
price = "98900 $",
|
||||
priceChangeConfig = PriceChangeConfig(
|
||||
valueInPercent = "10.89%",
|
||||
type = PriceChangeConfig.Type.UP,
|
||||
),
|
||||
)
|
||||
|
||||
private val marketPriceLoading = MarketPriceBlockState.Loading(currencyName = "USDT")
|
||||
|
||||
val tokenDetailsState = TokenDetailsState(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
class TokenDetailsLoadedBalanceConverter(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Either<CurrencyError, CryptoCurrencyStatus>, TokenDetailsState> {
|
||||
|
||||
override fun convert(value: Either<CurrencyError, CryptoCurrencyStatus>): TokenDetailsState {
|
||||
return value.fold(ifLeft = { convertError() }, ifRight = ::convert)
|
||||
}
|
||||
|
||||
private fun convertError(): TokenDetailsState {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
return currentStateProvider()
|
||||
}
|
||||
|
||||
private fun convert(status: CryptoCurrencyStatus): TokenDetailsState {
|
||||
val state = currentStateProvider()
|
||||
val currencyName = state.marketPriceBlockState.currencyName
|
||||
return state.copy(
|
||||
tokenBalanceBlockState = getBalanceState(status),
|
||||
marketPriceBlockState = getMarketPriceState(status = status.value, currencyName = currencyName),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getBalanceState(status: CryptoCurrencyStatus): TokenDetailsBalanceBlockState {
|
||||
return when (status.value) {
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
-> {
|
||||
TokenDetailsBalanceBlockState.Content(
|
||||
actionButtons = TokenDetailsPreviewData.actionButtons,
|
||||
fiatBalance = formatFiatAmount(status.value, appCurrencyProvider()),
|
||||
cryptoBalance = formatCryptoAmount(status),
|
||||
)
|
||||
}
|
||||
is CryptoCurrencyStatus.Loading -> {
|
||||
TokenDetailsBalanceBlockState.Loading(TokenDetailsPreviewData.disabledActionButtons)
|
||||
}
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
// TODO: [REDACTED_JIRA]
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> {
|
||||
TokenDetailsBalanceBlockState.Error(TokenDetailsPreviewData.actionButtons)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMarketPriceState(status: CryptoCurrencyStatus.Status, currencyName: String): MarketPriceBlockState {
|
||||
return when (status) {
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
-> MarketPriceBlockState.Content(
|
||||
currencyName = currencyName,
|
||||
price = formatPrice(status, appCurrencyProvider()),
|
||||
priceChangeConfig = PriceChangeConfig(
|
||||
valueInPercent = formatPriceChange(status),
|
||||
type = getPriceChangeType(status),
|
||||
),
|
||||
)
|
||||
is CryptoCurrencyStatus.Loading -> MarketPriceBlockState.Loading(currencyName)
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> MarketPriceBlockState.Error(currencyName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeConfig.Type {
|
||||
val priceChange = status.priceChange ?: return PriceChangeConfig.Type.DOWN
|
||||
|
||||
return if (priceChange > BigDecimal.ZERO) {
|
||||
PriceChangeConfig.Type.UP
|
||||
} else {
|
||||
PriceChangeConfig.Type.DOWN
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String {
|
||||
val priceChange = status.priceChange ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatPercent(
|
||||
percent = priceChange,
|
||||
useAbsoluteValue = true,
|
||||
)
|
||||
}
|
||||
|
||||
private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
|
||||
val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = fiatRate,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String {
|
||||
val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = fiatAmount,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
private fun formatCryptoAmount(status: CryptoCurrencyStatus): String {
|
||||
val amount = status.value.amount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, status.currency.symbol, status.currency.decimals)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSkeletonStateConverter.SkeletonModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class TokenDetailsSkeletonStateConverter(
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
) : Converter<SkeletonModel, TokenDetailsState> {
|
||||
|
||||
override fun convert(value: SkeletonModel): TokenDetailsState {
|
||||
return TokenDetailsState(
|
||||
topAppBarConfig = TokenDetailsTopAppBarConfig(
|
||||
onBackClick = clickIntents::onBackClick,
|
||||
onMoreClick = clickIntents::onMoreClick,
|
||||
),
|
||||
tokenInfoBlockState = TokenInfoBlockState(
|
||||
name = value.cryptoCurrency.name,
|
||||
iconUrl = requireNotNull(value.cryptoCurrency.iconUrl),
|
||||
currency = when (value.cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> TokenInfoBlockState.Currency.Native
|
||||
is CryptoCurrency.Token -> TokenInfoBlockState.Currency.Token(
|
||||
networkName = value.cryptoCurrency.standardType.name,
|
||||
blockchainName = value.cryptoCurrency.blockchainName,
|
||||
// TODO: [REDACTED_JIRA]
|
||||
networkIcon = R.drawable.img_eth_22,
|
||||
)
|
||||
},
|
||||
),
|
||||
tokenBalanceBlockState = TokenDetailsBalanceBlockState.Loading(
|
||||
TokenDetailsPreviewData.disabledActionButtons,
|
||||
),
|
||||
marketPriceBlockState = MarketPriceBlockState.Loading(value.cryptoCurrency.name),
|
||||
)
|
||||
}
|
||||
|
||||
data class SkeletonModel(val cryptoCurrency: CryptoCurrency)
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
|
||||
internal class TokenDetailsStateFactory(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
) {
|
||||
|
||||
private val skeletonStateConverter by lazy {
|
||||
TokenDetailsSkeletonStateConverter(clickIntents = clickIntents)
|
||||
}
|
||||
|
||||
private val tokenDetailsLoadedBalanceConverter by lazy {
|
||||
TokenDetailsLoadedBalanceConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
)
|
||||
}
|
||||
|
||||
fun getInitialState(cryptoCurrency: CryptoCurrency): TokenDetailsState {
|
||||
return skeletonStateConverter.convert(
|
||||
TokenDetailsSkeletonStateConverter.SkeletonModel(cryptoCurrency = cryptoCurrency),
|
||||
)
|
||||
}
|
||||
|
||||
fun getCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither: Either<CurrencyError, CryptoCurrencyStatus>,
|
||||
): TokenDetailsState {
|
||||
return tokenDetailsLoadedBalanceConverter.convert(cryptoCurrencyEither)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
||||
|
||||
interface TokenDetailsClickIntents {
|
||||
|
||||
fun onBackClick()
|
||||
|
||||
fun onMoreClick()
|
||||
}
|
||||
|
|
@ -3,67 +3,94 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.*
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.GetCurrencyUseCase
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
private const val LOADING_DELAY = 4_000L
|
||||
|
||||
@HiltViewModel
|
||||
internal class TokenDetailsViewModel @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
private val getCurrencyUseCase: GetCurrencyUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
|
||||
|
||||
private val cryptoCurrency: CryptoCurrency = savedStateHandle[TokenDetailsRouter.SELECTED_CURRENCY_KEY]
|
||||
?: error("no expected parameter CryptoCurrency found")
|
||||
|
||||
var router by Delegates.notNull<InnerTokenDetailsRouter>()
|
||||
|
||||
var uiState by mutableStateOf(getInitialState())
|
||||
private val marketPriceJobHolder = JobHolder()
|
||||
|
||||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
private val stateFactory = TokenDetailsStateFactory(
|
||||
currentStateProvider = Provider { uiState },
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
clickIntents = this,
|
||||
)
|
||||
var uiState: TokenDetailsState by mutableStateOf(stateFactory.getInitialState(cryptoCurrency))
|
||||
private set
|
||||
|
||||
init {
|
||||
// simulate loading state
|
||||
viewModelScope.launch {
|
||||
delay(LOADING_DELAY)
|
||||
uiState = uiState.copy(
|
||||
tokenBalanceBlockState = TokenDetailsPreviewData.balanceContent,
|
||||
marketPriceBlockState = TokenDetailsPreviewData.marketPriceContent,
|
||||
)
|
||||
}
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
updateContent(selectedWallet = getWallet(), refresh = false)
|
||||
}
|
||||
|
||||
private fun getInitialState() = TokenDetailsPreviewData.tokenDetailsState.copy(
|
||||
topAppBarConfig = TokenDetailsPreviewData.tokenDetailsTopAppBarConfig.copy(
|
||||
onBackClick = ::onBackClick,
|
||||
),
|
||||
tokenInfoBlockState = TokenInfoBlockState(
|
||||
name = cryptoCurrency.name,
|
||||
iconUrl = requireNotNull(cryptoCurrency.iconUrl),
|
||||
currency = when (cryptoCurrency) {
|
||||
is CryptoCurrency.Coin -> TokenInfoBlockState.Currency.Native
|
||||
is CryptoCurrency.Token -> TokenInfoBlockState.Currency.Token(
|
||||
networkName = cryptoCurrency.standardType.name,
|
||||
blockchainName = cryptoCurrency.blockchainName,
|
||||
// TODO: [REDACTED_JIRA]
|
||||
networkIcon = R.drawable.img_eth_22,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
private fun getWallet(): UserWallet {
|
||||
return getSelectedWalletUseCase()
|
||||
.fold(
|
||||
ifLeft = { error("Can not get selected wallet $it") },
|
||||
ifRight = { it },
|
||||
)
|
||||
}
|
||||
|
||||
private fun onBackClick() {
|
||||
private fun updateContent(selectedWallet: UserWallet, refresh: Boolean) {
|
||||
updateMarketPrice(selectedWallet = selectedWallet, refresh = refresh)
|
||||
}
|
||||
|
||||
private fun updateMarketPrice(selectedWallet: UserWallet, refresh: Boolean) {
|
||||
getCurrencyUseCase(userWalletId = selectedWallet.walletId, currencyId = cryptoCurrency.id, refresh = refresh)
|
||||
.distinctUntilChanged()
|
||||
.onEach { uiState = stateFactory.getCurrencyLoadedBalanceState(it) }
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(viewModelScope)
|
||||
.saveIn(marketPriceJobHolder)
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
return getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBackClick() {
|
||||
router.popBackStack()
|
||||
}
|
||||
|
||||
override fun onMoreClick() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue