Updated on 2026-08-14
This commit is contained in:
parent
1c3dd8cf3f
commit
65ead13295
7 changed files with 279 additions and 6 deletions
|
|
@ -33,7 +33,7 @@ import com.tangem.wallet.R
|
|||
import kotlin.math.max
|
||||
|
||||
@Composable
|
||||
fun StoriesScreen(
|
||||
internal fun StoriesScreen(
|
||||
homeState: MutableState<HomeState>,
|
||||
onScanButtonClick: () -> Unit,
|
||||
onShopButtonClick: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.features.details.DetailsFeatureToggles
|
|||
import com.tangem.features.details.component.DetailsComponent
|
||||
import com.tangem.features.disclaimer.api.components.DisclaimerComponent
|
||||
import com.tangem.features.managetokens.ManageTokensToggles
|
||||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
||||
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
|
|
@ -48,6 +49,7 @@ internal class ChildFactory @Inject constructor(
|
|||
private val detailsComponentFactory: DetailsComponent.Factory,
|
||||
private val walletSettingsComponentFactory: WalletSettingsComponent.Factory,
|
||||
private val disclaimerComponentFactory: DisclaimerComponent.Factory,
|
||||
private val manageTokensComponentFactory: ManageTokensComponent.Factory,
|
||||
private val sendRouter: SendRouter,
|
||||
private val tokenDetailsRouter: TokenDetailsRouter,
|
||||
private val walletRouter: WalletRouter,
|
||||
|
|
@ -122,7 +124,11 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.ManageTokens -> {
|
||||
if (manageTokensToggles.isFeatureEnabled) {
|
||||
TODO("Not implemented yet") // [REDACTED_JIRA]
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = ManageTokensComponent.Params(),
|
||||
componentFactory = manageTokensComponentFactory,
|
||||
)
|
||||
} else {
|
||||
route.asFragmentChild(Provider { TokensListFragment() })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,10 @@ package com.tangem.features.managetokens.component
|
|||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface ManageTokensComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
)
|
||||
class Params
|
||||
|
||||
interface Factory : ComponentFactory<Params, ManageTokensComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.features.managetokens.component.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||
import com.tangem.features.managetokens.model.ManageTokensModel
|
||||
import com.tangem.features.managetokens.ui.ManageTokensScreen
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultManageTokensComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted params: ManageTokensComponent.Params,
|
||||
) : ManageTokensComponent, AppComponentContext by context {
|
||||
|
||||
private val model: ManageTokensModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
||||
ManageTokensScreen(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : ManageTokensComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: ManageTokensComponent.Params,
|
||||
): DefaultManageTokensComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.features.managetokens.di
|
||||
|
||||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||
import com.tangem.features.managetokens.component.impl.DefaultManageTokensComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface ComponentModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindManageTokensComponentFactory(factory: DefaultManageTokensComponent.Factory): ManageTokensComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.managetokens.di
|
||||
|
||||
import com.tangem.core.decompose.di.DecomposeComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.managetokens.model.ManageTokensModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(DecomposeComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(ManageTokensModel::class)
|
||||
fun provideManageTokensModel(model: ManageTokensModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
package com.tangem.features.managetokens.model
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.components.rows.model.BlockchainRowUM
|
||||
import com.tangem.core.ui.components.rows.model.ChainRowUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.managetokens.entity.CurrencyItemUM
|
||||
import com.tangem.features.managetokens.entity.CurrencyNetworkUM
|
||||
import com.tangem.features.managetokens.entity.ManageTokensUM
|
||||
import com.tangem.features.managetokens.impl.R
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.mutate
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
internal class ManageTokensModel @Inject constructor(
|
||||
private val router: Router,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val changedItemsIds: MutableSet<String> = mutableSetOf()
|
||||
private var items = initItems()
|
||||
|
||||
val state: MutableStateFlow<ManageTokensUM> = MutableStateFlow(
|
||||
value = ManageTokensUM(
|
||||
popBack = router::pop,
|
||||
items = items,
|
||||
search = SearchBarUM(
|
||||
placeholderText = resourceReference(R.string.manage_tokens_search_placeholder),
|
||||
query = "",
|
||||
onQueryChange = ::searchCurrencies,
|
||||
isActive = false,
|
||||
onActiveChange = ::toggleSearchBar,
|
||||
),
|
||||
hasChanges = false,
|
||||
onSaveClick = {},
|
||||
onAddCustomToken = {},
|
||||
),
|
||||
)
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun searchCurrencies(query: String) {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
}
|
||||
|
||||
private fun toggleSearchBar(isActive: Boolean) {
|
||||
state.update { state ->
|
||||
state.copy(
|
||||
search = state.search.copy(isActive = isActive),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initItems() = List(size = 30) { index ->
|
||||
if (index < 2) {
|
||||
getCustomItem(index)
|
||||
} else {
|
||||
getBasicItem(index)
|
||||
}
|
||||
}.toPersistentList()
|
||||
|
||||
private fun getCustomItem(index: Int) = CurrencyItemUM.Custom(
|
||||
id = index.toString(),
|
||||
model = ChainRowUM(
|
||||
name = "Custom token $index",
|
||||
type = "CT$index",
|
||||
icon = CurrencyIconState.CustomTokenIcon(
|
||||
tint = Color.White,
|
||||
background = Color.Black,
|
||||
topBadgeIconResId = R.drawable.img_eth_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = true,
|
||||
),
|
||||
showCustom = true,
|
||||
),
|
||||
onRemoveClick = {},
|
||||
)
|
||||
|
||||
private fun getBasicItem(index: Int) = CurrencyItemUM.Basic(
|
||||
id = index.toString(),
|
||||
model = ChainRowUM(
|
||||
name = "Currency $index",
|
||||
type = "C$index",
|
||||
icon = CurrencyIconState.CoinIcon(
|
||||
url = null,
|
||||
fallbackResId = R.drawable.img_btc_22,
|
||||
isGrayscale = false,
|
||||
showCustomBadge = false,
|
||||
),
|
||||
showCustom = false,
|
||||
),
|
||||
networks = if (index == 2) {
|
||||
CurrencyItemUM.Basic.NetworksUM.Expanded(getCurrencyNetworks(index))
|
||||
} else {
|
||||
CurrencyItemUM.Basic.NetworksUM.Collapsed
|
||||
},
|
||||
onExpandClick = { toggleCurrency(index) },
|
||||
)
|
||||
|
||||
private fun getCurrencyNetworks(currencyIndex: Int) = List(size = 3) { networkIndex ->
|
||||
CurrencyNetworkUM(
|
||||
id = networkIndex.toString(),
|
||||
model = BlockchainRowUM(
|
||||
name = "NETWORK$networkIndex",
|
||||
type = "N$networkIndex",
|
||||
iconResId = R.drawable.ic_eth_16,
|
||||
isMainNetwork = networkIndex == 0,
|
||||
isSelected = false,
|
||||
),
|
||||
isSelected = false,
|
||||
onSelectedStateChange = { toggleNetwork(currencyIndex, networkIndex, isSelected = it) },
|
||||
)
|
||||
}.toImmutableList()
|
||||
|
||||
private fun toggleCurrency(index: Int) {
|
||||
val updatedItem = when (val item = items[index]) {
|
||||
is CurrencyItemUM.Basic -> item.copy(
|
||||
networks = if (item.networks is CurrencyItemUM.Basic.NetworksUM.Collapsed) {
|
||||
CurrencyItemUM.Basic.NetworksUM.Expanded(getCurrencyNetworks(index))
|
||||
} else {
|
||||
CurrencyItemUM.Basic.NetworksUM.Collapsed
|
||||
},
|
||||
)
|
||||
is CurrencyItemUM.Custom -> return
|
||||
}
|
||||
|
||||
state.update { state ->
|
||||
items = items.mutate {
|
||||
it[index] = updatedItem
|
||||
}
|
||||
state.copy(items = items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleNetwork(currencyIndex: Int, networkIndex: Int, isSelected: Boolean) {
|
||||
val updatedItem = when (val item = items[currencyIndex]) {
|
||||
is CurrencyItemUM.Basic -> {
|
||||
val updatedNetworks = (item.networks as? CurrencyItemUM.Basic.NetworksUM.Expanded)
|
||||
?.copy(
|
||||
networks = item.networks.networks.toPersistentList().mutate {
|
||||
it.fastForEachIndexed { index, network ->
|
||||
if (index == networkIndex) {
|
||||
it[index] = network.copy(
|
||||
model = network.model.copy(
|
||||
iconResId = if (isSelected) {
|
||||
R.drawable.img_eth_22
|
||||
} else {
|
||||
R.drawable.ic_eth_16
|
||||
},
|
||||
isSelected = isSelected,
|
||||
),
|
||||
isSelected = isSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
?: return
|
||||
|
||||
item.copy(networks = updatedNetworks)
|
||||
}
|
||||
is CurrencyItemUM.Custom -> return
|
||||
}
|
||||
|
||||
val id = "${currencyIndex}_$networkIndex"
|
||||
if (changedItemsIds.contains(id)) {
|
||||
changedItemsIds.remove(id)
|
||||
} else {
|
||||
changedItemsIds.add(id)
|
||||
}
|
||||
|
||||
state.update { state ->
|
||||
items = items.mutate {
|
||||
it[currencyIndex] = updatedItem
|
||||
}
|
||||
state.copy(
|
||||
items = items,
|
||||
hasChanges = changedItemsIds.isNotEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue