Updated on 2026-08-14

This commit is contained in:
Tangem 2023-04-16 21:54:28 +03:00
parent 8f845111f3
commit 3a20cd143c
3 changed files with 105 additions and 0 deletions

View file

@ -0,0 +1,49 @@
package com.tangem.feature.onboarding.domain
import com.tangem.utils.extensions.isNotWhitespace
/**
[REDACTED_AUTHOR]
*/
internal class PartWordFinder {
fun getLeadPartOfWord(text: String, cursorPosition: Int): String? {
val charText = text.toCharArray().asList()
if (cursorPosition > charText.size || cursorPosition < 0) {
// cursorPosition is out of text size
return null
}
if (cursorPosition == 0) {
// cursorPosition at start of text
return null
}
if (charText[cursorPosition - 1].isWhitespace()) {
// white space to the left of cursorPosition
return null
}
if (charText.size > cursorPosition && charText[cursorPosition].isNotWhitespace()) {
// cursorPosition in the middle of a word
return null
}
for (charIndex in cursorPosition - 1 downTo 0) {
val char = charText[charIndex]
when {
charIndex == 0 -> {
return text.substring(charIndex, cursorPosition)
}
char.isWhitespace() -> {
return text.substring(charIndex + 1, cursorPosition)
}
}
}
return null
}
fun getLastPartOfWord(text: String, word: String, cursorPosition: Int): String {
val leadPart = getLeadPartOfWord(text, cursorPosition) ?: return ""
return word.substring(leadPart.length, word.length)
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.feature.onboarding.presentation.wallet2.ui
import androidx.compose.ui.text.TextRange
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
@ -42,6 +44,26 @@ class ImportSeedPhraseStateBuilder {
),
)
fun updateSuggestions(
uiState: OnboardingSeedPhraseState,
suggestions: List<String>,
): OnboardingSeedPhraseState = uiState.copy(
importSeedPhraseState = uiState.importSeedPhraseState.copy(
suggestionsList = suggestions,
),
)
fun insertSuggestionWord(
uiState: OnboardingSeedPhraseState,
insertResult: InsertSuggestionResult,
): OnboardingSeedPhraseState {
val newTextFieldValue = uiState.importSeedPhraseState.tvSeedPhrase.textFieldValue.copy(
text = insertResult.text,
selection = TextRange(insertResult.cursorPosition, insertResult.cursorPosition),
)
return updateTextField(uiState, newTextFieldValue)
}
fun updateError(
uiState: OnboardingSeedPhraseState,
error: SeedPhraseError?,

View file

@ -31,6 +31,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
/**
@ -51,6 +52,7 @@ class SeedPhraseViewModel @Inject constructor(
get() = uiState.step
private val textFieldsDebouncers = mutableMapOf<String, Debouncer>()
private var suggestionWordInserted: AtomicBoolean = AtomicBoolean(false)
override fun onCleared() {
textFieldsDebouncers.forEach { entry -> entry.value.release() }
@ -138,18 +140,25 @@ class SeedPhraseViewModel @Inject constructor(
val inputMnemonic = fieldState.textFieldValue.text
when {
suggestionWordInserted.getAndSet(false) -> {
validateMnemonic(inputMnemonic)
return@launchSingle
}
isSameText && !isCursorMoved -> {
return@launchSingle
}
isSameText && isCursorMoved -> {
updateSuggestions(fieldState)
return@launchSingle
}
}
val isPasteFromClipboard = textFieldValue.text.length - oldTextFieldValue.text.length > 1
if (isPasteFromClipboard) {
updateSuggestions(fieldState)
validateMnemonic(inputMnemonic)
} else {
updateSuggestions(fieldState)
val debouncer = createOrGetDebouncer(MNEMONIC_DEBOUNCER)
debouncer.debounce(viewModelScope, MNEMONIC_DEBOUNCE_DELAY, dispatchers.io) {
validateMnemonic(inputMnemonic)
@ -180,6 +189,15 @@ class SeedPhraseViewModel @Inject constructor(
updateUi { uiBuilder.importSeedPhrase.updateError(mediateState, error) }
}
}
private suspend fun updateSuggestions(fieldState: TextFieldState) {
val suggestions = interactor.getSuggestions(
text = fieldState.textFieldValue.text,
hasSelection = !fieldState.textFieldValue.selection.collapsed,
cursorPosition = fieldState.textFieldValue.selection.end,
)
updateUi { uiBuilder.importSeedPhrase.updateSuggestions(uiState, suggestions) }
}
// endregion ImportSeedPhrase
// region ButtonClickHandlers
@ -250,6 +268,22 @@ class SeedPhraseViewModel @Inject constructor(
}
private fun buttonSuggestedPhraseClick(suggestionIndex: Int) {
viewModelScope.launchSingle {
suggestionWordInserted.set(true)
val textFieldValue = uiState.importSeedPhraseState.tvSeedPhrase.textFieldValue
val word = uiState.importSeedPhraseState.suggestionsList[suggestionIndex]
val cursorPosition = textFieldValue.selection.end
val insertResult = interactor.insertSuggestionWord(
text = textFieldValue.text,
suggestion = word,
cursorPosition = cursorPosition,
)
updateUi {
val mediateState = uiBuilder.importSeedPhrase.insertSuggestionWord(uiState, insertResult)
uiBuilder.importSeedPhrase.updateSuggestions(mediateState, emptyList())
}
}
}
private fun menuChatClick() {