Updated on 2026-08-14

This commit is contained in:
Tangem 2023-04-25 09:37:15 +03:00
parent 5024408d45
commit 4e81ef251d
19 changed files with 93 additions and 197 deletions

@ -1 +1 @@
Subproject commit f5f6ac954894ef66331db8fe383b9773feb69ef4
Subproject commit c714678d9c46dee1b15ef9e8d27aa23afa1dfd8d

View file

@ -22,5 +22,5 @@ class TestingCoroutineDispatcherProvider(
override val main: CoroutineDispatcher = Dispatchers.Unconfined,
override val io: CoroutineDispatcher = Dispatchers.Unconfined,
override val single: CoroutineDispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher(),
) : CoroutineDispatcherProvider
) : CoroutineDispatcherProvider

View file

@ -3,4 +3,4 @@ package com.tangem.utils.extensions
/**
[REDACTED_AUTHOR]
*/
fun Char.isNotWhitespace(): Boolean = !this.isWhitespace()
fun Char.isNotWhitespace(): Boolean = !this.isWhitespace()

View file

@ -1,10 +1,6 @@
package com.tangem.feature.onboarding.data
import com.tangem.crypto.bip39.BIP39Wordlist
import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.bip39.EntropyLength
import com.tangem.crypto.bip39.Mnemonic
import com.tangem.crypto.bip39.Wordlist
import com.tangem.crypto.bip39.*
import com.tangem.datasource.asset.AssetReader
/**
@ -33,4 +29,4 @@ internal class DefaultMnemonicRepository(
companion object {
private const val MNEMONIC_FILE_NAME = "mnemonic/mnemonic_dictionary_en.txt"
}
}
}

View file

@ -58,11 +58,7 @@ internal class DefaultSeedPhraseInteractor constructor(
}
}
override suspend fun getSuggestions(
text: String,
hasSelection: Boolean,
cursorPosition: Int,
): List<String> {
override suspend fun getSuggestions(text: String, hasSelection: Boolean, cursorPosition: Int): List<String> {
if (text.isEmpty() || cursorPosition == 0 || hasSelection) return emptyList()
val word = partWordFinder.getLeadPartOfWord(text, cursorPosition)
?: return emptyList()

View file

@ -29,12 +29,12 @@ sealed class SeedPhraseError(
message = message ?: this::class.java.simpleName,
data = data,
) {
object InvalidWordCount : SeedPhraseError(0)
object InvalidEntropyLength : SeedPhraseError(1)
object InvalidWordsFile : SeedPhraseError(2)
object InvalidChecksum : SeedPhraseError(3)
object MnenmonicCreationFailed : SeedPhraseError(4)
object NormalizationFailed : SeedPhraseError(5)
object UnsupportedLanguage : SeedPhraseError(6)
data class InvalidWords(val words: Set<String>) : SeedPhraseError(7)
}
object InvalidWordCount : SeedPhraseError(subCode = 0)
object InvalidEntropyLength : SeedPhraseError(subCode = 1)
object InvalidWordsFile : SeedPhraseError(subCode = 2)
object InvalidChecksum : SeedPhraseError(subCode = 3)
object MnenmonicCreationFailed : SeedPhraseError(subCode = 4)
object NormalizationFailed : SeedPhraseError(subCode = 5)
object UnsupportedLanguage : SeedPhraseError(subCode = 6)
data class InvalidWords(val words: Set<String>) : SeedPhraseError(subCode = 7)
}

View file

@ -18,6 +18,7 @@ internal class PartWordFinder {
val hadWhiteSpaceOnLeftOfCursor = charText[cursorPosition - 1].isWhitespace()
val cursorInMiddleOfWord = charText.size > cursorPosition && charText[cursorPosition].isNotWhitespace()
@Suppress("ComplexCondition")
if (cursorIsOutOfTextRange || cursorAtStartOfText || hadWhiteSpaceOnLeftOfCursor || cursorInMiddleOfWord) {
return null
}
@ -46,4 +47,4 @@ internal class PartWordFinder {
return word.substring(leadPart.length, word.length)
}
}
}

View file

@ -45,9 +45,10 @@ data class ImportSeedPhraseUiAction(
data class TextFieldUiAction(
val onTextFieldChanged: (TextFieldValue) -> Unit,
val onFocusChanged: (Boolean) -> Unit = {},
)
enum class SeedPhraseField {
Second,
Seventh,

View file

@ -4,14 +4,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@ -34,20 +27,17 @@ import com.tangem.feature.onboarding.presentation.wallet2.ui.components.Onboardi
[REDACTED_AUTHOR]
*/
@Composable
fun AboutSeedPhraseScreen(
state: AboutState,
modifier: Modifier = Modifier,
) {
fun AboutSeedPhraseScreen(state: AboutState, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
) {
Box(
modifier = Modifier.weight(0.8f),
modifier = Modifier.weight(weight = 0.8f),
) {
EditIconBlock()
}
Box(
modifier = Modifier.weight(1.2f),
modifier = Modifier.weight(weight = 1.2f),
) {
Column {
OnboardingDescriptionBlock {
@ -119,7 +109,6 @@ private fun EditIconBlock() {
style = TangemTheme.typography.body1,
)
}
}
}

View file

@ -1,10 +1,6 @@
package com.tangem.feature.onboarding.presentation.wallet2.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -22,11 +18,9 @@ import com.tangem.feature.onboarding.presentation.wallet2.ui.components.Onboardi
/**
[REDACTED_AUTHOR]
*/
@Suppress("ReusedModifierInstance")
@Composable
fun CheckSeedPhraseScreen(
state: CheckSeedPhraseState,
modifier: Modifier = Modifier,
) {
fun CheckSeedPhraseScreen(state: CheckSeedPhraseState, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize(),
@ -78,10 +72,7 @@ fun CheckSeedPhraseScreen(
}
@Composable
private fun CheckSeedPhraseBlock(
modifier: Modifier = Modifier,
state: CheckSeedPhraseState,
) {
private fun CheckSeedPhraseBlock(state: CheckSeedPhraseState, modifier: Modifier = Modifier) {
Column(modifier) {
OutlineTextField(
value = state.tvSecondPhrase.textFieldValue,

View file

@ -1,17 +1,7 @@
package com.tangem.feature.onboarding.presentation.wallet2.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.animation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
@ -39,10 +29,7 @@ import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseEr
[REDACTED_AUTHOR]
*/
@Composable
fun ImportSeedPhraseScreen(
state: ImportSeedPhraseState,
modifier: Modifier = Modifier,
) {
fun ImportSeedPhraseScreen(state: ImportSeedPhraseState, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
) {
@ -85,10 +72,7 @@ fun ImportSeedPhraseScreen(
}
@Composable
private fun PhraseBlock(
state: ImportSeedPhraseState,
modifier: Modifier = Modifier,
) {
private fun PhraseBlock(state: ImportSeedPhraseState, modifier: Modifier = Modifier) {
// TODO: replace by TextReference
val errorConverter = remember { SeedPhraseErrorConverter() }
@ -108,8 +92,9 @@ private fun PhraseBlock(
style = SpanStyle(color = TangemTheme.colors.text.warning),
),
colors = TangemTextFieldsDefault.defaultTextFieldColors,
)
Box(
modifier = Modifier
.fillMaxWidth()
@ -129,14 +114,12 @@ private fun PhraseBlock(
}
}
@Suppress("ReusedModifierInstance")
@Composable
private fun SuggestionsBlock(
state: ImportSeedPhraseState,
modifier: Modifier = Modifier,
) {
private fun SuggestionsBlock(state: ImportSeedPhraseState, modifier: Modifier = Modifier) {
AnimatedVisibility(
enter = fadeIn() + slideIn(initialOffset = { IntOffset(200, 0) }),
exit = slideOut(targetOffset = { IntOffset(-200, 0) }) + fadeOut(),
enter = fadeIn() + slideIn(initialOffset = { IntOffset(x = 200, y = 0) }),
exit = slideOut(targetOffset = { IntOffset(x = -200, y = 0) }) + fadeOut(),
visible = state.suggestionsList.isNotEmpty(),
) {
LazyRow(

View file

@ -1,11 +1,7 @@
package com.tangem.feature.onboarding.presentation.wallet2.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -28,20 +24,17 @@ import com.tangem.feature.onboarding.presentation.wallet2.ui.components.Onboardi
[REDACTED_AUTHOR]
*/
@Composable
fun IntroScreen(
state: IntroState,
modifier: Modifier = Modifier,
) {
fun IntroScreen(state: IntroState, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
) {
Box(
modifier = Modifier.weight(1.5f),
modifier = Modifier.weight(weight = 1.5f),
) {
CardImageBlock(state.cardImageUrl)
}
Box(
modifier = Modifier.weight(1f),
modifier = Modifier.weight(weight = 1f),
) {
OnboardingDescriptionBlock {
Description(
@ -79,9 +72,7 @@ fun IntroScreen(
}
@Composable
private fun CardImageBlock(
cardImageUrl: String? = null,
) {
private fun CardImageBlock(cardImageUrl: String? = null) {
Box(modifier = Modifier.fillMaxSize()) {
val cardImageModifier = Modifier
.fillMaxSize()
@ -101,7 +92,7 @@ private fun CardImageBlock(
}
@Composable
private fun CardPlaceHolder(modifier: Modifier) {
private fun CardPlaceHolder(modifier: Modifier = Modifier) {
Image(
modifier = modifier,
painter = painterResource(id = R.drawable.card_placeholder_black),

View file

@ -1,12 +1,6 @@
package com.tangem.feature.onboarding.presentation.wallet2.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material.Text
@ -29,15 +23,12 @@ import kotlinx.collections.immutable.ImmutableList
[REDACTED_AUTHOR]
*/
@Composable
fun YourSeedPhraseScreen(
state: YourSeedPhraseState,
modifier: Modifier = Modifier,
) {
fun YourSeedPhraseScreen(state: YourSeedPhraseState, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
) {
Box(
modifier = Modifier.weight(0.6f),
modifier = Modifier.weight(weight = 0.6f),
) {
OnboardingDescriptionBlock(
modifier = Modifier.align(Alignment.Center),
@ -49,7 +40,7 @@ fun YourSeedPhraseScreen(
}
}
Box(
modifier = Modifier.weight(1f),
modifier = Modifier.weight(weight = 1f),
) {
PhraseGreedBlock(state.mnemonicGridItems)
}
@ -72,7 +63,7 @@ fun YourSeedPhraseScreen(
@Composable
private fun PhraseGreedBlock(mnemonicGridItems: ImmutableList<MnemonicGridItem>) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
columns = GridCells.Fixed(count = 2),
) {
this.items(
count = mnemonicGridItems.size,
@ -95,7 +86,6 @@ private fun PhraseGreedBlock(mnemonicGridItems: ImmutableList<MnemonicGridItem>)
color = TangemTheme.colors.text.primary1,
)
}
}
}
}

View file

@ -19,10 +19,7 @@ import kotlinx.collections.immutable.ImmutableList
* UI component witch provides single and carousel description for the onboarding process
*/
@Composable
fun OnboardingDescriptionBlock(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
fun OnboardingDescriptionBlock(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Box(
modifier = modifier
.fillMaxWidth()
@ -33,10 +30,7 @@ fun OnboardingDescriptionBlock(
}
@Composable
fun Description(
@StringRes titleRes: Int,
@StringRes subTitleRes: Int,
) {
fun Description(@StringRes titleRes: Int, @StringRes subTitleRes: Int) {
Column {
DescriptionTitleText(text = stringResource(id = titleRes))
SpacerH12()
@ -44,13 +38,14 @@ fun Description(
}
}
@Suppress("UnusedPrivateMember")
@Composable
fun OnboardingCarouselDescriptionBlock(
modifier: Modifier = Modifier,
descriptionsList: ImmutableList<DescriptionResource>,
modifier: Modifier = Modifier,
) {
OnboardingDescriptionBlock(modifier) {
//TODO: implement
// TODO: implement
}
}

View file

@ -65,10 +65,7 @@ class CheckSeedPhraseStateBuilder {
)
}
fun updateCreateWalletButton(
uiState: OnboardingSeedPhraseState,
enabled: Boolean,
): OnboardingSeedPhraseState {
fun updateCreateWalletButton(uiState: OnboardingSeedPhraseState, enabled: Boolean): OnboardingSeedPhraseState {
return uiState.copy(
checkSeedPhraseState = uiState.checkSeedPhraseState.copy(
buttonCreateWallet = uiState.checkSeedPhraseState.buttonCreateWallet.copy(
@ -77,4 +74,4 @@ class CheckSeedPhraseStateBuilder {
),
)
}
}
}

View file

@ -22,10 +22,7 @@ class ImportSeedPhraseStateBuilder {
),
)
fun updateCreateWalletButton(
uiState: OnboardingSeedPhraseState,
enabled: Boolean,
): OnboardingSeedPhraseState {
fun updateCreateWalletButton(uiState: OnboardingSeedPhraseState, enabled: Boolean): OnboardingSeedPhraseState {
return uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
buttonCreateWallet = uiState.importSeedPhraseState.buttonCreateWallet.copy(
@ -35,23 +32,19 @@ class ImportSeedPhraseStateBuilder {
)
}
fun updateInvalidWords(
uiState: OnboardingSeedPhraseState,
invalidWords: Set<String>,
): OnboardingSeedPhraseState = uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
invalidWords = invalidWords,
),
)
fun updateInvalidWords(uiState: OnboardingSeedPhraseState, invalidWords: Set<String>): OnboardingSeedPhraseState =
uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
invalidWords = invalidWords,
),
)
fun updateSuggestions(
uiState: OnboardingSeedPhraseState,
suggestions: List<String>,
): OnboardingSeedPhraseState = uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
suggestionsList = suggestions,
),
)
fun updateSuggestions(uiState: OnboardingSeedPhraseState, suggestions: List<String>): OnboardingSeedPhraseState =
uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
suggestionsList = suggestions,
),
)
fun insertSuggestionWord(
uiState: OnboardingSeedPhraseState,
@ -64,10 +57,7 @@ class ImportSeedPhraseStateBuilder {
return updateTextField(uiState, newTextFieldValue)
}
fun updateError(
uiState: OnboardingSeedPhraseState,
error: SeedPhraseError?,
): OnboardingSeedPhraseState {
fun updateError(uiState: OnboardingSeedPhraseState, error: SeedPhraseError?): OnboardingSeedPhraseState {
return uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
error = error,

View file

@ -1,16 +1,6 @@
package com.tangem.feature.onboarding.presentation.wallet2.ui.stateBuiders
import com.tangem.feature.onboarding.presentation.wallet2.model.AboutState
import com.tangem.feature.onboarding.presentation.wallet2.model.ButtonState
import com.tangem.feature.onboarding.presentation.wallet2.model.CheckSeedPhraseState
import com.tangem.feature.onboarding.presentation.wallet2.model.ImportSeedPhraseState
import com.tangem.feature.onboarding.presentation.wallet2.model.IntroState
import com.tangem.feature.onboarding.presentation.wallet2.model.MnemonicGridItem
import com.tangem.feature.onboarding.presentation.wallet2.model.OnboardingSeedPhraseState
import com.tangem.feature.onboarding.presentation.wallet2.model.OnboardingSeedPhraseStep
import com.tangem.feature.onboarding.presentation.wallet2.model.TextFieldState
import com.tangem.feature.onboarding.presentation.wallet2.model.UiActions
import com.tangem.feature.onboarding.presentation.wallet2.model.YourSeedPhraseState
import com.tangem.feature.onboarding.presentation.wallet2.model.*
import kotlinx.collections.immutable.ImmutableList
/**
@ -89,16 +79,12 @@ class StateBuilder(
),
)
fun changeStep(
uiState: OnboardingSeedPhraseState,
step: OnboardingSeedPhraseStep,
): OnboardingSeedPhraseState = uiState.copy(
step = step,
)
fun changeStep(uiState: OnboardingSeedPhraseState, step: OnboardingSeedPhraseStep): OnboardingSeedPhraseState =
uiState.copy(
step = step,
)
fun generateMnemonicComponents(
uiState: OnboardingSeedPhraseState,
): OnboardingSeedPhraseState {
fun generateMnemonicComponents(uiState: OnboardingSeedPhraseState): OnboardingSeedPhraseState {
return uiState.copy(
aboutState = uiState.aboutState.copy(
buttonGenerateSeedPhrase = uiState.aboutState.buttonGenerateSeedPhrase.copy(

View file

@ -76,4 +76,4 @@ class InvalidWordsColorTransformation(
private const val WHITE_SPACE = " "
private const val WORD_SEPARATOR = "_$@-*"
}
}
}

View file

@ -8,18 +8,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.feature.onboarding.domain.SeedPhraseError
import com.tangem.feature.onboarding.domain.SeedPhraseInteractor
import com.tangem.feature.onboarding.presentation.wallet2.model.AboutUiAction
import com.tangem.feature.onboarding.presentation.wallet2.model.CheckSeedPhraseUiAction
import com.tangem.feature.onboarding.presentation.wallet2.model.ImportSeedPhraseUiAction
import com.tangem.feature.onboarding.presentation.wallet2.model.IntroUiAction
import com.tangem.feature.onboarding.presentation.wallet2.model.MnemonicGridItem
import com.tangem.feature.onboarding.presentation.wallet2.model.OnboardingSeedPhraseState
import com.tangem.feature.onboarding.presentation.wallet2.model.OnboardingSeedPhraseStep
import com.tangem.feature.onboarding.presentation.wallet2.model.SeedPhraseField
import com.tangem.feature.onboarding.presentation.wallet2.model.TextFieldState
import com.tangem.feature.onboarding.presentation.wallet2.model.TextFieldUiAction
import com.tangem.feature.onboarding.presentation.wallet2.model.UiActions
import com.tangem.feature.onboarding.presentation.wallet2.model.YourSeedPhraseUiAction
import com.tangem.feature.onboarding.presentation.wallet2.model.*
import com.tangem.feature.onboarding.presentation.wallet2.ui.stateBuiders.StateBuilder
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.Debouncer
@ -27,11 +16,7 @@ import com.tangem.utils.extensions.isEven
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
@ -195,9 +180,11 @@ class SeedPhraseViewModel @Inject constructor(
// endregion ImportSeedPhrase
// region ButtonClickHandlers
@Suppress("EmptyFunctionBlock")
private fun buttonCreateWalletClick() {
}
@Suppress("EmptyFunctionBlock")
private fun buttonCreateWalletWithSeedPhraseClick() {
}
@ -214,7 +201,7 @@ class SeedPhraseViewModel @Inject constructor(
private fun buttonGenerateSeedPhraseClick() {
viewModelScope.launchSingle {
updateUi { uiBuilder.generateMnemonicComponents(uiState) }
delay(300)
delay(DELAY_GENERATE_SEED_PHRASE)
interactor.generateMnemonic()
.onSuccess { mnemonic ->
val mnemonicGridItems = generateMnemonicGridList(mnemonic.mnemonicComponents)
@ -234,18 +221,20 @@ class SeedPhraseViewModel @Inject constructor(
val mnemonicGridItems = mutableListOf<MnemonicGridItem>()
for (index in 0 until splitIndex) {
if (index <= leftColumn.size) mnemonicGridItems.add(
MnemonicGridItem(
if (index <= leftColumn.size) {
val item = MnemonicGridItem(
index = index + 1,
mnemonic = leftColumn[index],
),
)
if (index < rightColumn.size) mnemonicGridItems.add(
MnemonicGridItem(
)
mnemonicGridItems.add(item)
}
if (index < rightColumn.size) {
val item = MnemonicGridItem(
index = index + splitIndex + 1,
mnemonic = rightColumn[index],
),
)
)
mnemonicGridItems.add(item)
}
}
return mnemonicGridItems.toImmutableList()
}
@ -324,5 +313,6 @@ class SeedPhraseViewModel @Inject constructor(
companion object {
private const val MNEMONIC_DEBOUNCER = "MnemonicDebouncer"
private const val MNEMONIC_DEBOUNCE_DELAY = 700L
private const val DELAY_GENERATE_SEED_PHRASE = 300L
}
}