diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt index 11682ec1f1..385824799e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt @@ -230,7 +230,12 @@ private fun PrimaryButtonSample() { verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { PrimaryButton(modifier = Modifier.fillMaxWidth(), text = "Manage tokens", onClick = { }) - PrimaryButton(modifier = Modifier.fillMaxWidth(), showProgress = true, text = "Manage tokens", onClick = { }) + PrimaryButton( + modifier = Modifier.fillMaxWidth(), + showProgress = true, + text = "Manage tokens", + onClick = { }, + ) PrimaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", @@ -289,7 +294,12 @@ private fun SecondaryButtonSample() { verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { SecondaryButton(modifier = Modifier.fillMaxWidth(), text = "Manage tokens", onClick = { }) - SecondaryButton(modifier = Modifier.fillMaxWidth(), showProgress = true, text = "Manage tokens", onClick = { }) + SecondaryButton( + modifier = Modifier.fillMaxWidth(), + showProgress = true, + text = "Manage tokens", + onClick = { }, + ) SecondaryButtonIconEnd( modifier = Modifier.fillMaxWidth(), text = "Manage tokens", diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Notifier.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Notifier.kt new file mode 100644 index 0000000000..a646f37f33 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Notifier.kt @@ -0,0 +1,68 @@ +package com.tangem.core.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.res.TangemTheme + +/** + * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=68%3A20&mode=design&t=WSV3AxC6zV1y0CHF-1) + * */ +@Composable +fun Notifier( + text: String, + modifier: Modifier = Modifier, + textColor: Color = TangemTheme.colors.text.primary1, + backgroundColor: Color = TangemTheme.colors.icon.inactive, +) { + Box( + modifier = modifier + .heightIn(TangemTheme.dimens.size32) + .background( + color = backgroundColor, + shape = TangemTheme.shapes.roundedCorners8, + ), + ) { + Text( + modifier = Modifier + .align(Alignment.Center) + .padding(horizontal = TangemTheme.dimens.size10), + text = text, + color = textColor, + style = TangemTheme.typography.button, + ) + } +} + +@Preview +@Composable +private fun TangemNotifierPreview_Light(@PreviewParameter(NotifierProvider::class) text: String) { + TangemTheme(isDark = false) { + Notifier(text = text) + } +} + +@Preview +@Composable +private fun TangemNotifierPreview_Dark(@PreviewParameter(NotifierProvider::class) text: String) { + TangemTheme(isDark = true) { + Notifier(text = text) + } +} + +private class NotifierProvider : CollectionPreviewParameterProvider( + collection = listOf( + "a", + "Card 1 of 2", + "Card 1 of 2 or many other", + ), +) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index c3f075ffb0..a78bd58dbd 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -43,6 +43,7 @@ data class TangemDimens internal constructor( val size5: Dp = 5.dp, val size7: Dp = 7.dp, val size8: Dp = 8.dp, + val size10: Dp = 10.dp, val size11: Dp = 11.dp, val size12: Dp = 12.dp, val size16: Dp = 16.dp, diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt index b452e38004..a3802221fb 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemShapes.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.graphics.Shape data class TangemShapes internal constructor( val roundedCornersSmall: Shape, val roundedCornersSmall2: Shape, + val roundedCorners8: Shape, val roundedCornersMedium: Shape, val roundedCornersXMedium: Shape, val roundedCornersLarge: Shape, @@ -16,6 +17,7 @@ data class TangemShapes internal constructor( constructor(dimens: TangemDimens) : this( roundedCornersSmall = RoundedCornerShape(size = dimens.radius2), roundedCornersSmall2 = RoundedCornerShape(size = dimens.radius4), + roundedCorners8 = RoundedCornerShape(size = dimens.radius8), roundedCornersMedium = RoundedCornerShape(size = dimens.radius12), roundedCornersXMedium = RoundedCornerShape(size = dimens.radius16), roundedCornersLarge = RoundedCornerShape(size = dimens.radius28), diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt index f3152c931e..a63b4bccea 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt @@ -5,6 +5,9 @@ import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.bip39.MnemonicErrorResult import com.tangem.feature.onboarding.data.MnemonicRepository import com.tangem.utils.extensions.isNotWhitespace +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList /** [REDACTED_AUTHOR] @@ -58,14 +61,18 @@ internal class DefaultSeedPhraseInteractor constructor( } } - override suspend fun getSuggestions(text: String, hasSelection: Boolean, cursorPosition: Int): List { - if (text.isEmpty() || cursorPosition == 0 || hasSelection) return emptyList() + override suspend fun getSuggestions( + text: String, + hasSelection: Boolean, + cursorPosition: Int, + ): ImmutableList { + if (text.isEmpty() || cursorPosition == 0 || hasSelection) return persistentListOf() val word = partWordFinder.getLeadPartOfWord(text, cursorPosition) - ?: return emptyList() + ?: return persistentListOf() val suggestions = repository.getWordsDictionary() .filter { it.startsWith(word, ignoreCase = false) && it != word } - .toList() + .toPersistentList() return suggestions } diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt index 43ed7ac068..2c0f319c19 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt @@ -1,6 +1,7 @@ package com.tangem.feature.onboarding.domain import com.tangem.crypto.bip39.Mnemonic +import kotlinx.collections.immutable.ImmutableList /** [REDACTED_AUTHOR] @@ -10,7 +11,7 @@ interface SeedPhraseInteractor { suspend fun getMnemonicComponents(): Result> suspend fun isWordMatch(word: String): Boolean suspend fun validateMnemonicString(text: String): Result> - suspend fun getSuggestions(text: String, hasSelection: Boolean, cursorPosition: Int): List + suspend fun getSuggestions(text: String, hasSelection: Boolean, cursorPosition: Int): ImmutableList suspend fun insertSuggestionWord(text: String, suggestion: String, cursorPosition: Int): InsertSuggestionResult companion object { diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/OnboardingSeedPhraseState.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/OnboardingSeedPhraseState.kt index 74b732e86b..44bdac25c3 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/OnboardingSeedPhraseState.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/OnboardingSeedPhraseState.kt @@ -52,7 +52,7 @@ data class ImportSeedPhraseState( val onSuggestedPhraseClick: (Int) -> Unit, val buttonCreateWallet: ButtonState, val invalidWords: Set = emptySet(), - val suggestionsList: List = emptyList(), + val suggestionsList: ImmutableList = persistentListOf(), val error: SeedPhraseError? = null, ) diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt index d29fd51838..be8a31f5df 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt @@ -1,6 +1,7 @@ package com.tangem.feature.onboarding.presentation.wallet2.ui import androidx.compose.animation.* +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.material.OutlinedTextField @@ -11,10 +12,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.SpanStyle +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.unit.IntOffset -import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.Notifier import com.tangem.core.ui.components.PrimaryButtonIconStart import com.tangem.core.ui.components.TangemTextFieldsDefault +import com.tangem.core.ui.components.buttons.common.* import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.onboarding.R import com.tangem.feature.onboarding.presentation.wallet2.model.ImportSeedPhraseState @@ -23,6 +29,8 @@ import com.tangem.feature.onboarding.presentation.wallet2.ui.components.Onboardi import com.tangem.feature.onboarding.presentation.wallet2.ui.components.OnboardingDescriptionBlock import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.InvalidWordsColorTransformation import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseErrorConverter +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf /** [REDACTED_AUTHOR] @@ -48,7 +56,9 @@ fun ImportSeedPhraseScreen(state: ImportSeedPhraseState, modifier: Modifier = Mo state = state, ) SuggestionsBlock( - state = state, + modifier = Modifier.padding(top = TangemTheme.dimens.size4), + suggestionsList = state.suggestionsList, + onClick = { index -> state.onSuggestedPhraseClick(index) }, ) } } @@ -114,25 +124,86 @@ private fun PhraseBlock(state: ImportSeedPhraseState, modifier: Modifier = Modif @Suppress("ReusedModifierInstance") @Composable -private fun SuggestionsBlock(state: ImportSeedPhraseState, modifier: Modifier = Modifier) { +private fun SuggestionsBlock( + suggestionsList: ImmutableList, + onClick: (Int) -> Unit, + modifier: Modifier = Modifier, +) { AnimatedVisibility( enter = fadeIn() + slideIn(initialOffset = { IntOffset(x = 200, y = 0) }), exit = slideOut(targetOffset = { IntOffset(x = -200, y = 0) }) + fadeOut(), - visible = state.suggestionsList.isNotEmpty(), + visible = suggestionsList.isNotEmpty(), ) { LazyRow( - modifier = modifier.fillMaxSize(), + modifier = modifier, contentPadding = PaddingValues(horizontal = TangemTheme.dimens.size16), ) { - items(state.suggestionsList.size) { index -> - PrimaryButton( - modifier = Modifier - .height(TangemTheme.dimens.size46) - .padding(all = TangemTheme.dimens.size4), - text = state.suggestionsList[index], - onClick = { state.onSuggestedPhraseClick(index) }, + items(suggestionsList.size) { index -> + SuggestionButton( + modifier = Modifier.rowPadding( + index = index, + rowSize = suggestionsList.size, + outSide = TangemTheme.dimens.size0, + inSide = TangemTheme.dimens.size4, + ), + text = suggestionsList[index], + onClick = { onClick(index) }, ) } } } -} \ No newline at end of file +} + +@Composable +private fun SuggestionButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) { + Box(modifier = modifier.clickable(onClick = onClick)) { + Notifier( + text = text, + backgroundColor = TangemTheme.colors.background.action, + textColor = TangemTheme.colors.text.primary2, + ) + } +} + +private fun Modifier.rowPadding(index: Int, rowSize: Int, outSide: Dp, inSide: Dp): Modifier = when (index) { + 0 -> this.padding(start = outSide, end = inSide) + rowSize - 1 -> this.padding(start = inSide, end = outSide) + else -> this.padding(horizontal = inSide) +} + +@Preview +@Composable +private fun SuggestionsBlockPreview_Light( + @PreviewParameter(SuggestionsPreviewParamsProvider::class) suggestions: ImmutableList, +) { + TangemTheme(isDark = false) { + SuggestionsBlock( + suggestionsList = suggestions, + onClick = {}, + ) + } +} + +@Preview +@Composable +private fun SuggestionsBlockPreview_Dark( + @PreviewParameter(SuggestionsPreviewParamsProvider::class) suggestions: ImmutableList, +) { + TangemTheme(isDark = true) { + SuggestionsBlock( + suggestionsList = suggestions, + onClick = {}, + ) + } +} + +private class SuggestionsPreviewParamsProvider : CollectionPreviewParameterProvider>( + collection = listOf( + persistentListOf( + "one", + "each", + "explorer", + "unknown", + ), + ), +) \ No newline at end of file diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/stateBuiders/ImportSeedPhraseStateBuilder.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/stateBuiders/ImportSeedPhraseStateBuilder.kt index 5ffb9b5c41..794ec1d599 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/stateBuiders/ImportSeedPhraseStateBuilder.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/stateBuiders/ImportSeedPhraseStateBuilder.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.text.input.TextFieldValue import com.tangem.feature.onboarding.domain.InsertSuggestionResult import com.tangem.feature.onboarding.domain.SeedPhraseError import com.tangem.feature.onboarding.presentation.wallet2.model.OnboardingSeedPhraseState +import kotlinx.collections.immutable.ImmutableList /** [REDACTED_AUTHOR] @@ -39,12 +40,14 @@ class ImportSeedPhraseStateBuilder { ), ) - fun updateSuggestions(uiState: OnboardingSeedPhraseState, suggestions: List): OnboardingSeedPhraseState = - uiState.copy( - importSeedPhraseState = uiState.importSeedPhraseState.copy( - suggestionsList = suggestions, - ), - ) + fun updateSuggestions( + uiState: OnboardingSeedPhraseState, + suggestions: ImmutableList, + ): OnboardingSeedPhraseState = uiState.copy( + importSeedPhraseState = uiState.importSeedPhraseState.copy( + suggestionsList = suggestions, + ), + ) fun insertSuggestionWord( uiState: OnboardingSeedPhraseState, diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt index 8dcb8ff0d6..c7d9c234ba 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt @@ -22,6 +22,7 @@ import com.tangem.utils.coroutines.Debouncer import com.tangem.utils.extensions.isEven import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.* import kotlinx.coroutines.flow.* @@ -373,7 +374,7 @@ class SeedPhraseViewModel @Inject constructor( ) updateUi { val mediateState = uiBuilder.importSeedPhrase.insertSuggestionWord(uiState, insertResult) - uiBuilder.importSeedPhrase.updateSuggestions(mediateState, emptyList()) + uiBuilder.importSeedPhrase.updateSuggestions(mediateState, persistentListOf()) } } }