Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-29 18:01:48 +04:00
parent 7ab5f7b547
commit 8889ad4c31
31 changed files with 762 additions and 29 deletions

View file

@ -35,7 +35,7 @@ internal sealed class SettingsItem(
data class WalletConnect(
override val onClick: () -> Unit,
) : SettingsItem(
iconResId = R.drawable.ic_walletconnect,
iconResId = R.drawable.ic_wallet_connect_24,
title = resourceReference(R.string.wallet_connect_title),
subtitle = resourceReference(R.string.wallet_connect_subtitle),
isLarge = true,
@ -81,14 +81,14 @@ internal sealed class SettingsItem(
data class Chat(
override val onClick: () -> Unit,
) : SettingsItem(
iconResId = R.drawable.ic_chat,
iconResId = R.drawable.ic_chat_24,
title = resourceReference(R.string.details_chat),
)
data class SendFeedback(
override val onClick: () -> Unit,
) : SettingsItem(
iconResId = R.drawable.ic_comment,
iconResId = R.drawable.ic_comment_24,
title = resourceReference(R.string.details_row_title_contact_to_support),
)
@ -102,7 +102,7 @@ internal sealed class SettingsItem(
data class TermsOfService(
override val onClick: () -> Unit,
) : SettingsItem(
iconResId = R.drawable.ic_text,
iconResId = R.drawable.ic_text_24,
title = resourceReference(R.string.disclaimer_title),
)
@ -125,15 +125,15 @@ internal sealed class EventError {
}
sealed class SocialNetwork(val id: String, val iconRes: Int) {
object Twitter : SocialNetwork("Twitter", R.drawable.ic_twitter)
object Telegram : SocialNetwork("Telegram", R.drawable.ic_telegram)
object Discord : SocialNetwork("Discord", R.drawable.ic_discord)
object Reddit : SocialNetwork("Reddit", R.drawable.ic_reddit)
object Instagram : SocialNetwork("Instagram", R.drawable.ic_instagram)
object GitHub : SocialNetwork("GitHub", R.drawable.ic_github)
object Facebook : SocialNetwork("Facebook", R.drawable.ic_facebook)
object LinkedIn : SocialNetwork("LinkedIn", R.drawable.ic_linkedin)
object YouTube : SocialNetwork("YouTube", R.drawable.ic_youtube)
object Twitter : SocialNetwork("Twitter", R.drawable.ic_twitter_24)
object Telegram : SocialNetwork("Telegram", R.drawable.ic_telegram_24)
object Discord : SocialNetwork("Discord", R.drawable.ic_discord_24)
object Reddit : SocialNetwork("Reddit", R.drawable.ic_reddit_24)
object Instagram : SocialNetwork("Instagram", R.drawable.ic_instagram_24)
object GitHub : SocialNetwork("GitHub", R.drawable.ic_github_24)
object Facebook : SocialNetwork("Facebook", R.drawable.ic_facebook_24)
object LinkedIn : SocialNetwork("LinkedIn", R.drawable.ic_linkedin_24)
object YouTube : SocialNetwork("YouTube", R.drawable.ic_youtube_24)
}
internal object TangemSocialAccounts {

View file

@ -88,7 +88,7 @@ private fun EmptyScreen(state: WalletConnectScreenState) {
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.ic_walletconnect),
painter = painterResource(id = R.drawable.ic_wallet_connect_24),
contentDescription = "",
colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive),
contentScale = ContentScale.FillWidth,

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M19.636,4.924C18.212,4.259 16.689,3.775 15.097,3.5C14.902,3.853 14.673,4.327 14.516,4.705C12.824,4.451 11.147,4.451 9.486,4.705C9.328,4.327 9.095,3.853 8.897,3.5C7.304,3.775 5.779,4.26 4.355,4.927C1.483,9.26 0.704,13.486 1.093,17.651C2.999,19.071 4.845,19.934 6.66,20.498C7.108,19.882 7.508,19.228 7.852,18.538C7.196,18.289 6.568,17.983 5.975,17.626C6.132,17.51 6.286,17.388 6.435,17.263C10.055,18.953 13.988,18.953 17.565,17.263C17.715,17.388 17.869,17.51 18.025,17.626C17.43,17.984 16.8,18.291 16.144,18.54C16.489,19.228 16.886,19.884 17.336,20.5C19.153,19.935 21.001,19.073 22.906,17.651C23.363,12.822 22.126,8.636 19.636,4.924ZM8.345,15.089C7.259,15.089 6.368,14.076 6.368,12.843C6.368,11.61 7.24,10.596 8.345,10.596C9.451,10.596 10.342,11.608 10.323,12.843C10.325,14.076 9.451,15.089 8.345,15.089ZM15.655,15.089C14.568,15.089 13.677,14.076 13.677,12.843C13.677,11.61 14.549,10.596 15.655,10.596C16.76,10.596 17.651,11.608 17.632,12.843C17.632,14.076 16.76,15.089 15.655,15.089Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M12,1.935C6.442,1.935 1.935,6.465 1.935,12.052C1.935,17.124 5.653,21.312 10.495,22.043V14.733H8.005V12.074H10.495V10.304C10.495,7.375 11.915,6.089 14.337,6.089C15.497,6.089 16.111,6.176 16.402,6.215V8.536H14.749C13.721,8.536 13.362,9.516 13.362,10.62V12.074H16.375L15.967,14.733H13.362V22.065C18.274,21.395 22.065,17.173 22.065,12.052C22.065,6.465 17.558,1.935 12,1.935Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M12,2.4C6.698,2.4 2.4,6.698 2.4,12C2.4,16.498 5.497,20.262 9.674,21.304C9.629,21.174 9.6,21.024 9.6,20.838V19.197C9.21,19.197 8.557,19.197 8.394,19.197C7.737,19.197 7.153,18.914 6.87,18.39C6.555,17.806 6.501,16.914 5.721,16.369C5.49,16.187 5.666,15.98 5.933,16.008C6.425,16.147 6.833,16.485 7.217,16.986C7.599,17.487 7.779,17.601 8.493,17.601C8.84,17.601 9.358,17.581 9.846,17.504C10.109,16.838 10.562,16.224 11.117,15.934C7.92,15.606 6.394,14.015 6.394,11.856C6.394,10.926 6.79,10.027 7.463,9.27C7.242,8.518 6.965,6.984 7.548,6.4C8.986,6.4 9.856,7.333 10.065,7.585C10.781,7.339 11.569,7.2 12.396,7.2C13.225,7.2 14.015,7.339 14.733,7.586C14.94,7.336 15.81,6.4 17.252,6.4C17.837,6.985 17.557,8.525 17.333,9.275C18.002,10.031 18.396,10.928 18.396,11.856C18.396,14.014 16.873,15.603 13.681,15.934C14.559,16.392 15.2,17.68 15.2,18.65V20.838C15.2,20.921 15.182,20.981 15.172,21.052C18.913,19.741 21.6,16.189 21.6,12C21.6,6.698 17.302,2.4 12,2.4Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -8,12 +8,12 @@
android:pathData="M2.4,2.4h19.2v19.2h-19.2z"/>
<path
android:pathData="M21.581,8.045C21.536,7.025 21.371,6.323 21.135,5.716C20.891,5.07 20.516,4.493 20.025,4.013C19.545,3.525 18.963,3.146 18.326,2.906C17.714,2.67 17.017,2.505 15.997,2.46C14.969,2.411 14.642,2.4 12.036,2.4C9.429,2.4 9.103,2.411 8.079,2.456C7.058,2.501 6.357,2.666 5.749,2.903C5.104,3.146 4.527,3.521 4.047,4.013C3.559,4.493 3.18,5.074 2.94,5.712C2.704,6.323 2.539,7.021 2.494,8.041C2.445,9.069 2.434,9.395 2.434,12.002C2.434,14.609 2.445,14.935 2.49,15.959C2.535,16.979 2.7,17.68 2.936,18.288C3.18,18.933 3.559,19.511 4.047,19.991C4.527,20.479 5.108,20.857 5.746,21.097C6.357,21.334 7.055,21.499 8.075,21.544C9.099,21.589 9.425,21.6 12.032,21.6C14.639,21.6 14.965,21.589 15.989,21.544C17.009,21.499 17.711,21.334 18.318,21.097C19.608,20.598 20.629,19.578 21.128,18.288C21.364,17.677 21.529,16.979 21.574,15.959C21.619,14.935 21.63,14.609 21.63,12.002C21.63,9.395 21.626,9.069 21.581,8.045ZM19.852,15.884C19.811,16.822 19.653,17.328 19.522,17.666C19.199,18.502 18.536,19.166 17.699,19.488C17.362,19.62 16.852,19.777 15.918,19.818C14.905,19.864 14.601,19.875 12.039,19.875C9.478,19.875 9.17,19.864 8.161,19.818C7.223,19.777 6.717,19.62 6.38,19.488C5.963,19.334 5.584,19.091 5.277,18.772C4.958,18.461 4.714,18.086 4.56,17.669C4.429,17.332 4.272,16.822 4.23,15.888C4.185,14.875 4.174,14.571 4.174,12.009C4.174,9.448 4.185,9.14 4.23,8.131C4.272,7.193 4.429,6.687 4.56,6.35C4.714,5.933 4.958,5.554 5.281,5.247C5.592,4.928 5.967,4.684 6.383,4.53C6.721,4.399 7.231,4.242 8.165,4.2C9.178,4.155 9.481,4.144 12.043,4.144C14.609,4.144 14.912,4.155 15.922,4.2C16.859,4.242 17.365,4.399 17.703,4.53C18.119,4.684 18.498,4.928 18.806,5.247C19.125,5.558 19.368,5.933 19.522,6.35C19.653,6.687 19.811,7.197 19.852,8.131C19.897,9.144 19.909,9.448 19.909,12.009C19.909,14.571 19.897,14.871 19.852,15.884Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
<path
android:pathData="M12.036,7.07C9.313,7.07 7.103,9.279 7.103,12.002C7.103,14.725 9.313,16.934 12.036,16.934C14.759,16.934 16.968,14.725 16.968,12.002C16.968,9.279 14.759,7.07 12.036,7.07ZM12.036,15.201C10.269,15.201 8.836,13.768 8.836,12.002C8.836,10.235 10.269,8.802 12.036,8.802C13.802,8.802 15.235,10.235 15.235,12.002C15.235,13.768 13.802,15.201 12.036,15.201Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
<path
android:pathData="M18.315,6.875C18.315,7.511 17.799,8.026 17.163,8.026C16.527,8.026 16.011,7.511 16.011,6.875C16.011,6.239 16.527,5.723 17.163,5.723C17.799,5.723 18.315,6.239 18.315,6.875Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</group>
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M19.2,3.2H4.8C3.916,3.2 3.2,3.916 3.2,4.8V19.2C3.2,20.084 3.916,20.8 4.8,20.8H19.2C20.084,20.8 20.8,20.084 20.8,19.2V4.8C20.8,3.916 20.084,3.2 19.2,3.2ZM8.763,17.6H6.403V10.006H8.763V17.6ZM7.559,8.921C6.798,8.921 6.183,8.304 6.183,7.545C6.183,6.785 6.799,6.169 7.559,6.169C8.318,6.169 8.934,6.786 8.934,7.545C8.934,8.304 8.318,8.921 7.559,8.921ZM17.603,17.6H15.245V13.907C15.245,13.026 15.229,11.894 14.018,11.894C12.79,11.894 12.602,12.853 12.602,13.843V17.6H10.243V10.006H12.507V11.044H12.539C12.854,10.447 13.624,9.817 14.772,9.817C17.162,9.817 17.603,11.39 17.603,13.435V17.6Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -5,6 +5,6 @@
android:viewportHeight="24">
<path
android:pathData="M22,12C22,17.523 17.523,22 12,22C6.477,22 2,17.523 2,12C2,6.477 6.477,2 12,2C17.523,2 22,6.477 22,12ZM17.161,10.594C17.969,10.566 18.645,11.195 18.674,12C18.684,12.562 18.369,13.079 17.867,13.33C17.876,13.477 17.876,13.624 17.867,13.77C17.867,16.012 15.257,17.832 12.038,17.832C8.819,17.832 6.209,16.01 6.209,13.77C6.199,13.624 6.199,13.477 6.209,13.33C6.062,13.266 5.931,13.174 5.816,13.067C5.227,12.515 5.2,11.59 5.751,11.004C6.303,10.415 7.228,10.387 7.814,10.939C8.968,10.159 10.323,9.731 11.715,9.709L12.453,6.239V6.234C12.49,6.065 12.657,5.96 12.823,5.998L15.274,6.487C15.431,6.214 15.714,6.03 16.03,5.998C16.579,5.938 17.071,6.336 17.131,6.885C17.191,7.434 16.793,7.926 16.243,7.986C15.694,8.046 15.202,7.648 15.142,7.099L13.002,6.649L12.353,9.768C13.728,9.798 15.065,10.223 16.204,10.999C16.462,10.753 16.803,10.606 17.161,10.594ZM9.676,12C9.125,12 8.675,12.45 8.675,13.002C8.675,13.554 9.122,13.999 9.676,14.004C10.228,14.004 10.678,13.554 10.678,13.002C10.678,12.45 10.228,12 9.676,12ZM14.484,15.751V15.791C14.593,15.684 14.595,15.505 14.488,15.396C14.381,15.287 14.203,15.284 14.093,15.391C13.484,15.826 12.748,16.04 12.003,16C11.257,16.035 10.524,15.811 9.922,15.371C9.82,15.289 9.679,15.289 9.579,15.371C9.465,15.465 9.447,15.637 9.542,15.751C10.253,16.286 11.123,16.559 12.013,16.522C12.903,16.559 13.773,16.286 14.484,15.751ZM14.297,14.076H14.347C14.899,14.054 15.329,13.589 15.307,13.037C15.307,12.485 14.856,12.035 14.305,12.035C13.753,12.035 13.303,12.485 13.303,13.037C13.303,13.589 13.753,14.039 14.305,14.039L14.297,14.076Z"
android:fillColor="#909090"
android:fillColor="#000"
android:fillType="evenOdd"/>
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M20.239,19.288C20.597,18.212 22.282,7.49 22.489,5.377C22.552,4.736 22.344,4.312 21.94,4.122C21.449,3.892 20.721,4.006 19.878,4.304C18.721,4.711 3.932,10.838 3.077,11.193C2.268,11.53 1.5,11.895 1.5,12.427C1.5,12.8 1.728,13.01 2.353,13.228C3.003,13.454 4.642,13.939 5.61,14.199C6.542,14.45 7.602,14.232 8.196,13.871C8.826,13.489 16.101,8.74 16.622,8.324C17.144,7.908 17.56,8.44 17.134,8.858C16.707,9.274 11.715,14.002 11.056,14.657C10.256,15.453 10.823,16.276 11.36,16.606C11.973,16.983 16.376,19.865 17.041,20.328C17.705,20.79 18.376,21 18.993,21C19.609,20.999 19.932,20.206 20.239,19.288Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M13.712,10.622L20.413,3H18.825L13.007,9.618L8.36,3H3L10.027,13.007L3,21H4.588L10.732,14.011L15.64,21H21L13.712,10.622H13.712ZM11.538,13.096L10.825,12.099L5.16,4.17H7.599L12.171,10.569L12.883,11.566L18.826,19.883H16.387L11.538,13.096V13.096Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -2,6 +2,6 @@
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="@color/accent"
android:fillColor="#000"
android:pathData="M4.915,7.735C8.827,4.088 15.173,4.088 19.085,7.735L19.555,8.171C19.753,8.353 19.753,8.648 19.555,8.831L17.946,10.333C17.847,10.421 17.692,10.421 17.593,10.333L16.943,9.726C14.213,7.181 9.787,7.181 7.057,9.726L6.364,10.374C6.265,10.462 6.11,10.462 6.011,10.374L4.395,8.878C4.197,8.695 4.197,8.401 4.395,8.218L4.915,7.735ZM22.415,10.84L23.851,12.177C24.049,12.359 24.049,12.654 23.851,12.837L17.382,18.863C17.184,19.046 16.869,19.046 16.677,18.863L12.09,14.586C12.04,14.539 11.96,14.539 11.91,14.586L7.323,18.863C7.125,19.046 6.809,19.046 6.617,18.863L0.149,12.837C-0.05,12.654 -0.05,12.359 0.149,12.177L1.585,10.84C1.783,10.657 2.099,10.657 2.29,10.84L6.877,15.116C6.927,15.163 7.007,15.163 7.057,15.116L11.644,10.84C11.842,10.657 12.158,10.657 12.35,10.84L16.937,15.116C16.986,15.163 17.067,15.163 17.116,15.116L21.703,10.84C21.902,10.657 22.217,10.657 22.415,10.84Z" />
</vector>

View file

@ -5,5 +5,5 @@
android:viewportHeight="24">
<path
android:pathData="M21.985,6.96C21.79,5.904 20.863,5.136 19.79,4.896C18.18,4.56 15.205,4.32 11.986,4.32C8.767,4.32 5.743,4.56 4.133,4.896C3.06,5.136 2.134,5.856 1.939,6.96C1.743,8.16 1.548,9.84 1.548,12C1.548,14.16 1.743,15.84 1.987,17.04C2.182,18.096 3.109,18.864 4.182,19.104C5.889,19.44 8.816,19.68 12.035,19.68C15.254,19.68 18.18,19.44 19.888,19.104C20.961,18.864 21.887,18.144 22.082,17.04C22.277,15.84 22.521,14.112 22.57,12C22.472,9.84 22.229,8.16 21.985,6.96ZM9.352,15.36V8.64L15.303,12L9.352,15.36Z"
android:fillColor="#909090"/>
android:fillColor="#000" />
</vector>

View file

@ -26,6 +26,7 @@ dependencies {
/* AndroidX */
implementation(deps.androidx.fragment.ktx)
implementation(deps.androidx.activity.compose)
implementation(deps.lifecycle.compose)
/* Compose */

View file

@ -3,12 +3,36 @@ package com.tangem.features.details.component.preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.features.details.component.DetailsComponent
import com.tangem.features.details.entity.DetailsFooterUM
import com.tangem.features.details.entity.DetailsUM
import com.tangem.features.details.ui.DetailsScreen
import com.tangem.features.details.utils.ItemsBuilder
import com.tangem.features.details.utils.SocialsBuilder
import kotlinx.coroutines.runBlocking
internal class PreviewDetailsComponent : DetailsComponent {
private val previewBlocks = runBlocking {
ItemsBuilder(
walletConnectComponent = PreviewWalletConnectComponent(),
userWalletListComponent = PreviewUserWalletListComponent(),
).buldAll()
}
private val previewFooter = DetailsFooterUM(
socials = SocialsBuilder(urlOpener = { /* no-op */ }).buildAll(),
appVersion = "1.0.0-preview",
)
val previewState = DetailsUM(
items = previewBlocks,
footer = previewFooter,
popBack = { /* no-op */ },
)
@Composable
@Suppress("TopLevelComposableFunctions") // TODO: Remove this check
override fun View(modifier: Modifier) {
TODO("Will be implemented in [REDACTED_TASK_KEY]")
DetailsScreen(state = previewState, modifier = modifier)
}
}

View file

@ -2,13 +2,63 @@ package com.tangem.features.details.component.preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.details.component.UserWalletListComponent
import com.tangem.features.details.entity.UserWalletListUM
import com.tangem.features.details.impl.R
import com.tangem.features.details.ui.UserWalletListBlock
import kotlinx.collections.immutable.persistentListOf
internal class PreviewUserWalletListComponent : UserWalletListComponent {
private val previewState = UserWalletListUM(
userWallets = persistentListOf(
UserWalletListUM.UserWalletUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = "My Wallet",
information = getInformation(3, "4 496,75 $"),
imageResId = R.drawable.ill_card_wallet_2_211_343,
onClick = {},
),
UserWalletListUM.UserWalletUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = "Old wallet",
information = getInformation(3, "4 496,75 $"),
imageResId = R.drawable.ill_card_note_eth_211_343,
onClick = {},
),
UserWalletListUM.UserWalletUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = "Multi Card",
information = getInformation(3, "4 496,75 $"),
imageResId = R.drawable.ill_card_note_bnb_211_343,
onClick = {},
),
),
addNewWalletText = resourceReference(R.string.user_wallet_list_add_button),
isWalletSavingInProgress = false,
onAddNewWalletClick = {},
)
@Composable
@Suppress("TopLevelComposableFunctions") // TODO: Remove this check
override fun View(modifier: Modifier) {
TODO("Will be implemented in [REDACTED_TASK_KEY]")
UserWalletListBlock(state = previewState, modifier = modifier)
}
private fun getInformation(cardCount: Int, totalBalance: String): TextReference {
val t1 = TextReference.PluralRes(
id = R.plurals.card_label_card_count,
count = cardCount,
formatArgs = wrappedList(cardCount),
)
val divider = stringReference(value = "")
val t2 = stringReference(totalBalance)
return TextReference.Combined(wrappedList(t1, divider, t2))
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.features.details.component.preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.features.details.component.WalletConnectComponent
import com.tangem.features.details.ui.WalletConnectBlock
internal class PreviewWalletConnectComponent : WalletConnectComponent {
@ -11,6 +12,6 @@ internal class PreviewWalletConnectComponent : WalletConnectComponent {
@Composable
@Suppress("TopLevelComposableFunctions") // TODO: Remove this check
override fun View(modifier: Modifier) {
TODO("Will be implemented in [REDACTED_TASK_KEY]")
WalletConnectBlock(onClick = { /* no-op */ }, modifier = modifier)
}
}

View file

@ -18,6 +18,7 @@ internal sealed class DetailsItemUM {
) : DetailsItemUM() {
data class Item(
val id: String,
val title: TextReference,
@DrawableRes
val iconRes: Int,

View file

@ -7,6 +7,7 @@ import kotlinx.collections.immutable.ImmutableList
internal data class UserWalletListUM(
val userWallets: ImmutableList<UserWalletUM>,
val isWalletSavingInProgress: Boolean,
val addNewWalletText: TextReference,
val onAddNewWalletClick: () -> Unit,
) {

View file

@ -0,0 +1,36 @@
package com.tangem.features.details.ui
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.Modifier
import com.tangem.core.ui.res.TangemTheme
@Composable
internal fun BlockCard(
modifier: Modifier = Modifier,
enabled: Boolean = true,
onClick: () -> Unit = {},
content: @Composable ColumnScope.() -> Unit = {},
) {
Card(
modifier = modifier,
onClick = onClick,
shape = TangemTheme.shapes.roundedCornersXMedium,
colors = BlockColors,
enabled = enabled,
content = content,
)
}
private val BlockColors: CardColors
@Composable
@ReadOnlyComposable
get() = CardColors(
containerColor = TangemTheme.colors.background.primary,
contentColor = TangemTheme.colors.text.primary1,
disabledContainerColor = TangemTheme.colors.button.disabled,
disabledContentColor = TangemTheme.colors.text.disabled,
)

View file

@ -0,0 +1,45 @@
package com.tangem.features.details.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.details.entity.DetailsItemUM
@Composable
internal fun BlockItem(model: DetailsItemUM.Basic.Item, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier,
onClick = model.onClick,
) {
Row(
modifier = Modifier.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12, Alignment.Start),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = model.iconRes),
tint = TangemTheme.colors.icon.secondary,
contentDescription = null,
)
Text(
text = model.title.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}

View file

@ -0,0 +1,221 @@
package com.tangem.features.details.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.details.component.preview.PreviewDetailsComponent
import com.tangem.features.details.entity.DetailsFooterUM
import com.tangem.features.details.entity.DetailsItemUM
import com.tangem.features.details.entity.DetailsUM
import com.tangem.features.details.impl.R
private const val COLLAPSED_APP_BAR_THRESHOLD = 0.4f
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun DetailsScreen(state: DetailsUM, modifier: Modifier = Modifier) {
val backgroundColor = TangemTheme.colors.background.secondary
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
SystemBarsEffect {
setSystemBarsColor(backgroundColor)
}
BackHandler(onBack = state.popBack)
Scaffold(
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
containerColor = backgroundColor,
topBar = { TopBar(state, scrollBehavior) },
) { paddingValues ->
Content(
modifier = Modifier.padding(paddingValues),
state = state,
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(state: DetailsUM, scrollBehavior: TopAppBarScrollBehavior, modifier: Modifier = Modifier) {
MediumTopAppBar(
modifier = modifier,
scrollBehavior = scrollBehavior,
colors = TopAppBarColors(
containerColor = TangemTheme.colors.background.secondary,
scrolledContainerColor = TangemTheme.colors.background.secondary,
navigationIconContentColor = TangemTheme.colors.icon.primary1,
titleContentColor = TangemTheme.colors.text.primary1,
actionIconContentColor = TangemTheme.colors.icon.primary1,
),
title = {
val collapsedStyle = TangemTheme.typography.subtitle1
val expandedStyle = TangemTheme.typography.h1
val style by remember(scrollBehavior.state.collapsedFraction) {
derivedStateOf {
if (scrollBehavior.state.collapsedFraction >= COLLAPSED_APP_BAR_THRESHOLD) {
collapsedStyle
} else {
expandedStyle
}
}
}
Text(
text = stringResource(id = R.string.details_title),
style = style,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
navigationIcon = {
IconButton(
modifier = Modifier.size(TangemTheme.dimens.size32),
onClick = state.popBack,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = R.drawable.ic_back_24),
contentDescription = null,
)
}
},
)
}
@Composable
private fun Content(state: DetailsUM, modifier: Modifier = Modifier) {
LazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
contentPadding = PaddingValues(
top = TangemTheme.dimens.spacing16,
bottom = TangemTheme.dimens.spacing16,
),
) {
items(
items = state.items,
key = DetailsItemUM::id,
) { block ->
Block(
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
model = block,
)
}
item(key = "footer") {
Footer(
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12),
model = state.footer,
)
}
}
}
@Composable
private fun Block(model: DetailsItemUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.background(
shape = TangemTheme.shapes.roundedCornersXMedium,
color = TangemTheme.colors.background.primary,
),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Top,
) {
when (model) {
is DetailsItemUM.Basic -> {
model.items.forEach { item ->
key(item.id) {
BlockItem(
modifier = Modifier.fillMaxWidth(),
model = item,
)
}
}
}
is DetailsItemUM.Component -> {
model.content(
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}
@Composable
private fun Footer(model: DetailsFooterUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.padding(
top = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing16,
)
.fillMaxWidth(),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
) {
val socialsScrollState = rememberScrollState()
Row(
modifier = Modifier
.fillMaxWidth()
.scrollable(socialsScrollState, orientation = Orientation.Horizontal),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
model.socials.forEach { social ->
key(social.id) {
IconButton(
modifier = Modifier.size(TangemTheme.dimens.size32),
onClick = social.onClick,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = social.iconResId),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
}
}
}
}
Text(
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing6),
text = model.appVersion,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun Preview_DetailsScreen() {
TangemThemePreview {
PreviewDetailsComponent().View(modifier = Modifier.fillMaxSize())
}
}
// endregion Preview

View file

@ -0,0 +1,121 @@
package com.tangem.features.details.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.details.entity.UserWalletListUM
import com.tangem.features.details.impl.R
@Composable
internal fun UserWalletListBlock(state: UserWalletListUM, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier,
) {
state.userWallets.forEach { model ->
key(model.id) {
UserWalletItem(
modifier = Modifier.fillMaxWidth(),
model = model,
)
}
}
AddWalletButton(
text = state.addNewWalletText,
isInProgress = state.isWalletSavingInProgress,
onClick = state.onAddNewWalletClick,
)
}
}
@Composable
private fun UserWalletItem(model: UserWalletListUM.UserWalletUM, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier,
onClick = model.onClick,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size68)
.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
Image(
modifier = Modifier
.width(TangemTheme.dimens.size24)
.height(TangemTheme.dimens.size36),
painter = painterResource(id = model.imageResId),
contentScale = ContentScale.FillBounds,
contentDescription = null,
)
Column(
modifier = Modifier.heightIn(min = TangemTheme.dimens.size40),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.SpaceEvenly,
) {
Text(
text = model.name,
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = model.information.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
@Composable
private fun AddWalletButton(
text: TextReference,
isInProgress: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
BlockCard(
modifier = modifier,
onClick = onClick,
enabled = !isInProgress,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = R.drawable.ic_plus_24),
tint = TangemTheme.colors.icon.accent,
contentDescription = null,
)
Text(
text = text.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.accent,
)
}
}
}

View file

@ -0,0 +1,51 @@
package com.tangem.features.details.ui
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.details.impl.R
@Composable
internal fun WalletConnectBlock(onClick: () -> Unit, modifier: Modifier = Modifier) {
BlockCard(
modifier = modifier,
onClick = onClick,
) {
Row(
modifier = Modifier.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
painter = painterResource(id = R.drawable.ic_wallet_connect_24),
tint = TangemColorPalette.Azure,
contentDescription = null,
)
Column(
modifier = Modifier.heightIn(min = TangemTheme.dimens.size48),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.SpaceAround,
) {
Text(
text = stringResource(id = R.string.wallet_connect_title),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
Text(
text = stringResource(id = R.string.wallet_connect_subtitle),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
)
}
}
}
}

View file

@ -0,0 +1,97 @@
package com.tangem.features.details.utils
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.features.details.component.UserWalletListComponent
import com.tangem.features.details.component.WalletConnectComponent
import com.tangem.features.details.entity.DetailsItemUM
import com.tangem.features.details.impl.BuildConfig
import com.tangem.features.details.impl.R
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal class ItemsBuilder(
private val walletConnectComponent: WalletConnectComponent,
private val userWalletListComponent: UserWalletListComponent,
) {
suspend fun buldAll(): ImmutableList<DetailsItemUM> = buildList {
buildWalletConnectBlock()?.let(::add)
buildUserWalletListBlock().let(::add)
buildShopBlock().let(::add)
buildSettingsBlock().let(::add)
buildSupportBlock().let(::add)
}.toImmutableList()
private suspend fun buildWalletConnectBlock(): DetailsItemUM? {
return if (walletConnectComponent.checkIsAvailable()) {
DetailsItemUM.Component(
id = "wallet_connect",
content = {
walletConnectComponent.View(modifier = it)
},
)
} else {
null
}
}
fun buildUserWalletListBlock(): DetailsItemUM = DetailsItemUM.Component(
id = "user_wallet_list",
content = {
userWalletListComponent.View(modifier = it)
},
)
private fun buildShopBlock(): DetailsItemUM = DetailsItemUM.Basic(
id = "shop",
items = persistentListOf(
DetailsItemUM.Basic.Item(
id = "buy_tangem_wallet",
title = stringReference("Buy Tangem Wallet"), // TODO: Move to resources in [REDACTED_TASK_KEY]
iconRes = R.drawable.ic_tangem_24,
onClick = { /* [REDACTED_TODO_COMMENT] */ },
),
),
)
private fun buildSettingsBlock(): DetailsItemUM = DetailsItemUM.Basic(
id = "settings",
items = buildList {
DetailsItemUM.Basic.Item(
id = "app_settings",
title = resourceReference(R.string.app_settings_title),
iconRes = R.drawable.ic_settings_24,
onClick = { /* [REDACTED_TODO_COMMENT] */ },
).let(::add)
if (BuildConfig.TESTER_MENU_ENABLED) {
DetailsItemUM.Basic.Item(
id = "tester_menu",
title = stringReference(value = "Tester menu"),
iconRes = R.drawable.ic_alert_24,
onClick = { /* [REDACTED_TODO_COMMENT] */ },
).let(::add)
}
}.toImmutableList(),
)
private fun buildSupportBlock(): DetailsItemUM = DetailsItemUM.Basic(
id = "support",
items = persistentListOf(
DetailsItemUM.Basic.Item(
id = "send_feedback",
title = stringReference("Send feedback"), // TODO: Move to resources in [REDACTED_TASK_KEY]
iconRes = R.drawable.ic_comment_24,
onClick = { /* [REDACTED_TODO_COMMENT] */ },
),
DetailsItemUM.Basic.Item(
id = "disclaimer",
title = resourceReference(R.string.disclaimer_title),
iconRes = R.drawable.ic_text_24,
onClick = { /* [REDACTED_TODO_COMMENT] */ },
),
),
)
}

View file

@ -0,0 +1,84 @@
package com.tangem.features.details.utils
import androidx.compose.ui.text.intl.Locale
import com.tangem.features.details.entity.DetailsFooterUM
import com.tangem.features.details.impl.R
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
internal class SocialsBuilder(
private val urlOpener: (url: String) -> Unit,
) {
fun buildAll(): ImmutableList<DetailsFooterUM.Social> = Social.all.map { social ->
DetailsFooterUM.Social(
id = social.name,
iconResId = social.iconResId,
onClick = { openUrl(social) },
)
}.toImmutableList()
private fun openUrl(social: Social) {
val locale = Locale.current.region
if (locale == RUSSIA_LOCALE && social.urlRu != null) {
urlOpener(social.urlRu)
} else {
urlOpener(social.url)
}
}
private enum class Social(
val iconResId: Int,
val url: String,
val urlRu: String? = null,
) {
X(
iconResId = R.drawable.ic_twitter_24,
url = "https://x.com/tangem",
),
TELEGRAM(
iconResId = R.drawable.ic_telegram_24,
url = "https://t.me/tangem_chat",
urlRu = "https://t.me/tangem_chat_ru",
),
DISCORD(
iconResId = R.drawable.ic_discord_24,
url = "https://discord.gg/tangem",
),
REDDIT(
iconResId = R.drawable.ic_reddit_24,
url = "https://www.reddit.com/r/Tangem/",
),
INSTAGRAM(
iconResId = R.drawable.ic_instagram_24,
url = "https://www.instagram.com/tangemwallet",
),
GIT_HUB(
iconResId = R.drawable.ic_github_24,
url = "https://github.com/tangem",
),
FACEBOOK(
iconResId = R.drawable.ic_facebook_24,
url = "https://www.facebook.com/tangemwallet",
),
LINKEDIN(
iconResId = R.drawable.ic_linkedin_24,
url = "https://www.linkedin.com/company/tangem",
),
YOUTUBE(
iconResId = R.drawable.ic_youtube_24,
url = "https://youtube.com/@tangem_official",
),
;
companion object {
val all = values()
}
}
private companion object {
const val RUSSIA_LOCALE = "ru"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB