Updated on 2026-08-14
This commit is contained in:
parent
9957e986e1
commit
a774347c70
29 changed files with 887 additions and 86 deletions
|
|
@ -198,6 +198,8 @@ dependencies {
|
|||
implementation(projects.features.biometry.impl)
|
||||
implementation(projects.features.nft.api)
|
||||
implementation(projects.features.nft.impl)
|
||||
implementation(projects.features.walletconnect.api)
|
||||
implementation(projects.features.walletconnect.impl)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
|
|
|
|||
|
|
@ -8,4 +8,7 @@ internal class WalletConnectFeatureToggles(
|
|||
|
||||
val isSolanaTxSignEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "WC_SOLANA_TX_SIGN_ENABLED")
|
||||
|
||||
val isRedesignedWalletConnectEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("WALLET_CONNECT_REDESIGN_ENABLED")
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import com.tangem.features.swap.SwapComponent
|
|||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import com.tangem.features.wallet.WalletEntryComponent
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent
|
||||
import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent
|
||||
import com.tangem.tap.features.details.ui.cardsettings.api.CardSettingsComponent
|
||||
|
|
@ -43,6 +44,7 @@ import com.tangem.utils.Provider
|
|||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import java.util.WeakHashMap
|
||||
import javax.inject.Inject
|
||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent as RedisegnedWalletConnectComponent
|
||||
|
||||
@ActivityScoped
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -78,8 +80,10 @@ internal class ChildFactory @Inject constructor(
|
|||
private val walletComponentFactory: WalletEntryComponent.Factory,
|
||||
private val sendComponentFactoryV2: com.tangem.features.send.v2.api.SendComponent.Factory,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val redesignedWalletConnectComponentFactory: RedisegnedWalletConnectComponent.Factory,
|
||||
private val testerRouter: TesterRouter,
|
||||
private val routingFeatureToggles: RoutingFeatureToggles,
|
||||
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
) {
|
||||
|
||||
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
|
||||
|
|
@ -296,11 +300,19 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
is AppRoute.WalletConnectSessions -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = redesignedWalletConnectComponentFactory,
|
||||
)
|
||||
} else {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.QrScanning -> {
|
||||
val source = when (route.source) {
|
||||
|
|
@ -574,11 +586,19 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
is AppRoute.WalletConnectSessions -> {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = redesignedWalletConnectComponentFactory,
|
||||
)
|
||||
} else {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.CurrencyDetails -> {
|
||||
route.asComponentChild(
|
||||
|
|
|
|||
|
|
@ -78,5 +78,9 @@
|
|||
{
|
||||
"name": "SEND_V2_ENABLED",
|
||||
"version": "undefined"
|
||||
},
|
||||
{
|
||||
"name": "WALLET_CONNECT_REDESIGN_ENABLED",
|
||||
"version": "undefined"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@
|
|||
</plurals>
|
||||
<string name="common_delete">Delete</string>
|
||||
<string name="common_disabled">Disabled</string>
|
||||
<string name="common_disconnect">Disconnect</string>
|
||||
<string name="common_done">Done</string>
|
||||
<string name="common_enable">Enable</string>
|
||||
<string name="common_enabled">Enabled</string>
|
||||
|
|
@ -1213,6 +1214,13 @@
|
|||
<string name="warning_testnet_card_message">This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.</string>
|
||||
<string name="warning_testnet_card_title">For testing purposes only</string>
|
||||
<string name="warning_token_balance_not_updated">Balance may be outdated. Refresh the page.</string>
|
||||
<string name="wc_connections">Connections</string>
|
||||
<string name="wc_disconnect_all">Disconnect all</string>
|
||||
<string name="wc_disconnect_all_alert_desc">Text about discnected all dApps</string>
|
||||
<string name="wc_disconnect_all_alert_title">Disconect All dApps</string>
|
||||
<string name="wc_new_connection">New connection</string>
|
||||
<string name="wc_no_sessions_desc">Connect your wallet to a different dApps</string>
|
||||
<string name="wc_no_sessions_title">No sessions</string>
|
||||
<string name="welcome_interrupted_backup_alert_discard">Discard</string>
|
||||
<string name="welcome_interrupted_backup_alert_message">You have an interrupted backup. Do you want to resume?</string>
|
||||
<string name="welcome_interrupted_backup_alert_resume">Yes, resume</string>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package com.tangem.core.ui.components.dropdownmenu
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
@Suppress("ComposableEventParameterNaming")
|
||||
@Composable
|
||||
fun TangemDropdownItem(item: TangemDropdownMenuItem, dismissParent: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.clickable {
|
||||
dismissParent()
|
||||
item.onClick()
|
||||
}
|
||||
.padding(vertical = TangemTheme.dimens.spacing8, horizontal = TangemTheme.dimens.spacing16),
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.button.copy(color = item.textColorProvider()),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_TokenDetailsAppBarDropdownItem() {
|
||||
TangemThemePreview {
|
||||
TangemDropdownItem(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
dismissParent = {},
|
||||
item = TangemDropdownMenuItem(
|
||||
title = TextReference.Res(id = R.string.token_details_hide_token),
|
||||
textColorProvider = { TangemTheme.colors.text.warning },
|
||||
onClick = { },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
@file:Suppress("TopLevelPropertyNaming")
|
||||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
|
||||
|
||||
package com.tangem.core.ui.components.dropdownmenu
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
|
|
@ -15,16 +21,19 @@ import androidx.compose.ui.unit.*
|
|||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupPositionProvider
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Just copy paste [DropdownMenu] from material3 with deleting vertical paddings.
|
||||
*/
|
||||
@Composable
|
||||
internal fun TangemDropdownMenu(
|
||||
fun TangemDropdownMenu(
|
||||
expanded: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
offset: DpOffset = DpOffset(0.dp, 0.dp),
|
||||
offset: DpOffset = DpOffset(x = TangemTheme.dimens.spacing20, y = TangemTheme.dimens.spacing10.times(-1)),
|
||||
properties: PopupProperties = PopupProperties(focusable = true),
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
|
|
@ -142,10 +151,7 @@ private fun calculateTransformOrigin(parentBounds: IntRect, menuBounds: IntRect)
|
|||
menuBounds.width == 0 -> 0f
|
||||
else -> {
|
||||
val intersectionCenter =
|
||||
(
|
||||
kotlin.math.max(parentBounds.left, menuBounds.left) +
|
||||
kotlin.math.min(parentBounds.right, menuBounds.right)
|
||||
) / 2
|
||||
(max(parentBounds.left, menuBounds.left) + min(parentBounds.right, menuBounds.right)) / 2
|
||||
(intersectionCenter - menuBounds.left).toFloat() / menuBounds.width
|
||||
}
|
||||
}
|
||||
|
|
@ -155,10 +161,7 @@ private fun calculateTransformOrigin(parentBounds: IntRect, menuBounds: IntRect)
|
|||
menuBounds.height == 0 -> 0f
|
||||
else -> {
|
||||
val intersectionCenter =
|
||||
(
|
||||
kotlin.math.max(parentBounds.top, menuBounds.top) +
|
||||
kotlin.math.min(parentBounds.bottom, menuBounds.bottom)
|
||||
) / 2
|
||||
(max(parentBounds.top, menuBounds.top) + min(parentBounds.bottom, menuBounds.bottom)) / 2
|
||||
(intersectionCenter - menuBounds.top).toFloat() / menuBounds.height
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.core.ui.components.dropdownmenu
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
data class TangemDropdownMenuItem(
|
||||
val title: TextReference,
|
||||
val textColorProvider: @Composable () -> Color,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
15
core/ui/src/main/res/drawable/img_wallet_connect_76.xml
Normal file
15
core/ui/src/main/res/drawable/img_wallet_connect_76.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="76dp"
|
||||
android:height="76dp"
|
||||
android:viewportWidth="76"
|
||||
android:viewportHeight="76">
|
||||
<path
|
||||
android:fillColor="#3B99FC"
|
||||
android:pathData="M16,0L60,0A16,16 0,0 1,76 16L76,60A16,16 0,0 1,60 76L16,76A16,16 0,0 1,0 60L0,16A16,16 0,0 1,16 0z" />
|
||||
<group>
|
||||
<clip-path android:pathData="M11.084,22.167h53.833v31.667h-53.833z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M22.112,28.351C30.889,20.101 45.126,20.101 53.903,28.351L54.959,29.337C55.403,29.751 55.403,30.417 54.959,30.83L51.348,34.229C51.126,34.429 50.778,34.429 50.556,34.229L49.098,32.856C42.973,27.098 33.042,27.098 26.917,32.856L25.362,34.322C25.139,34.522 24.792,34.522 24.57,34.322L20.945,30.937C20.501,30.524 20.501,29.857 20.945,29.444L22.112,28.351ZM61.376,35.375L64.598,38.4C65.042,38.813 65.042,39.48 64.598,39.893L50.084,53.527C49.639,53.94 48.931,53.94 48.501,53.527L38.209,43.851C38.098,43.745 37.917,43.745 37.806,43.851L27.514,53.527C27.07,53.94 26.362,53.94 25.931,53.527L11.417,39.893C10.973,39.48 10.973,38.813 11.417,38.4L14.639,35.375C15.084,34.962 15.792,34.962 16.223,35.375L26.514,45.051C26.626,45.158 26.806,45.158 26.917,45.051L37.209,35.375C37.653,34.962 38.362,34.962 38.792,35.375L49.084,45.051C49.195,45.158 49.376,45.158 49.487,45.051L59.778,35.375C60.223,34.962 60.931,34.962 61.376,35.375Z" />
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
|
|
@ -27,7 +28,7 @@ internal object TokenDetailsPreviewData {
|
|||
onBackClick = {},
|
||||
tokenDetailsAppBarMenuConfig = TokenDetailsAppBarMenuConfig(
|
||||
persistentListOf(
|
||||
TokenDetailsAppBarMenuConfig.MenuItem(
|
||||
TangemDropdownMenuItem(
|
||||
title = TextReference.Res(id = R.string.token_details_hide_token),
|
||||
textColorProvider = { TangemTheme.colors.text.warning },
|
||||
onClick = { },
|
||||
|
|
|
|||
|
|
@ -1,14 +1,6 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class TokenDetailsAppBarMenuConfig(val items: ImmutableList<MenuItem>) {
|
||||
data class MenuItem(
|
||||
val title: TextReference,
|
||||
val textColorProvider: @Composable () -> Color,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
}
|
||||
internal data class TokenDetailsAppBarMenuConfig(val items: ImmutableList<TangemDropdownMenuItem>)
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -14,9 +15,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
|
@ -86,7 +87,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
private fun createMenu(cryptoCurrency: CryptoCurrency): TokenDetailsAppBarMenuConfig = TokenDetailsAppBarMenuConfig(
|
||||
items = buildList {
|
||||
addGenerateXPubMenuItem(cryptoCurrency)
|
||||
TokenDetailsAppBarMenuConfig.MenuItem(
|
||||
TangemDropdownMenuItem(
|
||||
title = TextReference.Res(id = R.string.token_details_hide_token),
|
||||
textColorProvider = { TangemTheme.colors.text.warning },
|
||||
onClick = clickIntents::onHideClick,
|
||||
|
|
@ -94,9 +95,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
}.toImmutableList(),
|
||||
)
|
||||
|
||||
private fun MutableList<TokenDetailsAppBarMenuConfig.MenuItem>.addGenerateXPubMenuItem(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
private fun MutableList<TangemDropdownMenuItem>.addGenerateXPubMenuItem(cryptoCurrency: CryptoCurrency) {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return
|
||||
|
||||
val isBitcoin = isBitcoin(cryptoCurrency.network.id.value)
|
||||
|
|
@ -107,7 +106,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
|
||||
if (isBitcoin && hasDerivations) {
|
||||
add(
|
||||
TokenDetailsAppBarMenuConfig.MenuItem(
|
||||
TangemDropdownMenuItem(
|
||||
title = resourceReference(R.string.token_details_generate_xpub),
|
||||
textColorProvider = { TangemTheme.colors.text.primary1 },
|
||||
onClick = clickIntents::onGenerateExtendedKey,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
|||
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -376,13 +377,13 @@ internal class TokenDetailsStateFactory(
|
|||
return copy(
|
||||
items = buildList {
|
||||
if (isSupported && hasDerivations) {
|
||||
TokenDetailsAppBarMenuConfig.MenuItem(
|
||||
TangemDropdownMenuItem(
|
||||
title = resourceReference(R.string.token_details_generate_xpub),
|
||||
textColorProvider = { TangemTheme.colors.text.primary1 },
|
||||
onClick = clickIntents::onGenerateExtendedKey,
|
||||
).let(::add)
|
||||
}
|
||||
TokenDetailsAppBarMenuConfig.MenuItem(
|
||||
TangemDropdownMenuItem(
|
||||
title = TextReference.Res(id = R.string.token_details_hide_token),
|
||||
textColorProvider = { TangemTheme.colors.text.warning },
|
||||
onClick = clickIntents::onHideClick,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -14,14 +12,12 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarConfig
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
|
|
@ -58,10 +54,9 @@ internal fun TokenDetailsTopAppBar(config: TokenDetailsTopAppBarConfig) {
|
|||
expanded = showDropdownMenu,
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
onDismissRequest = { showDropdownMenu = false },
|
||||
offset = DpOffset(x = TangemTheme.dimens.spacing20, y = TangemTheme.dimens.spacing10.times(-1)),
|
||||
content = {
|
||||
config.tokenDetailsAppBarMenuConfig?.items?.fastForEach {
|
||||
AppBarDropdownItem(
|
||||
TangemDropdownItem(
|
||||
item = it,
|
||||
dismissParent = { showDropdownMenu = false },
|
||||
)
|
||||
|
|
@ -78,42 +73,6 @@ internal fun TokenDetailsTopAppBar(config: TokenDetailsTopAppBarConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("ComposableEventParameterNaming")
|
||||
@Composable
|
||||
private fun AppBarDropdownItem(
|
||||
item: TokenDetailsAppBarMenuConfig.MenuItem,
|
||||
dismissParent: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.clickable {
|
||||
dismissParent()
|
||||
item.onClick()
|
||||
}
|
||||
.padding(vertical = TangemTheme.dimens.spacing8, horizontal = TangemTheme.dimens.spacing16),
|
||||
text = item.title.resolveReference(),
|
||||
style = TangemTheme.typography.body1.copy(color = item.textColorProvider()),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_TokenDetailsAppBarDropdownItem() {
|
||||
TangemThemePreview {
|
||||
AppBarDropdownItem(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
dismissParent = {},
|
||||
item = TokenDetailsAppBarMenuConfig.MenuItem(
|
||||
title = TextReference.Res(id = R.string.token_details_hide_token),
|
||||
textColorProvider = { TangemTheme.colors.text.warning },
|
||||
onClick = { },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
|
|||
1
features/walletconnect/api/.gitignore
vendored
Normal file
1
features/walletconnect/api/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
18
features/walletconnect/api/build.gradle.kts
Normal file
18
features/walletconnect/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.walletconnect.api"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
/** Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** Domain models */
|
||||
implementation(projects.domain.wallets.models)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.features.walletconnect.components
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
||||
interface WalletConnectEntryComponent : ComposableContentComponent {
|
||||
interface Factory : ComponentFactory<Unit, WalletConnectEntryComponent>
|
||||
}
|
||||
1
features/walletconnect/impl/.gitignore
vendored
Normal file
1
features/walletconnect/impl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
45
features/walletconnect/impl/build.gradle.kts
Normal file
45
features/walletconnect/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.features.walletconnect.impl"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.features.walletconnect.api)
|
||||
|
||||
/** Core */
|
||||
implementation(projects.core.configToggles)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** Domain models */
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.activity.compose)
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.accompanist.systemUiController)
|
||||
implementation(deps.compose.coil)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.ui)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
|
||||
/** Other */
|
||||
implementation(deps.decompose.ext.compose)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.timber)
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.features.walletconnect.connections.components
|
||||
|
||||
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.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.walletconnect.connections.model.WcConnectionsModel
|
||||
import com.tangem.features.walletconnect.connections.ui.WcConnectionsContent
|
||||
|
||||
internal class ConnectionsComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
) : AppComponentContext by appComponentContext, ComposableContentComponent {
|
||||
|
||||
private val model: WcConnectionsModel = getOrCreateModel()
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
WcConnectionsContent(modifier = modifier, state = state)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tangem.features.walletconnect.connections.components
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
||||
import com.tangem.features.walletconnect.connections.routes.ConnectionsInnerRoute
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
internal class DefaultWalletConnectEntryComponent @AssistedInject constructor(
|
||||
@Assisted private val appComponentContext: AppComponentContext,
|
||||
@Assisted params: Unit,
|
||||
) : AppComponentContext by appComponentContext, WalletConnectEntryComponent {
|
||||
|
||||
private val contentNavigation = StackNavigation<ConnectionsInnerRoute>()
|
||||
private val contentStack = childStack(
|
||||
source = contentNavigation,
|
||||
serializer = ConnectionsInnerRoute.serializer(),
|
||||
initialConfiguration = ConnectionsInnerRoute.Connections,
|
||||
childFactory = ::screenChild,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val childStack by contentStack.subscribeAsState()
|
||||
|
||||
BackHandler(onBack = router::pop)
|
||||
Children(stack = childStack, animation = stackAnimation()) { child ->
|
||||
child.instance.Content(modifier = modifier)
|
||||
}
|
||||
}
|
||||
|
||||
private fun screenChild(
|
||||
config: ConnectionsInnerRoute,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableContentComponent = when (config) {
|
||||
ConnectionsInnerRoute.Connections -> ConnectionsComponent(childByContext(componentContext))
|
||||
ConnectionsInnerRoute.QrScan -> TODO("[REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : WalletConnectEntryComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: Unit): DefaultWalletConnectEntryComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.features.walletconnect.connections.entity
|
||||
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class WcConnectionsState(
|
||||
val topAppBarConfig: WcConnectionsTopAppBarConfig,
|
||||
val connections: ImmutableList<WcConnectionsUM>,
|
||||
val onNewConnectionClick: () -> Unit,
|
||||
)
|
||||
|
||||
internal data class WcConnectionsTopAppBarConfig(
|
||||
val startButtonUM: TopAppBarButtonUM,
|
||||
val endActions: ImmutableList<TangemDropdownMenuItem>,
|
||||
)
|
||||
|
||||
internal data class WcConnectionsUM(
|
||||
val userWalletId: String,
|
||||
val walletName: String,
|
||||
val connectedApps: ImmutableList<WcConnectedAppInfo>,
|
||||
)
|
||||
|
||||
internal data class WcConnectedAppInfo(
|
||||
val name: String,
|
||||
val iconUrl: String,
|
||||
val subtitle: TextReference = stringReference("Connected App"),
|
||||
val isVerified: Boolean = false,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.features.walletconnect.connections.model
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsState
|
||||
import com.tangem.features.walletconnect.connections.ui.preview.WcConnectionsPreviewData
|
||||
import com.tangem.features.walletconnect.impl.R
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class WcConnectionsModel @Inject constructor(
|
||||
private val router: Router,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
val uiState: StateFlow<WcConnectionsState> = MutableStateFlow(
|
||||
WcConnectionsPreviewData.stateForModel(
|
||||
onBackClicked = router::pop,
|
||||
openQrScanScreen = ::openQrScanScreen,
|
||||
disconnectAll = ::disconnectAll,
|
||||
),
|
||||
).asStateFlow()
|
||||
|
||||
private fun openQrScanScreen() {
|
||||
// TODO("[REDACTED_JIRA]")
|
||||
}
|
||||
|
||||
private fun disconnectAll() {
|
||||
val message = DialogMessage(
|
||||
title = resourceReference(R.string.wc_disconnect_all_alert_title),
|
||||
message = resourceReference(R.string.wc_disconnect_all_alert_desc),
|
||||
firstActionBuilder = {
|
||||
EventMessageAction(
|
||||
title = resourceReference(R.string.common_disconnect),
|
||||
onClick = {
|
||||
// TODO(wc): disconnect all
|
||||
},
|
||||
)
|
||||
},
|
||||
secondActionBuilder = { cancelAction() },
|
||||
)
|
||||
uiMessageSender.send(message)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.walletconnect.connections.routes
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed class ConnectionsInnerRoute {
|
||||
@Serializable
|
||||
data object Connections : ConnectionsInnerRoute()
|
||||
|
||||
@Serializable
|
||||
data object QrScan : ConnectionsInnerRoute()
|
||||
}
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
package com.tangem.features.walletconnect.connections.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import coil.compose.AsyncImage
|
||||
import com.tangem.core.ui.components.BottomFade
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfo
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsState
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsTopAppBarConfig
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsUM
|
||||
import com.tangem.features.walletconnect.connections.ui.preview.WcConnectionsPreviewData
|
||||
import com.tangem.features.walletconnect.impl.R
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
internal fun WcConnectionsContent(state: WcConnectionsState, modifier: Modifier = Modifier) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
topBar = {
|
||||
ConnectionsTopBar(
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
config = state.topAppBarConfig,
|
||||
showEndButton = state.connections.isNotEmpty(),
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
if (state.connections.isNotEmpty()) {
|
||||
NewConnectionButton(
|
||||
onClick = state.onNewConnectionClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
floatingActionButtonPosition = FabPosition.Center,
|
||||
content = { innerPadding ->
|
||||
Content(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize(),
|
||||
connections = state.connections,
|
||||
onNewConnectionClick = state.onNewConnectionClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(
|
||||
connections: ImmutableList<WcConnectionsUM>,
|
||||
onNewConnectionClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (connections.isEmpty()) {
|
||||
EmptyConnectionsBlock(modifier = modifier, onNewConnectionClick = onNewConnectionClick)
|
||||
} else {
|
||||
ConnectionsBlock(modifier = modifier, connections = connections)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmptyConnectionsBlock(onNewConnectionClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.img_wallet_connect_76),
|
||||
contentDescription = "Wallet Connect",
|
||||
modifier = Modifier.size(76.dp),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing24),
|
||||
text = stringResourceSafe(R.string.wc_no_sessions_title),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.h3,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
text = stringResourceSafe(R.string.wc_no_sessions_desc),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
NewConnectionButton(
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing48),
|
||||
onClick = onNewConnectionClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConnectionsBlock(connections: ImmutableList<WcConnectionsUM>, modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing76 + bottomBarHeight,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
items(
|
||||
items = connections,
|
||||
key = { it.userWalletId },
|
||||
itemContent = {
|
||||
ConnectionItem(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius14),
|
||||
)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14)),
|
||||
connection = it,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
BottomFade(modifier = Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConnectionItem(connection: WcConnectionsUM, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
key("${connection.userWalletId}_${connection.walletName}") {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp, start = 12.dp, end = 12.dp),
|
||||
text = connection.walletName,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
}
|
||||
connection.connectedApps.fastForEach { appInfo ->
|
||||
key("${connection.userWalletId}_${connection.walletName}_${appInfo.name}_${appInfo.iconUrl}") {
|
||||
AppInfoItem(
|
||||
appInfo = appInfo,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = appInfo.onClick)
|
||||
.padding(all = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppInfoItem(appInfo: WcConnectedAppInfo, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius8)),
|
||||
model = appInfo.iconUrl,
|
||||
contentDescription = appInfo.name,
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
) {
|
||||
Text(
|
||||
text = appInfo.name,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
Text(
|
||||
text = appInfo.subtitle.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ConnectionsTopBar(
|
||||
config: WcConnectionsTopAppBarConfig,
|
||||
showEndButton: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showDropdownMenu by rememberSaveable { mutableStateOf(false) }
|
||||
// TangemTopAppBar is not friendly with dropdown menu
|
||||
TopAppBar(
|
||||
modifier = modifier,
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
titleContentColor = TangemTheme.colors.icon.primary1,
|
||||
actionIconContentColor = TangemTheme.colors.icon.primary1,
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = config.startButtonUM.onIconClicked) {
|
||||
Icon(
|
||||
painter = painterResource(id = config.startButtonUM.iconRes),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = "Back",
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.wc_connections),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
AnimatedVisibility(showEndButton) {
|
||||
IconButton(onClick = { showDropdownMenu = true }) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_more_vertical_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = "More",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
TangemDropdownMenu(
|
||||
expanded = showDropdownMenu,
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
onDismissRequest = { showDropdownMenu = false },
|
||||
content = {
|
||||
config.endActions.fastForEach {
|
||||
TangemDropdownItem(
|
||||
item = it,
|
||||
dismissParent = { showDropdownMenu = false },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NewConnectionButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
PrimaryButton(
|
||||
modifier = modifier,
|
||||
text = stringResourceSafe(R.string.wc_new_connection),
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun WcConnectionsContentPreview(
|
||||
@PreviewParameter(WcConnectionsStateProvider::class) params: WcConnectionsState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
WcConnectionsContent(state = params)
|
||||
}
|
||||
}
|
||||
|
||||
private class WcConnectionsStateProvider : CollectionPreviewParameterProvider<WcConnectionsState>(
|
||||
listOf(
|
||||
WcConnectionsPreviewData.stateWithEmptyConnections,
|
||||
WcConnectionsPreviewData.fullState,
|
||||
),
|
||||
)
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package com.tangem.features.walletconnect.connections.ui.preview
|
||||
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectedAppInfo
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsState
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsTopAppBarConfig
|
||||
import com.tangem.features.walletconnect.connections.entity.WcConnectionsUM
|
||||
import com.tangem.features.walletconnect.impl.R
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import timber.log.Timber
|
||||
|
||||
internal object WcConnectionsPreviewData {
|
||||
private const val BASE_URL = "https://raw.githubusercontent.com/TaranVH/LOGOS/refs/heads/master/Crypto"
|
||||
private val connections = persistentListOf(
|
||||
WcConnectionsUM(
|
||||
userWalletId = "wallet_id_1",
|
||||
walletName = "Wallet 2.0",
|
||||
connectedApps = persistentListOf(
|
||||
WcConnectedAppInfo(
|
||||
name = "Moralis",
|
||||
iconUrl = "$BASE_URL/satoshi.png",
|
||||
isVerified = true,
|
||||
onClick = { Timber.d("Moralis clicked") },
|
||||
),
|
||||
WcConnectedAppInfo(
|
||||
name = "website.com",
|
||||
iconUrl = "$BASE_URL/doge_again_lol.jpg",
|
||||
onClick = { Timber.d("website.com clicked") },
|
||||
),
|
||||
),
|
||||
),
|
||||
WcConnectionsUM(
|
||||
userWalletId = "wallet_id_2",
|
||||
walletName = "Bitcoin",
|
||||
connectedApps = persistentListOf(
|
||||
WcConnectedAppInfo(
|
||||
name = "React app",
|
||||
iconUrl = "$BASE_URL/LiteCoin.png",
|
||||
isVerified = true,
|
||||
onClick = { Timber.d("React app clicked") },
|
||||
),
|
||||
),
|
||||
),
|
||||
WcConnectionsUM(
|
||||
userWalletId = "wallet_id_3",
|
||||
walletName = "Wallet 2.0",
|
||||
connectedApps = persistentListOf(
|
||||
WcConnectedAppInfo(
|
||||
name = "Moralis",
|
||||
iconUrl = "$BASE_URL/satoshi.png",
|
||||
isVerified = true,
|
||||
onClick = { Timber.d("Moralis clicked") },
|
||||
),
|
||||
WcConnectedAppInfo(
|
||||
name = "website.com",
|
||||
iconUrl = "$BASE_URL/doge_again_lol.jpg",
|
||||
onClick = { Timber.d("website.com clicked") },
|
||||
),
|
||||
),
|
||||
),
|
||||
WcConnectionsUM(
|
||||
userWalletId = "wallet_id_4",
|
||||
walletName = "Bitcoin",
|
||||
connectedApps = persistentListOf(
|
||||
WcConnectedAppInfo(
|
||||
name = "React app",
|
||||
iconUrl = "$BASE_URL/LiteCoin.png",
|
||||
isVerified = true,
|
||||
onClick = { Timber.d("React app clicked") },
|
||||
),
|
||||
),
|
||||
),
|
||||
WcConnectionsUM(
|
||||
userWalletId = "wallet_id_5",
|
||||
walletName = "Wallet 2.0",
|
||||
connectedApps = persistentListOf(
|
||||
WcConnectedAppInfo(
|
||||
name = "Moralis",
|
||||
iconUrl = "$BASE_URL/satoshi.png",
|
||||
isVerified = true,
|
||||
onClick = { Timber.d("Moralis clicked") },
|
||||
),
|
||||
WcConnectedAppInfo(
|
||||
name = "website.com",
|
||||
iconUrl = "$BASE_URL/doge_again_lol.jpg",
|
||||
onClick = { Timber.d("website.com clicked") },
|
||||
),
|
||||
),
|
||||
),
|
||||
WcConnectionsUM(
|
||||
userWalletId = "wallet_id_6",
|
||||
walletName = "Bitcoin",
|
||||
connectedApps = persistentListOf(
|
||||
WcConnectedAppInfo(
|
||||
name = "React app",
|
||||
iconUrl = "$BASE_URL/LiteCoin.png",
|
||||
isVerified = true,
|
||||
onClick = { Timber.d("React app clicked") },
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val stateWithEmptyConnections = WcConnectionsState(
|
||||
topAppBarConfig = WcConnectionsTopAppBarConfig(
|
||||
startButtonUM = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onIconClicked = {},
|
||||
enabled = true,
|
||||
),
|
||||
endActions = persistentListOf(
|
||||
TangemDropdownMenuItem(
|
||||
title = resourceReference(R.string.wc_disconnect_all),
|
||||
textColorProvider = { TangemTheme.colors.text.warning },
|
||||
onClick = { },
|
||||
),
|
||||
),
|
||||
),
|
||||
connections = persistentListOf(),
|
||||
onNewConnectionClick = {},
|
||||
)
|
||||
val fullState = stateWithEmptyConnections.copy(connections = connections)
|
||||
fun stateForModel(
|
||||
onBackClicked: () -> Unit,
|
||||
openQrScanScreen: () -> Unit,
|
||||
disconnectAll: () -> Unit,
|
||||
): WcConnectionsState {
|
||||
return fullState.copy(
|
||||
topAppBarConfig = fullState.topAppBarConfig.copy(
|
||||
startButtonUM = fullState.topAppBarConfig.startButtonUM.copy(onIconClicked = onBackClicked),
|
||||
endActions = fullState.topAppBarConfig.endActions
|
||||
.map { it.copy(onClick = disconnectAll) }
|
||||
.toImmutableList(),
|
||||
),
|
||||
onNewConnectionClick = openQrScanScreen,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.walletconnect.di
|
||||
|
||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
||||
import com.tangem.features.walletconnect.connections.components.DefaultWalletConnectEntryComponent
|
||||
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 WalletConnectFeatureModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindWalletConnectEntryComponentFactory(
|
||||
factory: DefaultWalletConnectEntryComponent.Factory,
|
||||
): WalletConnectEntryComponent.Factory
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.walletconnect.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.walletconnect.connections.model.WcConnectionsModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface WalletConnectModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(WcConnectionsModel::class)
|
||||
fun bindWcConnectionsModel(model: WcConnectionsModel): Model
|
||||
}
|
||||
|
|
@ -235,6 +235,9 @@ include(":features:biometry:impl")
|
|||
|
||||
include(":features:nft:api")
|
||||
include(":features:nft:impl")
|
||||
|
||||
include(":features:walletconnect:api")
|
||||
include(":features:walletconnect:impl")
|
||||
// endregion Feature modules
|
||||
|
||||
// region Domain modules
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue