diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 79fee709bf..fb391f8164 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -22,7 +22,6 @@ import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.config.FeaturesLocalLoader import com.tangem.datasource.config.models.Config import com.tangem.datasource.connection.NetworkConnectionManager -import com.tangem.domain.DomainLayer import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.common.LogConfig @@ -210,7 +209,6 @@ class TapApplication : Application(), ImageLoaderFactory { activityResultCaller = foregroundActivityObserver registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks) - DomainLayer.init() preferencesStorage = preferencesDataSource walletConnectRepository = WalletConnectRepository(this) diff --git a/app/src/main/java/com/tangem/tap/common/CompositionCounter.kt b/app/src/main/java/com/tangem/tap/common/CompositionCounter.kt deleted file mode 100644 index b09112d39f..0000000000 --- a/app/src/main/java/com/tangem/tap/common/CompositionCounter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.tangem.tap.common - -import timber.log.Timber - -class CompositionCounter( - val id: String, - count: Int = 0, -) { - var count: Int = count - private set - - fun increase(id: String): CompositionCounter { - if (this.id != id) return this - - count += 1 - return CompositionCounter(id, count) - } -} - -class CompositionLogger( - private val recomposeViewId: String, - private val tag: String = recomposeViewId, - private var turnOnForIds: List = listOf(recomposeViewId), -) { - val count: Int - get() = compositionCounter.count - - private var compositionCounter: CompositionCounter = CompositionCounter(recomposeViewId) - - fun nextComposition() { - compositionCounter = compositionCounter.increase(recomposeViewId) - log("") - } - - fun log(message: String) { - if (!turnOnForIds.contains(recomposeViewId)) return - - Timber.d("$tag[$recomposeViewId]:[${compositionCounter.count}]: $message") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt b/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt deleted file mode 100644 index 59cf01d88c..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/OutlinedTextFieldWidget.kt +++ /dev/null @@ -1,277 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.animateContentSize -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.LinearProgressIndicator -import androidx.compose.material.OutlinedTextField -import androidx.compose.material.Text -import androidx.compose.material.TextFieldColors -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.tangem.common.module.ModuleError -import com.tangem.core.ui.res.TangemTheme -import com.tangem.domain.common.form.Field -import com.tangem.tap.common.CompositionLogger -import com.tangem.tap.common.compose.extensions.stringResourceDefault -import com.tangem.tap.domain.moduleMessage.ModuleMessageConverter - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun OutlinedTextFieldWidget( - fieldData: Field.Data, - labelId: Int? = null, - label: String = "", - placeholderId: Int? = null, - placeholder: String = "", - trailingIcon: @Composable (() -> Unit)? = null, - isEnabled: Boolean = true, - isVisible: Boolean = true, - isLoading: Boolean = false, - error: ModuleError? = null, - errorConverter: ModuleMessageConverter? = null, - debounceTextChanges: Long = 400, - visualTransformation: VisualTransformation = VisualTransformation.None, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - onTextChange: (String) -> Unit, -) { - if (!isVisible) return - - Column(modifier = Modifier.animateContentSize()) { - OutlinedProgressTextField( - fieldData = fieldData, - label = stringResourceDefault(labelId, label), - placeholder = stringResourceDefault(placeholderId, placeholder), - trailingIcon = trailingIcon, - isEnabled = isEnabled, - isLoading = isLoading, - error = error, - debounce = debounceTextChanges, - visualTransformation = visualTransformation, - keyboardOptions = keyboardOptions, - onTextChange = onTextChange, - ) - errorConverter?.let { AnimatedErrorView(errorConverter = it, error = error) } - } -} - -@Suppress("LongMethod", "NestedBlockDepth", "MagicNumber", "MaxLineLength") -@Composable -private fun OutlinedProgressTextField( - fieldData: Field.Data, - label: String = "", - placeholder: String = "", - isEnabled: Boolean = true, - isLoading: Boolean = false, - error: ModuleError? = null, - debounce: Long = 400, - visualTransformation: VisualTransformation = VisualTransformation.None, - keyboardOptions: KeyboardOptions = KeyboardOptions.Default, - colors: TextFieldColors = TangemTextFieldsDefault.defaultTextFieldColors, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - trailingIcon: @Composable (() -> Unit)? = null, - onTextChange: (String) -> Unit, -) { - val logger = remember { - CompositionLogger(label, "OutlinedProgressTextField", listOf("Символ токена")) - } - logger.nextComposition() - - val textValueState = remember { mutableStateOf(fieldData.value) } - val textDebouncer = valueDebouncerAsState( - initialValue = fieldData.value, - debounce = debounce, - onEmitValueReceive = { - logger.log("DEBOUNCER: onEmitValueReceived: [$it]") - logger.log("DEBOUNCER: start RECOMPOSE by new value for textValueState.value = [$it]") - textValueState.value = it - }, - onValueChange = { - logger.log("DEBOUNCER: onValueChanged: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> dispatch.toStore([$it])") - onTextChange(it) - }, - ) - - logger.log("RECOMPOSE ---------------------------------------------------------------START [${logger.count}]") - logger.log("RECOMPOSE --data: fieldData.value: [$fieldData]") - logger.log("RECOMPOSE --data: textValueState.value: [${textValueState.value}]") - logger.log("RECOMPOSE --data: textDebouncer.emittedValue = [${textDebouncer.emittedValue}]") - logger.log("RECOMPOSE --data: textDebouncer.debounced = [${textDebouncer.debounced}]") - - if (!fieldData.isUserInput) { - // initial value is not from an user - val isNotUserInput = "-- IS NOT USER INPUT" - logger.log("recompose $isNotUserInput") - if (textValueState.value == fieldData.value) { - logger.log("$isNotUserInput: внешние данные ОДИНАКОВЫ с данными в поле") - } else { - logger.log("$isNotUserInput: внешние данные РАЗЛИЧАЮТСЯ с данными в поле") - if (textDebouncer.emittedValue != textDebouncer.debounced || textDebouncer.emitsCountBeforeDebounce > 0) { - logger.log("$isNotUserInput: пользователь ВВОДИТ данные -> внешние данные игнорируем, ждем RECOMPOSE") - } else { - logger.log("$isNotUserInput: пользователь НЕ вводит данные -> пытаемся обработать внешние данные") - if (textValueState.value != textDebouncer.emittedValue || - textValueState.value != textDebouncer.debounced - ) { - logger.log("$isNotUserInput: даннные в поле не соответствуют данным из textDebouncer") - if (textDebouncer.emittedValue.isEmpty() && textDebouncer.debounced.isEmpty()) { - logger.log( - "$isNotUserInput: даннные в textDebouncer ПУСТЫ -> start RECOMPOSE новые данные для " + - "textValueState.value = [${fieldData.value}]", - ) - textValueState.value = fieldData.value - } else { - logger.log( - "$isNotUserInput: даннные в textDebouncer НЕ ПУСТЫ -> start RECOMPOSE новые данные для " + - "textValueState.value = [${fieldData.value}]", - ) - textValueState.value = fieldData.value - } - } else { - logger.log( - "$isNotUserInput: в пустое поле вставляются данные -> start RECOMPOSE новые данные для " + - "textValueState.value = [${fieldData.value}]", - ) - textValueState.value = fieldData.value - } - } - } - } - logger.log("recompose --------------------------------------------------------------FINISH [${logger.count}]") - - Box { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth(), - value = textValueState.value, - onValueChange = { - logger.log("WIDGET: textDebouncer.emmit([$it])") - textDebouncer.emmit(it) - }, - keyboardOptions = keyboardOptions, - label = { - Text( - text = label, - style = TangemTheme.typography.caption, - color = colors.labelColor( - enabled = isEnabled, - error = error != null, - interactionSource = interactionSource, - ).value, - ) - }, - placeholder = { - Text( - text = placeholder, - style = TangemTheme.typography.body1, - color = colors.placeholderColor(enabled = isEnabled).value, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - }, - trailingIcon = trailingIcon, - singleLine = true, - enabled = isEnabled, - isError = error != null, - visualTransformation = visualTransformation, - colors = colors, - interactionSource = interactionSource, - ) - AnimatedVisibility( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter) - .padding(start = 6.dp, top = 0.dp, end = 6.dp, bottom = 6.dp), - visible = isLoading, - ) { - LinearProgressIndicator( - color = TangemTheme.colors.icon.primary1, - ) - } - } -} - -@Composable -private fun AnimatedErrorView(errorConverter: ModuleMessageConverter, error: ModuleError? = null) { - AnimatedVisibility( - visible = error != null, - enter = fadeIn() + slideInVertically(), - exit = slideOutVertically() + fadeOut(), - ) { - error?.let { - ErrorView( - text = errorConverter.convert(it).message, - style = TextStyle(fontSize = 14.sp), - ) - } - } -} - -@Preview -@Composable -private fun OutlinedTextFieldWithErrorTest() { - val context = LocalContext.current - val converter = remember { ModuleMessageConverter(context) } - - class SimpleError( - override val code: Int = 1, - override val message: String = "Error message", - override val data: Any? = null, - ) : ModuleError() - - val modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - Column { - OutlinedTextFieldWidget( - fieldData = Field.Data("", false), - label = "First label", - placeholder = "1 placeholder", - error = null, - errorConverter = converter, - ) {} - OutlinedTextFieldWidget( - fieldData = Field.Data("First", false), - label = "First label", - placeholder = "1 placeholder", - error = null, - errorConverter = converter, - ) {} - OutlinedTextFieldWidget( - fieldData = Field.Data("First", false), - label = "First label", - placeholder = "1 placeholder", - isLoading = true, - error = null, - errorConverter = converter, - ) {} - OutlinedTextFieldWidget( - fieldData = Field.Data("First", false), - label = "First label", - placeholder = "1 placeholder", - error = SimpleError(), - errorConverter = converter, - ) {} - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/Undefined.kt b/app/src/main/java/com/tangem/tap/common/compose/Undefined.kt deleted file mode 100644 index d5e5168be3..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/Undefined.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.foundation.layout.Column -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.sp - -/** -[REDACTED_AUTHOR] - * Compose views are not typically used as a main or base view. - */ - -@Composable -fun TitleSubtitle(title: String, subtitle: String) { - Column { - Text(text = title) - Text( - text = subtitle, - fontSize = 12.sp, - color = Color.Gray, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/Resources.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/Resources.kt deleted file mode 100644 index 5b8da92e24..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/Resources.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.common.compose.extensions - -import android.content.res.Resources -import androidx.annotation.StringRes -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun stringResourceDefault(@StringRes id: Int?, default: String = ""): String { - val resources = LocalContext.current.resources - return try { - resources.getString(requireNotNull(id)) - } catch (ex: Resources.NotFoundException) { - default - } catch (ex: IllegalArgumentException) { - default - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 9119734bfd..f90e296e22 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -6,7 +6,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.FragmentShareTransition import com.tangem.feature.referral.ReferralFragment import com.tangem.feature.swap.presentation.SwapFragment -import com.tangem.tap.features.customtoken.legacy.AddCustomTokenFragment +import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment import com.tangem.tap.features.details.ui.appsettings.AppSettingsFragment import com.tangem.tap.features.details.ui.cardsettings.CardSettingsFragment import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRecoveryFragment @@ -33,7 +33,6 @@ import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.wallet.R import timber.log.Timber -import com.tangem.tap.features.customtoken.impl.presentation.AddCustomTokenFragment as RedesignedAddCustomTokenFragment fun FragmentActivity.openFragment( screen: AppScreen, @@ -155,18 +154,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.AccessCodeRecovery -> AccessCodeRecoveryFragment() AppScreen.Disclaimer -> DisclaimerFragment() AppScreen.AddTokens -> TokensListFragment() - - AppScreen.AddCustomToken -> { - val featureToggles = store.state.daggerGraphState.get( - getDependency = DaggerGraphState::customTokenFeatureToggles, - ) - if (featureToggles.isRedesignedScreenEnabled) { - RedesignedAddCustomTokenFragment() - } else { - AddCustomTokenFragment() - } - } - + AppScreen.AddCustomToken -> AddCustomTokenFragment() AppScreen.WalletDetails -> { val featureToggles = store.state.daggerGraphState.get( getDependency = DaggerGraphState::tokenDetailsFeatureToggles, diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ValueCallback.kt b/app/src/main/java/com/tangem/tap/common/extensions/ValueCallback.kt deleted file mode 100644 index 3dd4357bba..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/ValueCallback.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.tangem.tap.common.extensions - -/** -[REDACTED_AUTHOR] - */ -typealias ValueCallback = (T) -> Unit \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/api/featuretoggles/CustomTokenFeatureToggles.kt b/app/src/main/java/com/tangem/tap/features/customtoken/api/featuretoggles/CustomTokenFeatureToggles.kt index b64f9efd14..a6894e04b1 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/api/featuretoggles/CustomTokenFeatureToggles.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/api/featuretoggles/CustomTokenFeatureToggles.kt @@ -7,8 +7,5 @@ package com.tangem.tap.features.customtoken.api.featuretoggles */ interface CustomTokenFeatureToggles { - /** Availability of redesigned screen (internal feature) */ - val isRedesignedScreenEnabled: Boolean - val isNewCardScanningEnabled: Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/featuretoggles/DefaultCustomTokenFeatureToggles.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/featuretoggles/DefaultCustomTokenFeatureToggles.kt index a19dde5403..cd06665b00 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/featuretoggles/DefaultCustomTokenFeatureToggles.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/featuretoggles/DefaultCustomTokenFeatureToggles.kt @@ -14,9 +14,6 @@ internal class DefaultCustomTokenFeatureToggles( private val featureTogglesManager: FeatureTogglesManager, ) : CustomTokenFeatureToggles { - override val isRedesignedScreenEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(name = "REDESIGNED_CUSTOM_TOKEN_SCREEN_ENABLED") - override val isNewCardScanningEnabled: Boolean get() = featureTogglesManager.isFeatureEnabled(name = "NEW_CARD_SCANNING_ENABLED") } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/AddCustomTokenFragment.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/AddCustomTokenFragment.kt deleted file mode 100644 index 204f95ec25..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/AddCustomTokenFragment.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy - -import android.os.Bundle -import android.view.View -import android.view.WindowManager -import androidx.appcompat.widget.Toolbar -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.ComposeView -import com.tangem.core.analytics.Analytics -import com.tangem.core.ui.res.TangemTheme -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState -import com.tangem.domain.redux.domainStore -import com.tangem.tap.common.analytics.events.ManageTokens -import com.tangem.tap.features.BaseStoreFragment -import com.tangem.tap.features.FragmentOnBackPressedHandler -import com.tangem.tap.features.addBackPressHandler -import com.tangem.tap.features.customtoken.legacy.compose.AddCustomTokenScreen -import com.tangem.tap.features.customtoken.legacy.compose.ClosePopupTrigger -import com.tangem.wallet.R -import org.rekotlin.StoreSubscriber - -/** -[REDACTED_AUTHOR] - */ -class AddCustomTokenFragment : BaseStoreFragment(R.layout.view_compose_fragment), StoreSubscriber { - - private var state: MutableState = mutableStateOf(domainStore.state.addCustomTokensState) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - Analytics.send(ManageTokens.CustomToken.ScreenOpened) - } - - override fun subscribeToStore() { - domainStore.subscribe(this) { state -> - state.skipRepeats { oldState, newState -> - oldState.addCustomTokensState == newState.addCustomTokensState - }.select { it.addCustomTokensState } - } - } - - override fun newState(state: AddCustomTokenState) { - if (activity == null || view == null) return - - this.state.value = state - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) - view.findViewById(R.id.toolbar)?.setTitle(R.string.add_custom_token_title) - - val closePopupTrigger = initClosingPopupTriggerEvent() - view.findViewById(R.id.view_compose)?.setContent { - TangemTheme { - Box( - modifier = Modifier - .fillMaxSize(), - ) { - AddCustomTokenScreen(state, closePopupTrigger) - } - } - } - } - - private fun initClosingPopupTriggerEvent(): ClosePopupTrigger = ClosePopupTrigger().apply { - onCloseComplete = ::handleOnBackPressed - addBackPressHandler( - object : FragmentOnBackPressedHandler { - override fun handleOnBackPressed() = close() - }, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt deleted file mode 100644 index baa487cec7..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/AddCustomTokenScreen.kt +++ /dev/null @@ -1,208 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.PrimaryButtonIconStart -import com.tangem.core.ui.components.keyboardAsState -import com.tangem.core.ui.res.TangemTheme -import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.common.form.DataField -import com.tangem.domain.common.form.FieldId -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState -import com.tangem.domain.features.addCustomToken.redux.ScreenState -import com.tangem.domain.features.addCustomToken.redux.ViewStates -import com.tangem.domain.redux.domainStore -import com.tangem.tap.common.compose.AddCustomTokenWarning -import com.tangem.tap.domain.moduleMessage.ModuleMessageConverter -import com.tangem.tap.features.customtoken.legacy.compose.test.TestCase -import com.tangem.tap.features.customtoken.legacy.compose.test.TestCasesList -import com.tangem.wallet.R -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterialApi::class) -@Composable -fun AddCustomTokenScreen(state: MutableState, closePopupTrigger: ClosePopupTrigger) { - val selectedTestCase = remember { mutableStateOf(TestCase.ContractAddress) } - - val bottomSheetScaffoldState = rememberBottomSheetScaffoldState( - bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed), - ) - val coroutineScope = rememberCoroutineScope() - val toggleBottomSheet = { coroutineScope.launch { bottomSheetScaffoldState.toggle() } } - - BottomSheetScaffold( - scaffoldState = bottomSheetScaffoldState, - sheetContent = { - Surface(color = colorResource(id = R.color.lightGray5)) { - selectedTestCase.value.content(toggleBottomSheet) - } - }, - sheetPeekHeight = 0.dp, - ) { - Column { - TestCasesList( - onItemClick = { - selectedTestCase.value = it - toggleBottomSheet() - }, - ) - ScreenContent(state, closePopupTrigger) - } - } - - ComposeDialogManager() - LaunchedEffect(key1 = Unit, block = { domainStore.dispatch(AddCustomTokenAction.OnCreate) }) - DisposableEffect(key1 = Unit, effect = { onDispose { domainStore.dispatch(AddCustomTokenAction.OnDestroy) } }) -} - -@OptIn(ExperimentalMaterialApi::class) -private suspend fun BottomSheetScaffoldState.toggle() { - if (bottomSheetState.isCollapsed) { - bottomSheetState.expand() - } else { - bottomSheetState.collapse() - } -} - -@Composable -private fun ScreenContent(state: MutableState, closePopupTrigger: ClosePopupTrigger) { - val scaffoldState = rememberScaffoldState() - - Scaffold( - scaffoldState = scaffoldState, - backgroundColor = colorResource(id = R.color.backgroundLightGray), - floatingActionButton = { - HangingOverKeyboardView(keyboardState = keyboardAsState()) { - AddButton(state) - } - }, - floatingActionButtonPosition = FabPosition.Center, - ) { paddings -> - Box( - modifier = Modifier - .padding(paddings) - .fillMaxSize(), - ) { - LazyColumn( - contentPadding = PaddingValues(bottom = 90.dp), - ) { - item { - Surface( - modifier = Modifier.padding(16.dp), - shape = MaterialTheme.shapes.small, - elevation = 4.dp, - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - ) { - FormFields(state, closePopupTrigger) - } - } - } - item { Warnings(state.value.warnings.toList()) } - } - } - } -} - -@Composable -private fun FormFields(state: MutableState, closePopupTrigger: ClosePopupTrigger) { - val context = LocalContext.current - val errorConverter = remember { ModuleMessageConverter(context) } - - val stateValue = state.value - stateValue.form.fieldList.forEach { field -> - val data = ScreenFieldData.fromState(field, stateValue, errorConverter) - when (field.id) { - ContractAddress -> TokenContractAddressView(data) - Network -> TokenNetworkView(data, stateValue, closePopupTrigger) - Name -> TokenNameView(data) - Symbol -> TokenSymbolView(data) - Decimals -> TokenDecimalsView(data) - DerivationPath -> TokenDerivationPathView(data, stateValue, closePopupTrigger) - } - } -} - -@Composable -fun Warnings(warnings: List) { - if (warnings.isEmpty()) return - - val context = LocalContext.current - val warningConverter = remember { ModuleMessageConverter(context) } - - Column { - warnings.forEachIndexed { index, item -> - val modifier = when (index) { - 0 -> Modifier.padding(vertical = 0.dp) - warnings.lastIndex -> Modifier.padding(top = 8.dp, bottom = 16.dp) - else -> Modifier.padding(top = 8.dp, bottom = 0.dp) - } - AddCustomTokenWarning( - modifier = modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - warning = item, - converter = warningConverter, - ) - } - } -} - -@Composable -private fun AddButton(state: MutableState) { - PrimaryButtonIconStart( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - text = stringResource(id = R.string.common_add), - iconResId = R.drawable.ic_plus_24, - enabled = state.value.screenState.addButton.isEnabled, - onClick = { domainStore.dispatch(AddCustomTokenAction.OnAddCustomTokenClicked) }, - ) -} - -data class ScreenFieldData( - val field: DataField<*>, - val error: AddCustomTokenError?, - val errorConverter: ModuleMessageConverter, - val viewState: ViewStates.TokenField, -) { - companion object { - fun fromState( - field: DataField<*>, - state: AddCustomTokenState, - errorConverter: ModuleMessageConverter, - ): ScreenFieldData { - return ScreenFieldData( - field = field, - error = state.getError(field.id), - errorConverter = errorConverter, - viewState = selectField(field.id, state.screenState), - ) - } - - private fun selectField(id: FieldId, screenState: ScreenState): ViewStates.TokenField { - return when (id) { - ContractAddress -> screenState.contractAddressField - Network -> screenState.network - Name -> screenState.name - Symbol -> screenState.symbol - Decimals -> screenState.decimals - DerivationPath -> screenState.derivationPath - else -> throw UnsupportedOperationException() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/BlockchainSpinner.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/BlockchainSpinner.kt deleted file mode 100644 index d6c60c2b26..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/BlockchainSpinner.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import com.tangem.blockchain.common.Blockchain -import com.tangem.domain.common.form.Field - -@Composable -fun BlockchainSpinner( - @StringRes title: Int, - itemList: List, - selectedItem: Field.Data, - isEnabled: Boolean = true, - textFieldConverter: (Blockchain) -> String, - dropdownItemView: @Composable ((Blockchain) -> Unit)? = null, - closePopupTrigger: ClosePopupTrigger = ClosePopupTrigger(), - onItemSelect: (Blockchain) -> Unit, -) { - OutlinedSpinner( - modifier = Modifier.fillMaxWidth(), - label = stringResource(id = title), - itemList = itemList, - selectedItem = selectedItem, - textFieldConverter = textFieldConverter, - dropdownItemView = dropdownItemView, - isEnabled = isEnabled, - onItemSelected = onItemSelect, - closePopupTrigger = closePopupTrigger, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/ComposeDialogManager.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/ComposeDialogManager.kt deleted file mode 100644 index 453ace8c9e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/ComposeDialogManager.kt +++ /dev/null @@ -1,157 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material.AlertDialog -import androidx.compose.material.Button -import androidx.compose.material.LocalTextStyle -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import com.tangem.core.ui.components.SpacerH16 -import com.tangem.core.ui.res.TangemTheme -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.domain.DomainDialog -import com.tangem.domain.redux.domainStore -import com.tangem.domain.redux.global.DomainGlobalAction -import com.tangem.domain.redux.global.DomainGlobalState -import com.tangem.tap.domain.moduleMessage.ModuleMessageConverter -import com.tangem.wallet.R -import org.rekotlin.StoreSubscriber - -@Composable -internal fun ComposeDialogManager() { - val dialogSate = remember { mutableStateOf(null) } - val subscriber = remember { - object : StoreSubscriber { - override fun newState(state: DomainGlobalState) { - dialogSate.value = state.dialog - } - } - } - - ShowTheDialog(dialogSate) - - LaunchedEffect( - key1 = Unit, - block = { - domainStore.subscribe(subscriber) { state -> - state.skipRepeats { oldState, newState -> - oldState.globalState == newState.globalState - }.select { it.globalState } - } - }, - ) - DisposableEffect( - key1 = Unit, - effect = { - onDispose { domainStore.unsubscribe(subscriber) } - }, - ) -} - -@Composable -private fun ShowTheDialog(dialogState: MutableState) { - if (dialogState.value == null) return - - val context = LocalContext.current - val errorConverter = remember { ModuleMessageConverter(context) } - val onDismissRequest = { domainStore.dispatch(DomainGlobalAction.ShowDialog(null)) } - - when (val dialog = dialogState.value) { - is DomainDialog.DialogError -> ErrorDialog( - title = stringResource(id = R.string.common_error), - body = errorConverter.convert(dialog.error).message, - onDismissRequest, - ) - is DomainDialog.SelectTokenDialog -> SelectTokenNetworkDialog(dialog, onDismissRequest) - else -> {} - } -} - -/** - * Dialog with single item selection - */ -@Composable -fun SimpleDialog( - title: String, - items: List, - onSelect: (CoinsResponse.Coin.Network) -> Unit, - onDismissRequest: () -> Unit, - itemContent: @Composable (CoinsResponse.Coin.Network) -> Unit, -) { - Dialog( - properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false), - onDismissRequest = { }, - ) { - Surface(modifier = Modifier.fillMaxWidth(), shape = MaterialTheme.shapes.medium) { - Column(modifier = Modifier.padding(TangemTheme.dimens.spacing22)) { - DialogTitle(title = title) - LazyColumn { - items(items = items, key = CoinsResponse.Coin.Network::networkId) { item -> - Row( - modifier = Modifier - .fillMaxWidth() - .height(56.dp) - .clickable { - onSelect(item) - onDismissRequest() - }, - verticalAlignment = Alignment.CenterVertically, - ) { itemContent(item) } - } - } - } - } - } -} - -@Composable -private fun DialogTitle(title: String) { - Text( - text = title, - style = LocalTextStyle.provides( - TextStyle( - fontWeight = FontWeight.Bold, - fontSize = 20.sp, - ), - ).value, - ) - SpacerH16() -} - -@Composable -fun ErrorDialog(title: String, body: String, onDismissRequest: () -> Unit) { - AlertDialog( - title = { DialogTitle(title) }, - text = { Text(body) }, - onDismissRequest = onDismissRequest, - confirmButton = { - Button(onClick = onDismissRequest) { - Text(text = stringResource(id = R.string.common_ok)) - } - }, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/FormFieldViews.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/FormFieldViews.kt deleted file mode 100644 index a640e91575..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/FormFieldViews.kt +++ /dev/null @@ -1,149 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.KeyboardType -import com.tangem.core.ui.components.SpacerH8 -import com.tangem.domain.common.form.Field -import com.tangem.domain.features.addCustomToken.TokenBlockchainField -import com.tangem.domain.features.addCustomToken.TokenDerivationPathField -import com.tangem.domain.features.addCustomToken.TokenField -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenContractAddressChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDecimalsChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDerivationPathChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNameChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNetworkChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenSymbolChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState -import com.tangem.domain.redux.domainStore -import com.tangem.tap.common.compose.OutlinedTextFieldWidget -import com.tangem.tap.common.compose.TitleSubtitle -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun TokenContractAddressView(screenFieldData: ScreenFieldData) { - if (!screenFieldData.viewState.isVisible) return - - val tokenField = screenFieldData.field as TokenField - - OutlinedTextFieldWidget( - fieldData = tokenField.data, - labelId = R.string.custom_token_contract_address_input_title, - placeholder = "0x0000000000000000000000000000000000000000", - isEnabled = screenFieldData.viewState.isEnabled, - isLoading = screenFieldData.viewState.isLoading, - error = screenFieldData.error, - errorConverter = screenFieldData.errorConverter, -// trailingIcon = { PasteClearButton(showFirst = tokenField.data.value.isEmpty()) } - ) { - domainStore.dispatch(OnTokenContractAddressChanged(Field.Data(it, true))) - } - SpacerH8() -} - -@Composable -fun TokenNameView(screenFieldData: ScreenFieldData) { - if (!screenFieldData.viewState.isVisible) return - - val tokenField = screenFieldData.field as TokenField - - OutlinedTextFieldWidget( - fieldData = tokenField.data, - labelId = R.string.custom_token_name_input_title, - placeholderId = R.string.custom_token_name_input_placeholder, - isEnabled = screenFieldData.viewState.isEnabled, - error = screenFieldData.error, - errorConverter = screenFieldData.errorConverter, - ) { - domainStore.dispatch(OnTokenNameChanged(Field.Data(it, true))) - } - SpacerH8() -} - -@Composable -fun TokenNetworkView( - screenFieldData: ScreenFieldData, - state: AddCustomTokenState, - closePopupTrigger: ClosePopupTrigger, -) { - if (!screenFieldData.viewState.isVisible) return - - val notSelected = stringResource(id = R.string.custom_token_network_input_not_selected) - val networkField = screenFieldData.field as TokenBlockchainField - - BlockchainSpinner( - title = R.string.custom_token_network_input_title, - itemList = networkField.itemList, - selectedItem = networkField.data, - isEnabled = screenFieldData.viewState.isEnabled, - textFieldConverter = { state.blockchainToName(it) ?: notSelected }, - closePopupTrigger = closePopupTrigger, - ) { domainStore.dispatch(OnTokenNetworkChanged(Field.Data(it, true))) } - SpacerH8() -} - -@Composable -fun TokenSymbolView(screenFieldData: ScreenFieldData) { - if (!screenFieldData.viewState.isVisible) return - - val tokenField = screenFieldData.field as TokenField - - OutlinedTextFieldWidget( - fieldData = tokenField.data, - labelId = R.string.custom_token_token_symbol_input_title, - placeholderId = R.string.custom_token_token_symbol_input_placeholder, - isEnabled = screenFieldData.viewState.isEnabled, - error = screenFieldData.error, - errorConverter = screenFieldData.errorConverter, - ) { domainStore.dispatch(OnTokenSymbolChanged(Field.Data(it, true))) } - SpacerH8() -} - -@Composable -fun TokenDecimalsView(screenFieldData: ScreenFieldData) { - if (!screenFieldData.viewState.isVisible) return - - val tokenField = screenFieldData.field as TokenField - - OutlinedTextFieldWidget( - fieldData = tokenField.data, - labelId = R.string.custom_token_decimals_input_title, - placeholder = "8", - isEnabled = screenFieldData.viewState.isEnabled, - error = screenFieldData.error, - errorConverter = screenFieldData.errorConverter, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - ) { domainStore.dispatch(OnTokenDecimalsChanged(Field.Data(it, true))) } - SpacerH8() -} - -@Composable -fun TokenDerivationPathView( - screenFieldData: ScreenFieldData, - state: AddCustomTokenState, - closePopupTrigger: ClosePopupTrigger, -) { - if (!screenFieldData.viewState.isVisible) return - - val notSelected = stringResource(id = R.string.custom_token_derivation_path_default) - val networkField = screenFieldData.field as TokenDerivationPathField - - BlockchainSpinner( - title = R.string.custom_token_derivation_path_input_title, - itemList = networkField.itemList, - selectedItem = networkField.data, - isEnabled = screenFieldData.viewState.isEnabled, - textFieldConverter = { state.blockchainToName(it) ?: notSelected }, - dropdownItemView = { blockchain -> - val derivationPathName = state.blockchainToName(blockchain, true) ?: notSelected - val blockchainName = state.blockchainToName(blockchain) ?: notSelected - TitleSubtitle(derivationPathName, blockchainName) - }, - closePopupTrigger = closePopupTrigger, - ) { domainStore.dispatch(OnTokenDerivationPathChanged(Field.Data(it, true))) } - SpacerH8() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/HangingOverKeyboardView.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/HangingOverKeyboardView.kt deleted file mode 100644 index 6aa2994210..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/HangingOverKeyboardView.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.Keyboard - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun HangingOverKeyboardView( - modifier: Modifier = Modifier, - keyboardState: State, - spaceBetweenKeyboard: Dp = 0.dp, - content: @Composable (BoxScope.() -> Unit), -) { - val padding = remember(keyboardState) { - when (val state = keyboardState.value) { - is Keyboard.Closed -> 0.dp - is Keyboard.Opened -> state.height + spaceBetweenKeyboard - } - } - - Box(modifier.padding(bottom = padding)) { content() } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/OutlinedSpinner.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/OutlinedSpinner.kt deleted file mode 100644 index a3ddaedf7e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/OutlinedSpinner.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import android.os.Handler -import android.os.Looper -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.key -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.core.os.postDelayed -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.VoidCallback -import com.tangem.domain.common.form.Field -import com.tangem.tap.common.compose.TangemTextFieldsDefault -import com.tangem.tap.common.extensions.ValueCallback - -/** -[REDACTED_AUTHOR] - */ -@Suppress("MagicNumber") -@OptIn(ExperimentalMaterialApi::class) -@Composable -internal fun OutlinedSpinner( - label: String, - itemList: List, - selectedItem: Field.Data, - onItemSelected: ValueCallback, - modifier: Modifier = Modifier, - textFieldConverter: (T) -> String = { it.toString() }, - dropdownItemView: @Composable ((T) -> Unit)? = null, - isEnabled: Boolean = true, - onClose: VoidCallback = {}, - closePopupTrigger: ClosePopupTrigger = ClosePopupTrigger(), -) { - val rIsExpanded = remember { mutableStateOf(false) } - val stateSelectedItem = remember { mutableStateOf(selectedItem.value) } - if (!selectedItem.isUserInput) { - stateSelectedItem.value = selectedItem.value - } - - val onDropDownItemSelectedInternal: (T) -> Unit = { - stateSelectedItem.value = it - rIsExpanded.value = false - onItemSelected(it) - } - val onDismissRequest = { - rIsExpanded.value = false - onClose() - } - - closePopupTrigger.close = { - onDismissRequest() - Handler(Looper.getMainLooper()).postDelayed(100) { - closePopupTrigger.onCloseComplete() - } - } - - ExposedDropdownMenuBox( - expanded = rIsExpanded.value, - onExpandedChange = { rIsExpanded.value = !rIsExpanded.value }, - ) { - OutlinedTextField( - modifier = modifier, - readOnly = true, - enabled = isEnabled, - value = textFieldConverter(stateSelectedItem.value), - onValueChange = {}, - label = { Text(label) }, - trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = rIsExpanded.value) }, - colors = TangemTextFieldsDefault.defaultTextFieldColors, - ) - - if (isEnabled) { - ExposedDropdownMenu(expanded = rIsExpanded.value, onDismissRequest = onDismissRequest) { - itemList.forEach { item -> - key(item) { - DropdownMenuItem(onClick = { onDropDownItemSelectedInternal(item) }) { - if (dropdownItemView == null) Text(textFieldConverter(item)) else dropdownItemView(item) - } - } - } - } - } - } -} - -class ClosePopupTrigger { - var close: () -> Unit = {} - var onCloseComplete: () -> Unit = {} -} - -@Preview -@Composable -private fun TestSpinnerPreview() { - OutlinedSpinner( - label = "Blockchain name", - itemList = listOf(Blockchain.values()), - selectedItem = Field.Data(Blockchain.Avalanche, false), - onItemSelected = {}, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/SelectTokenNetworkDialog.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/SelectTokenNetworkDialog.kt deleted file mode 100644 index 441969ad5e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/SelectTokenNetworkDialog.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose - -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import com.tangem.domain.DomainDialog -import com.tangem.tap.common.compose.TitleSubtitle -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun SelectTokenNetworkDialog(dialog: DomainDialog.SelectTokenDialog, onDismissRequest: () -> Unit) { - SimpleDialog( - title = stringResource(id = R.string.custom_token_network_input_title), - items = dialog.items, - onSelect = dialog.onSelect, - onDismissRequest = onDismissRequest, - ) { network -> TitleSubtitle(dialog.networkIdConverter(network.networkId), network.contractAddress ?: "") } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/test/ContractAddressTests.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/test/ContractAddressTests.kt deleted file mode 100644 index 3ba171f427..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/test/ContractAddressTests.kt +++ /dev/null @@ -1,120 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose.test - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.Button -import androidx.compose.material.Divider -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.VoidCallback -import com.tangem.domain.common.form.Field -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction -import com.tangem.domain.redux.domainStore - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun ContractAddressTests(onItemClick: VoidCallback) { - val casesInfo = listOf( - "USDC on ETH" to "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - "BUSD on ETH" to "0x4fabb145d64652a948d72533023f6e7a623c7c53", - "ETH on AVALANCHE" to "0xf20d962a6c8f70c731bd838a3a388d7d48fa6e15", - "USDC on ETH (invalid - cut address)" to "0xa0b86991c6218b36c1d1", - "Custom EVM" to "0x1111111111111111112111111111111111111113", - "Supported by several networks" to "0xa1faa113cbe53436df28ff0aee54275c13b40975", - "Invalid" to "!@#_ _-%%^&&*((){P P2iOWsdfFQLA", - ) - CasesListContent(casesInfo, onItemClick) -} - -@Composable -fun SolanaAddressTests(onItemClick: VoidCallback) { - val casesInfo = listOf( - "USDT (full)" to "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", - "USDT (valid - 2/3 of address)" to "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8Ben", - "USDT (invalid - 1/3 of address)" to "Es9vMFrzaCERmJ", - "ETH (full)" to "2FPyTwcZLUg1MDrwsyoP4D6s1tM7hAkHYRjkNb5w6Pxk", - ) - CasesListContent(casesInfo, onItemClick) -} - -@Composable -private fun CasesListContent(casesList: List>, onItemClick: VoidCallback) { - LazyColumn( - content = { - item { - Row { - ResetContractAddressButton(onItemClick) - Text("", modifier = Modifier.weight(1f)) - ResetAllFieldsButton(onItemClick) - } - Divider() - } - items(casesList.size) { - val (info, address) = casesList[it] - ContractAddressButton(info, address, onItemClick) - } - }, - ) -} - -@Composable -fun ResetAllFieldsButton(onItemClick: VoidCallback) { - ActionButton( - name = "Reset", - onClick = { - onItemClick() - domainStore.dispatch(AddCustomTokenAction.OnTokenContractAddressChanged(Field.Data("", false))) - domainStore.dispatch(AddCustomTokenAction.OnTokenNetworkChanged(Field.Data(Blockchain.Unknown, false))) - domainStore.dispatch(AddCustomTokenAction.OnTokenNameChanged(Field.Data("", false))) - domainStore.dispatch(AddCustomTokenAction.OnTokenSymbolChanged(Field.Data("", false))) - domainStore.dispatch(AddCustomTokenAction.OnTokenDecimalsChanged(Field.Data("", false))) - domainStore.dispatch( - AddCustomTokenAction.OnTokenDerivationPathChanged( - Field.Data( - Blockchain.Unknown, - false, - ), - ), - ) - }, - ) -} - -@Composable -fun ResetContractAddressButton(onItemClick: VoidCallback) { - ActionButton( - name = "Set empty address", - onClick = { - onItemClick() - domainStore.dispatch(AddCustomTokenAction.OnTokenContractAddressChanged(Field.Data("", false))) - }, - ) -} - -@Composable -private fun ContractAddressButton(name: String, address: String, onItemClick: VoidCallback) { - ActionButton( - modifier = Modifier.fillMaxWidth(), - name = name, - onClick = { - onItemClick() - domainStore.dispatch(AddCustomTokenAction.OnTokenContractAddressChanged(Field.Data(address, false))) - }, - ) -} - -@Composable -fun ActionButton(name: String, onClick: () -> Unit, modifier: Modifier = Modifier) { - Button( - modifier = modifier.padding(horizontal = 8.dp), - onClick = onClick, - ) { Text(name, fontSize = 12.sp) } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/test/TestCasesList.kt b/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/test/TestCasesList.kt deleted file mode 100644 index 3bae26e193..0000000000 --- a/app/src/main/java/com/tangem/tap/features/customtoken/legacy/compose/test/TestCasesList.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.features.customtoken.legacy.compose.test - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.material.Button -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.unit.dp -import com.tangem.common.extensions.VoidCallback -import com.tangem.wallet.BuildConfig -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun TestCasesList(onItemClick: (TestCase) -> Unit) { - if (!BuildConfig.TEST_ACTION_ENABLED) return - - Surface(color = colorResource(id = R.color.lightGray5)) { - Column(Modifier.padding(horizontal = 16.dp)) { - listOf(TestCase.ContractAddress, TestCase.SolanaTokens) - .map { case -> TestCaseListItem(testCase = case, onItemClick = { onItemClick(case) }) } - } - } -} - -@Composable -fun TestCaseListItem(testCase: TestCase, onItemClick: () -> Unit) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = Modifier.weight(1f), - text = testCase.description, - ) - Button( - onClick = onItemClick, - ) { Text("Start") } - } -} - -enum class TestCase(val description: String, val content: @Composable (VoidCallback) -> Unit) { - ContractAddress("Test contract address field", { ContractAddressTests(it) }), - Auto("Test contract address field", { ContractAddressTests(it) }), - SolanaTokens("Test Solana contract addresses", { SolanaAddressTests(it) }), - ; -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/di/TokensListRouterModule.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/di/TokensListRouterModule.kt index 07a7a3412d..2a7da5cca4 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/di/TokensListRouterModule.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/di/TokensListRouterModule.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.tokens.impl.di -import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles import com.tangem.tap.features.tokens.impl.presentation.router.DefaultTokensListRouter import com.tangem.tap.features.tokens.impl.presentation.router.TokensListRouter import dagger.Module @@ -18,7 +17,5 @@ internal object TokensListRouterModule { @Provides @ViewModelScoped - fun provideTokensListRouter(customTokenFeatureToggles: CustomTokenFeatureToggles): TokensListRouter { - return DefaultTokensListRouter(customTokenFeatureToggles) - } + fun provideTokensListRouter(): TokensListRouter = DefaultTokensListRouter() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt index 93ae4e1b17..68b277ea7e 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt @@ -6,8 +6,6 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles -import com.tangem.tap.features.tokens.legacy.redux.TokensAction import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -18,20 +16,14 @@ import com.tangem.wallet.R * [REDACTED_AUTHOR] */ -internal class DefaultTokensListRouter( - private val customTokenFeatureToggles: CustomTokenFeatureToggles, -) : TokensListRouter { +internal class DefaultTokensListRouter : TokensListRouter { override fun popBackStack() { store.dispatch(NavigationAction.PopBackTo()) } override fun openAddCustomTokenScreen() { - if (customTokenFeatureToggles.isRedesignedScreenEnabled) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomToken)) - } else { - store.dispatch(TokensAction.PrepareAndNavigateToAddCustomToken) - } + store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomToken)) } override fun showAddressCopiedNotification() { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt index c24f982fca..fb79c7bbde 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt @@ -17,7 +17,4 @@ sealed interface TokensAction : Action { // TODO: [REDACTED_TASK_KEY] Remove this action data class SaveChanges(val tokens: List, val blockchains: List) : TokensAction - - // TODO: Remove this action in 4.7 release - object PrepareAndNavigateToAddCustomToken : TokensAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt index 1a5eb1fd45..db87942c84 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt @@ -9,28 +9,19 @@ import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.DomainWrapped import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.common.util.supportsHdWallet -import com.tangem.domain.features.addCustomToken.CustomCurrency -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.redux.domainStore import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.tap.* -import com.tangem.tap.common.analytics.events.ManageTokens import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.features.wallet.models.Currency import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -44,7 +35,6 @@ object TokensMiddleware { { action -> when (action) { is TokensAction.SaveChanges -> handleSaveChanges(action) - is TokensAction.PrepareAndNavigateToAddCustomToken -> handleAddingCustomToken() } next(action) } @@ -231,56 +221,4 @@ object TokensMiddleware { } walletCurrenciesManager.removeCurrencies(selectedUserWallet, currencies) } - - private fun isNeedToDerive(scanResponse: ScanResponse, currency: Currency): Boolean { - return currency.derivationPath?.let { - !scanResponse.hasDerivation(currency.blockchain, it) - } ?: false - } - - private fun handleAddingCustomToken() = scope.launch { - val onAddCustomToken = fun(customCurrency: CustomCurrency) { - val scanResponse = store.state.globalState.scanResponse ?: return - - fun submitAndPopBack(scanResponse: ScanResponse, currencyList: List) { - submitAdd(scanResponse, currencyList) - // pop from the AddCustomTokenScreen - store.dispatchOnMain(NavigationAction.PopBackTo()) - store.dispatchOnMain(NavigationAction.PopBackTo()) - } - - Analytics.send(ManageTokens.CustomToken.TokenWasAdded(customCurrency)) - val currency = Currency.fromCustomCurrency(customCurrency) - val isNeedToDerive = isNeedToDerive(scanResponse, currency) - val currencyList = listOf(currency) - if (isNeedToDerive) { - deriveMissingBlockchains(scanResponse, currencyList) { - submitAndPopBack(it, currencyList) - } - } else { - submitAndPopBack(scanResponse, currencyList) - } - } - - val addedCurrencies = store.state.walletState.walletsStores - .map { walletStore -> walletStore.walletsData.map(WalletDataModel::currency) } - .flatten() - .map { currency -> - when (currency) { - is Currency.Blockchain -> DomainWrapped.Currency.Blockchain( - currency.blockchain, - currency.derivationPath, - ) - - is Currency.Token -> DomainWrapped.Currency.Token( - currency.token, - currency.blockchain, - currency.derivationPath, - ) - } - } - domainStore.dispatch(AddCustomTokenAction.Init.SetAddedCurrencies(addedCurrencies)) - domainStore.dispatch(AddCustomTokenAction.Init.SetOnAddTokenCallback(onAddCustomToken)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomToken)) - } } \ No newline at end of file diff --git a/app/src/main/res/layout/view_compose_fragment.xml b/app/src/main/res/layout/view_compose_fragment.xml deleted file mode 100644 index af9273a84d..0000000000 --- a/app/src/main/res/layout/view_compose_fragment.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - diff --git a/common/src/main/java/com/tangem/common/Filter.kt b/common/src/main/java/com/tangem/common/Filter.kt deleted file mode 100644 index 6607f4080f..0000000000 --- a/common/src/main/java/com/tangem/common/Filter.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.common - -/** -[REDACTED_AUTHOR] - */ -interface Filter { - fun filter(value: T): Boolean -} \ No newline at end of file diff --git a/common/src/main/java/com/tangem/common/Validator.kt b/common/src/main/java/com/tangem/common/Validator.kt deleted file mode 100644 index 8654e9d07e..0000000000 --- a/common/src/main/java/com/tangem/common/Validator.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.common - -/** -[REDACTED_AUTHOR] - */ -interface Validator { - fun validate(data: Data? = null): Error? -} \ No newline at end of file diff --git a/common/src/main/java/com/tangem/common/module/ModuleMessage.kt b/common/src/main/java/com/tangem/common/module/ModuleMessage.kt index 511ffa8d99..1f8414a0ca 100644 --- a/common/src/main/java/com/tangem/common/module/ModuleMessage.kt +++ b/common/src/main/java/com/tangem/common/module/ModuleMessage.kt @@ -17,11 +17,6 @@ abstract class ModuleError : Throwable(), ModuleMessage { abstract val data: Any? } -/** - * An exception marked as FbConsumeException should be submitted to Firebase.Crashlytics as a non-fatal issue. - */ -interface FbConsumeException - interface ModuleMessageConverter { fun convert(message: ModuleMessage): R } \ No newline at end of file diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 8cd92e9d2f..2c809df137 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -3,10 +3,6 @@ "name": "OPTIMISM_SWAP_FEATURE_ENABLED", "version": "4.3.1" }, - { - "name": "REDESIGNED_CUSTOM_TOKEN_SCREEN_ENABLED", - "version": "4.7.0" - }, { "name": "NEW_CARD_SCANNING_ENABLED", "version": "4.11.0" diff --git a/domain/legacy/src/main/java/com/tangem/domain/DomainDialog.kt b/domain/legacy/src/main/java/com/tangem/domain/DomainDialog.kt deleted file mode 100644 index c1f72a19de..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/DomainDialog.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.domain - -import com.tangem.common.extensions.VoidCallback -import com.tangem.datasource.api.tangemTech.models.CoinsResponse - -/** -[REDACTED_AUTHOR] - */ -sealed interface DomainDialog { - - data class DialogError(val error: DomainModuleError) : DomainDialog - - data class SelectTokenDialog( - val items: List, - val networkIdConverter: (String) -> String, - val onSelect: (CoinsResponse.Coin.Network) -> Unit, - val onClose: VoidCallback = {}, - ) : DomainDialog -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/DomainLayer.kt b/domain/legacy/src/main/java/com/tangem/domain/DomainLayer.kt deleted file mode 100644 index a8a52c2d4e..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/DomainLayer.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.domain - -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState -import com.tangem.domain.redux.state.ActionStateLoggerImpl - -/** -[REDACTED_AUTHOR] - */ -object DomainLayer { - internal val actionStateLogger = ActionStateLoggerImpl() - - var onInitComplete: ((DomainModuleError?) -> Unit)? = null - - fun init() { - initActionStateLogger() - - onInitComplete?.invoke(null) - } - - private fun initActionStateLogger() { - val factory = actionStateLogger.actionStateConvertersFactory - - factory.addConverter(AddCustomTokenAction::class.java, AddCustomTokenState.Converter()) - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt b/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt index 27d7606e7f..d762e94382 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt @@ -1,6 +1,5 @@ package com.tangem.domain -import com.tangem.common.module.FbConsumeException import com.tangem.common.module.ModuleError import com.tangem.common.module.ModuleErrorCode import com.tangem.common.module.ModuleMessage @@ -36,32 +35,14 @@ sealed class AddCustomTokenError( ) { object FieldIsEmpty : AddCustomTokenError() - object FieldIsNotEmpty : AddCustomTokenError() object InvalidContractAddress : AddCustomTokenError() object NetworkIsNotSelected : AddCustomTokenError() object InvalidDecimalsCount : AddCustomTokenError() object InvalidDerivationPath : AddCustomTokenError() - sealed class Network : AddCustomTokenError() { - object CheckAddressRequestError : Network() - } - sealed class Warning : AddCustomTokenError() { object PotentialScamToken : Warning() object TokenAlreadyAdded : Warning() object UnsupportedSolanaToken : Warning() } - - data class SelectTokeNetworkError(val networkId: String) : - AddCustomTokenError( - message = "Unknown network [$networkId] should not be included in the network selection dialog.", - ), - FbConsumeException - - data class UnAppropriateInitialization( - val of: String, - val info: String? = null, - ) : AddCustomTokenError( - message = "The [$of], must be properly initialized. Info [$info]", - ) } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/DomainWrapped.kt b/domain/legacy/src/main/java/com/tangem/domain/DomainWrapped.kt deleted file mode 100644 index 902244560d..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/DomainWrapped.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.domain - -/** -[REDACTED_AUTHOR] - * Provides a temporary copies of the app module classes, data structures, etc. - */ -// TODO: refactoring: : after refactoring they should be unwrapped and moved -// to appropriate parts of module -@Deprecated("After refactoring they should be unwrapped and moved to appropriate parts of module") -sealed interface DomainWrapped { - - // Mirror reflection ot the com.tangem.tap.features.wallet.redux.Currency - sealed interface Currency { - val blockchain: com.tangem.blockchain.common.Blockchain - val currencySymbol: String - val derivationPath: String? - - data class Token( - val token: com.tangem.blockchain.common.Token, - override val blockchain: com.tangem.blockchain.common.Blockchain, - override val derivationPath: String?, - ) : Currency { - override val currencySymbol = token.symbol - } - - data class Blockchain( - override val blockchain: com.tangem.blockchain.common.Blockchain, - override val derivationPath: String?, - ) : Currency { - override val currencySymbol: String = blockchain.currency - } - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index bd892377ae..0dcaa2b00a 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -1,7 +1,6 @@ package com.tangem.domain.common import com.tangem.blockchain.common.Blockchain -import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion import com.tangem.domain.models.scan.CardDTO import java.util.* @@ -69,6 +68,4 @@ object TapWorkarounds { fun isStart2CoinIssuer(cardIssuer: String?): Boolean { return cardIssuer?.lowercase(Locale.US) == START_2_COIN_ISSUER } - - fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId] ?: null } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/form/FieldDataConverters.kt b/domain/legacy/src/main/java/com/tangem/domain/common/form/FieldDataConverters.kt deleted file mode 100644 index b406d02f7c..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/common/form/FieldDataConverters.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.tangem.domain.common.form - -import com.tangem.common.json.MoshiJsonConverter - -/** -[REDACTED_AUTHOR] - */ -interface DataConverterVisitor { - fun visit(data: Data?) - fun getConvertedData(): Result -} - -interface FieldDataConverter : DataConverterVisitor - -abstract class BaseFieldDataConverter : FieldDataConverter { - private val collectIds: List - get() = getIdToCollect() - - protected val collectedData: MutableMap = mutableMapOf() - - override fun visit(data: Pair>?) { - val id = data?.first ?: return - - if (collectIds.contains(id)) { - collectedData[id] = data.second.value - } - } - - abstract fun getIdToCollect(): List -} - -class FieldToJsonConverter( - private val fieldsToConvert: List = listOf(), - private val jsonConverter: MoshiJsonConverter, -) : BaseFieldDataConverter() { - - override fun getConvertedData(): String = jsonConverter.toJson(collectedData, " ") - - override fun getIdToCollect(): List = fieldsToConvert -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt b/domain/legacy/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt deleted file mode 100644 index 067feb900f..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/common/form/FieldsValidators.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.tangem.domain.common.form - -import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService -import com.tangem.blockchain.blockchains.solana.SolanaAddressService -import com.tangem.blockchain.blockchains.tron.TronAddressService -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.AddressService -import com.tangem.common.Validator -import com.tangem.common.card.EllipticCurve -import com.tangem.domain.AddCustomTokenError -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - */ -interface CustomTokenValidator : Validator - -class StringIsEmptyValidator : CustomTokenValidator { - override fun validate(data: String?): AddCustomTokenError? { - return if (data.isNullOrEmpty()) null else AddCustomTokenError.FieldIsNotEmpty - } -} - -class StringIsNotEmptyValidator : CustomTokenValidator { - override fun validate(data: String?): AddCustomTokenError? { - return if (data.isNullOrEmpty()) AddCustomTokenError.FieldIsEmpty else null - } -} - -class TokenContractAddressValidator : CustomTokenValidator { - - private var blockchain: Blockchain = Blockchain.Unknown - - private val successAddressValidator = object : AddressService() { - override fun makeAddress(walletPublicKey: ByteArray, curve: EllipticCurve?): String { - throw UnsupportedOperationException() - } - - override fun validate(address: String): Boolean = true - } - - fun nextValidationFor(blockchain: Blockchain) { - this.blockchain = blockchain - } - - override fun validate(data: String?): AddCustomTokenError? { - return when { - data.isNullOrEmpty() -> AddCustomTokenError.FieldIsEmpty - getAddressService().validate(data) -> null - else -> AddCustomTokenError.InvalidContractAddress - } - } - - private fun getAddressService(): AddressService { - return when (blockchain) { - Blockchain.Unknown -> successAddressValidator - Blockchain.Binance, Blockchain.BinanceTestnet -> successAddressValidator - Blockchain.Solana, Blockchain.SolanaTestnet -> SolanaAddressService() - Blockchain.Tron, Blockchain.TronTestnet -> TronAddressService() - else -> { - if (blockchain.isEvm()) { - EthereumAddressService() - } else { - Timber.e("Throw for blockchain: ${blockchain.fullName}") - throw UnsupportedOperationException() - } - } - } - } -} - -class TokenNetworkValidator : CustomTokenValidator { - override fun validate(data: Blockchain?): AddCustomTokenError? { - return when (data) { - null, Blockchain.Unknown -> AddCustomTokenError.NetworkIsNotSelected - else -> null - } - } -} - -class TokenNameValidator : CustomTokenValidator { - override fun validate(data: String?): AddCustomTokenError? = StringIsNotEmptyValidator().validate(data) -} - -class TokenSymbolValidator : CustomTokenValidator { - override fun validate(data: String?): AddCustomTokenError? = StringIsNotEmptyValidator().validate(data) -} - -class TokenDecimalsValidator : CustomTokenValidator { - override fun validate(data: String?): AddCustomTokenError? { - val decimal = data?.toIntOrNull() ?: return AddCustomTokenError.FieldIsEmpty - - return if (decimal > INVALID_DECIMALS_COUNT) AddCustomTokenError.InvalidDecimalsCount else null - } - - private companion object { - const val INVALID_DECIMALS_COUNT = 30 - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/form/Form.kt b/domain/legacy/src/main/java/com/tangem/domain/common/form/Form.kt deleted file mode 100644 index cca8b72a5f..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/common/form/Form.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.tangem.domain.common.form - -/** -[REDACTED_AUTHOR] - */ -class Form( - fieldList: List>, -) { - private val _fieldList: MutableList> = fieldList.toMutableList() - - val fieldList: List> - get() = _fieldList.toList() - - fun getField(id: FieldId): DataField<*>? = fieldList.firstOrNull { it.id == id } - - fun getData(id: FieldId): Pair? = getField(id)?.getData() - - fun setField(field: DataField<*>) { - val oldField = getField(field.id) ?: return - val oldIndexOfField = _fieldList.indexOf(oldField) - if (oldIndexOfField == -1) return - - _fieldList.removeAt(oldIndexOfField) - _fieldList.add(oldIndexOfField, field) - } - - // convert this form data whatever you want - fun visitDataConverter(converter: FieldDataConverter<*>) { - fieldList.forEach { it.visitDataConverter(converter) } - } -} - -interface FieldId - -interface Field { - val id: FieldId - var data: Data - - data class Data( - val value: Data, - val isUserInput: Boolean, - ) -} - -typealias FieldData = Pair> - -interface DataField : Field { - fun getData(): Pair> - fun visitDataConverter(dataConverter: FieldDataConverter<*>) -} - -abstract class BaseDataField( - override val id: FieldId, - override var data: Field.Data, -) : DataField { - - override fun getData(): Pair> = id to data - - override fun visitDataConverter(dataConverter: FieldDataConverter<*>) { - dataConverter.visit(getData()) - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt deleted file mode 100644 index 1a5fc65e8c..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.domain.features.addCustomToken - -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext - -/** -[REDACTED_AUTHOR] - */ -class AddCustomTokenService( - private val tangemTechApi: TangemTechApi, - private val dispatchers: CoroutineDispatcherProvider, - private val supportedTokenNetworkIds: List, -) { - - suspend fun findToken(contractAddress: String, networkId: String?): List { - return withContext(dispatchers.io) { - runCatching { - tangemTechApi.getCoins( - contractAddress = contractAddress, - networkIds = selectNetworksForSearch(networkId), - ) - } - .fold( - onSuccess = { response -> - var coinsList = mutableListOf() - response.coins.forEach { coin -> - val networksWithTheSameAddress = coin.networks - .filter { it.contractAddress != null || it.decimalCount != null } - .filter { it.contractAddress?.equals(contractAddress, ignoreCase = true) == true } - .filter { supportedTokenNetworkIds.contains(it.networkId) } - if (networksWithTheSameAddress.isNotEmpty()) { - val newToken = coin.copy(networks = networksWithTheSameAddress) - coinsList.add(newToken) - } - } - if (coinsList.size > 1) { - // https://tangem.slack.com/archives/GMXC6PP71/p1649672562078679 - coinsList = mutableListOf(coinsList[0]) - } - coinsList - }, - onFailure = { emptyList() }, - ) - } - } - - private fun selectNetworksForSearch(networkId: String?): String { - return networkId ?: supportedTokenNetworkIds.joinToString(",") - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt index 78ff46be86..15debdd88f 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt @@ -2,11 +2,7 @@ package com.tangem.domain.features.addCustomToken import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.form.BaseFieldDataConverter -import com.tangem.domain.common.form.FieldId -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState /** [REDACTED_AUTHOR] @@ -16,65 +12,16 @@ sealed class CustomCurrency( val derivationPath: DerivationPath?, ) { + @Deprecated("It will be removed in next releases") class CustomBlockchain( network: Blockchain, derivationPath: DerivationPath?, - ) : CustomCurrency(network, derivationPath) { - - class Converter( - private val derivationStyle: DerivationStyle?, - ) : BaseFieldDataConverter() { - override fun getConvertedData(): CustomBlockchain { - val mainNetwork = collectedData[CustomTokenFieldId.Network] as Blockchain - val derivationPathNetwork = collectedData[CustomTokenFieldId.DerivationPath] as Blockchain - val derivationPath = AddCustomTokenState.getDerivationPath( - mainNetwork, - derivationPathNetwork, - derivationStyle, - ) - return CustomBlockchain(mainNetwork, derivationPath) - } - - override fun getIdToCollect(): List = - listOf(CustomTokenFieldId.Network, CustomTokenFieldId.DerivationPath) - } - } + ) : CustomCurrency(network, derivationPath) + @Deprecated("It will be removed in next releases") class CustomToken( val token: Token, network: Blockchain, derivationPath: DerivationPath?, - ) : CustomCurrency(network, derivationPath) { - - class Converter( - private val tokenId: String?, - private val derivationStyle: DerivationStyle?, - ) : BaseFieldDataConverter() { - - override fun getConvertedData(): CustomToken { - val mainNetwork = collectedData[CustomTokenFieldId.Network] as Blockchain - val derivationPathNetwork = collectedData[CustomTokenFieldId.DerivationPath] as Blockchain - val derivationPath = AddCustomTokenState.getDerivationPath( - mainNetwork, - derivationPathNetwork, - derivationStyle, - ) - - val token = Token( - name = collectedData[CustomTokenFieldId.Name] as String, - symbol = collectedData[CustomTokenFieldId.Symbol] as String, - contractAddress = collectedData[CustomTokenFieldId.ContractAddress] as String, - decimals = (collectedData[CustomTokenFieldId.Decimals] as String).toInt(), - id = tokenId, - ) - return CustomToken( - token, - collectedData[CustomTokenFieldId.Network] as Blockchain, - derivationPath, - ) - } - - override fun getIdToCollect(): List = CustomTokenFieldId.values().toList() - } - } + ) : CustomCurrency(network, derivationPath) } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt deleted file mode 100644 index e9ec16b2a1..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/FormFields.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.domain.features.addCustomToken - -import com.tangem.blockchain.common.Blockchain -import com.tangem.domain.common.form.BaseDataField -import com.tangem.domain.common.form.Field -import com.tangem.domain.common.form.FieldId - -/** -[REDACTED_AUTHOR] - */ -enum class CustomTokenFieldId : FieldId { - ContractAddress, - Network, - Name, - Symbol, - Decimals, - DerivationPath, -} - -data class TokenField( - override val id: FieldId, -) : BaseDataField(id, Field.Data("", false)) - -data class TokenBlockchainField( - override val id: FieldId, - val itemList: List, -) : BaseDataField(id, Field.Data(Blockchain.Unknown, false)) - -data class TokenDerivationPathField( - override val id: FieldId, - val itemList: List, -) : BaseDataField(id, Field.Data(Blockchain.Unknown, false)) \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenAction.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenAction.kt deleted file mode 100644 index c178c4a610..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenAction.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.tangem.domain.features.addCustomToken.redux - -import com.tangem.blockchain.common.Blockchain -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.DomainWrapped -import com.tangem.domain.common.form.Field -import com.tangem.domain.common.form.FieldId -import com.tangem.domain.features.addCustomToken.CustomCurrency -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId -import org.rekotlin.Action - -/** -[REDACTED_AUTHOR] - */ -sealed class AddCustomTokenAction : Action { - sealed class Init : AddCustomTokenAction() { - data class SetAddedCurrencies(val addedCurrencies: List) : AddCustomTokenAction() - data class SetOnAddTokenCallback(val callback: (CustomCurrency) -> Unit) : AddCustomTokenAction() - } - - object OnCreate : AddCustomTokenAction() - - object OnDestroy : AddCustomTokenAction() - - // from user, ui - data class OnTokenContractAddressChanged(val contractAddress: Field.Data) : AddCustomTokenAction() - data class OnTokenNetworkChanged(val blockchainNetwork: Field.Data) : AddCustomTokenAction() - data class OnTokenNameChanged(val tokenName: Field.Data) : AddCustomTokenAction() - data class OnTokenSymbolChanged(val tokenSymbol: Field.Data) : AddCustomTokenAction() - data class OnTokenDerivationPathChanged( - val blockchainDerivationPath: Field.Data, - ) : AddCustomTokenAction() - - data class OnTokenDecimalsChanged(val tokenDecimals: Field.Data) : AddCustomTokenAction() - object OnAddCustomTokenClicked : AddCustomTokenAction() - - data class SetFoundTokenInfo(val foundToken: CoinsResponse.Coin?) : AddCustomTokenAction() - - // form fields - data class UpdateForm(val state: AddCustomTokenState) : AddCustomTokenAction() - - sealed class FieldError : AddCustomTokenAction() { - data class Add(val id: CustomTokenFieldId, val error: AddCustomTokenError) : FieldError() - data class Remove(val id: CustomTokenFieldId) : FieldError() - } - - // warnings - sealed class Warning : AddCustomTokenAction() { - data class Add(val warnings: Set) : Warning() - data class Remove(val warnings: Set) : Warning() - data class Replace( - val remove: Set, - val add: Set, - ) : Warning() - } - - // To change the screenState - sealed class Screen : AddCustomTokenAction() { - data class UpdateTokenFields(val pairs: List>) : Screen() - data class UpdateAddButton(val addButton: ViewStates.AddButton) : Screen() - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt deleted file mode 100644 index aa12f86b00..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ /dev/null @@ -1,720 +0,0 @@ -package com.tangem.domain.features.addCustomToken.redux - -import android.webkit.ValueCallback -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.guard -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.AddCustomTokenError.Warning.* -import com.tangem.domain.DomainDialog -import com.tangem.domain.DomainWrapped -import com.tangem.domain.common.extensions.canHandleToken -import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.domain.common.extensions.supportedBlockchains -import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.common.form.* -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.features.addCustomToken.* -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.* -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState.Companion.createInitialScreenState -import com.tangem.domain.redux.BaseStoreHub -import com.tangem.domain.redux.DomainState -import com.tangem.domain.redux.ReStoreReducer -import com.tangem.domain.redux.domainStore -import com.tangem.domain.redux.extensions.dispatchOnMain -import com.tangem.domain.redux.global.DomainGlobalAction -import com.tangem.domain.redux.global.DomainGlobalState -import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.rekotlin.Action -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - */ -@Suppress("LargeClass") -internal class AddCustomTokenHub : BaseStoreHub("AddCustomTokenHub") { - - private val hubState: AddCustomTokenState - get() = domainStore.state.addCustomTokensState - - override fun getReducer(): ReStoreReducer = AddCustomTokenReducer(globalState) - - override fun getHubState(storeState: DomainState): AddCustomTokenState = hubState - - override fun updateStoreState(storeState: DomainState, newHubState: AddCustomTokenState): DomainState { - return storeState.copy(addCustomTokensState = newHubState) - } - - @Suppress("ComplexMethod") - override suspend fun handleAction(action: Action, storeState: DomainState, cancel: ValueCallback) { - if (action !is AddCustomTokenAction) return - - when (action) { - is OnCreate -> { - hubState.appSavedCurrencies.guard { - return throwUnAppropriateInitialization("addedTokens") - } - } - is OnDestroy -> cancelAll() - is OnTokenContractAddressChanged -> { - validateContractAddressAndNotify(action.contractAddress.value) - } - is OnTokenNetworkChanged -> { - if (!action.blockchainNetwork.isUserInput) return - - validateContractAddressAndNotify(ContractAddress.getFieldValue()) - } - is OnTokenDerivationPathChanged -> { - updateAddButton() - } - is OnTokenNameChanged, is OnTokenSymbolChanged, is OnTokenDecimalsChanged -> { - updateAddButton() - } - is OnAddCustomTokenClicked -> { - val state = hubState - val completeData = when { - state.getCustomTokenType() == CustomTokenType.Token && state.networkIsSelected() -> { - state.gatherUserToken() - } - state.getCustomTokenType() == CustomTokenType.Blockchain && state.networkIsSelected() -> { - state.gatherBlockchain() - } - else -> null - } - - if (completeData == null) { - // normally it can't be, because the AddButton must be blocked - } else { - hubScope.launch(Dispatchers.Main) { - state.onTokenAddCallback?.invoke(completeData) - } - } - } - else -> Unit - } - } - - private suspend fun validateContractAddressAndNotify(contractAddress: String) { - val error = ContractAddress.validateValue(contractAddress) - if (Network.isFilled()) { - when (error) { - null -> { - // valid contract address - ContractAddress.removeError() - findTokenAndUpdateFields(contractAddress) - } - AddCustomTokenError.InvalidContractAddress -> { - ContractAddress.addError(error) - enableDisableTokenDetailFields(hubState.tokensAnyFieldsIsFilled()) - } - AddCustomTokenError.FieldIsEmpty -> { - ContractAddress.removeError() - clearTokenDetailsFields() - disableTokenDetailFields() - } - else -> {} - } - } else { - // is default selection (Blockchain.Unknown) - when (error) { - null -> { - // Blockchain.Unknown has always valid contract address - ContractAddress.removeError() - findTokenAndUpdateFields(contractAddress) - } - else -> { - ContractAddress.removeError() - clearTokenDetailsFields() - disableTokenDetailFields() - } - } - } - updateDerivationPath(Network.getFieldValue()) - updateWarnings() - updateAddButton() - } - - private suspend fun findTokenAndUpdateFields(contractAddress: String) { - val foundTokens = requestInfoAboutToken(contractAddress) - if (foundTokens.isEmpty()) { - // token not found - it's completely custom - dispatchOnMain(SetFoundTokenInfo(null)) - enableTokenDetailFields() - return - } - - // foundToken - contains all info about the token - val foundToken = foundTokens[0] - dispatchOnMain(SetFoundTokenInfo(foundToken)) - when { - foundToken.networks.isEmpty() -> { - Timber.e("Unexpected state -> throw to FB") - } - foundToken.networks.size == 1 -> { - // token with single contract address - val singleTokenContract = foundToken.networks[0] - fillTokenFields(foundToken, singleTokenContract) - disableTokenDetailFields() - } - else -> { - val dialog = DomainDialog.SelectTokenDialog( - items = foundToken.networks, - networkIdConverter = { networkId -> - val blockchain = Blockchain.fromNetworkId(networkId) - if (blockchain == null || blockchain == Blockchain.Unknown) { - throw AddCustomTokenError.SelectTokeNetworkError(networkId) - } - hubState.blockchainToName(blockchain) ?: "" - }, - onSelect = { selectedContract -> - hubScope.launch { - // find how to connect to the upper coroutineContext and dispatch through them - fillTokenFields(foundToken, selectedContract) - disableTokenDetailFields() - } - }, - ) - dispatchOnMain(DomainGlobalAction.ShowDialog(dialog)) - } - } - } - - private suspend fun updateDerivationPath(blockchainNetwork: Blockchain) { - val state = hubState - val derivationIsSupportedByNetwork = blockchainNetwork.isEvm() || blockchainNetwork == Blockchain.Unknown - - if (DerivationPath.isFilled() && !derivationIsSupportedByNetwork) { - // reset to default - val derivationField = DerivationPath.getField() - derivationField.data = derivationField.data.copy( - value = Blockchain.Unknown, - isUserInput = false, - ) - state.setField(derivationField) - dispatchOnMain(UpdateForm(hubState)) - } - - if (state.screenState.derivationPath.isEnabled != derivationIsSupportedByNetwork) { - val action = Screen.UpdateTokenFields( - listOf( - DerivationPath to state.screenState.derivationPath.copy( - isEnabled = derivationIsSupportedByNetwork, - ), - ), - ) - dispatchOnMain(action) - } - } - - private suspend fun updateWarnings() { - val state = hubState - val warningsAdd = mutableSetOf() - val warningsRemove = mutableSetOf() - - val tokenIsSupported = tokenIsSupported(Network.getFieldValue()) - val alreadyAdded = isPersistIntoAppSavedTokensList() - when (state.getCustomTokenType()) { - CustomTokenType.Blockchain -> { - warningsRemove.add(UnsupportedSolanaToken) - - if (alreadyAdded) warningsAdd.add(TokenAlreadyAdded) else warningsRemove.add(TokenAlreadyAdded) - - if (state.derivationPathIsSelected()) { - warningsAdd.add(PotentialScamToken) - } else { - warningsRemove.add(PotentialScamToken) - } - } - CustomTokenType.Token -> { - if (tokenIsSupported) { - warningsRemove.add(UnsupportedSolanaToken) - } else { - val validationResult = ContractAddress.validateValue(ContractAddress.getFieldValue()) - if (validationResult == AddCustomTokenError.FieldIsEmpty) { - warningsRemove.add(UnsupportedSolanaToken) - } else { - warningsAdd.add(UnsupportedSolanaToken) - } - } - - if (isPersistIntoAppSavedTokensList()) { - warningsAdd.add(TokenAlreadyAdded) - } else { - warningsRemove.add(TokenAlreadyAdded) - } - - if (state.foundToken == null) { - if (state.tokensAnyFieldsIsFilled()) { - warningsAdd.add(PotentialScamToken) - } else { - warningsRemove.add(PotentialScamToken) - } - } else { - if (state.foundToken.active) { - warningsRemove.add(PotentialScamToken) - } else { - warningsAdd.add(PotentialScamToken) - } - } - } - } - - dispatchOnMain( - Warning.Replace( - remove = warningsRemove, - add = warningsAdd, - ), - ) - } - - private suspend fun updateAddButton() { - if (isPersistIntoAppSavedTokensList()) { - TokenAlreadyAdded.add() - disableAddButton() - return - } else { - TokenAlreadyAdded.remove() - } - - val state = hubState - when { - // token - state.tokensFieldsIsFilled() && state.networkIsSelected() -> { - val error = ContractAddress.validateValue(ContractAddress.getFieldValue()) - val tokenIsSupported = tokenIsSupported(Network.getFieldValue()) - enableDisableAddButton(tokenIsSupported && error == null) - } - // token - state.tokensAnyFieldsIsFilled() -> { - disableAddButton() - } - // blockchain - else -> { - if (state.networkIsSelected()) { - if (isBlockchainPersistIntoAppSavedTokensList()) disableAddButton() else enableAddButton() - } else { - disableAddButton() - } - } - } - } - - private suspend fun requestInfoAboutToken(contractAddress: String): List { - val tangemTechServiceManager = requireNotNull(hubState.tangemTechServiceManager) - dispatchOnMain(Screen.UpdateTokenFields(listOf(ContractAddress to ViewStates.TokenField(isLoading = true)))) - - val field = hubState.getField(Network) - val selectedNetworkId: String? = field.data.value.let { - if (it == Blockchain.Unknown) null else it - }?.toNetworkId() - - // simulate loading effect. It would be better if the delay would only run if tokenManager.checkAddress() - // got the result faster than 500ms and the delay would only be the difference between them. - delay(timeMillis = 500) - - val result = tangemTechServiceManager.findToken(contractAddress, selectedNetworkId) - - dispatchOnMain(Screen.UpdateTokenFields(listOf(ContractAddress to ViewStates.TokenField(isLoading = false)))) - return result - } - - /** - * These are helper functions. - */ - private fun isPersistIntoAppSavedTokensList(): Boolean = when (hubState.getCustomTokenType()) { - CustomTokenType.Blockchain -> isBlockchainPersistIntoAppSavedTokensList() - CustomTokenType.Token -> isTokenPersistIntoAppSavedTokensList() - } - - private fun isTokenPersistIntoAppSavedTokensList(): Boolean { - val savedCurrencies = hubState.appSavedCurrencies ?: return false - - val tokenId = hubState.foundToken?.id - val tokenContractAddress = ContractAddress.getFieldValue() - val tokenNetworkId = Network.getFieldValue().toNetworkId() - val selectedDerivation = DerivationPath.getFieldValue() - - val derivationPath = getDerivationPathFromSelectedBlockchain(selectedDerivation) - savedCurrencies.forEach { wrappedCurrency -> - when (wrappedCurrency) { - is DomainWrapped.Currency.Blockchain -> Unit - is DomainWrapped.Currency.Token -> { - val sameId = tokenId == wrappedCurrency.token.id - val sameAddress = tokenContractAddress == wrappedCurrency.token.contractAddress - val sameBlockchain = Blockchain.fromNetworkId(tokenNetworkId) == wrappedCurrency.blockchain - val sameDerivationPath = derivationPath?.rawPath == wrappedCurrency.derivationPath - @Suppress("ComplexCondition") - if (sameId && sameAddress && sameBlockchain && sameDerivationPath) { - return true - } - } - } - } - return false - } - - private fun isBlockchainPersistIntoAppSavedTokensList(): Boolean { - val savedCurrencies = hubState.appSavedCurrencies ?: return false - val selectedNetwork = Network.getFieldValue() - val selectedDerivation = DerivationPath.getFieldValue() - val derivationPath = getDerivationPathFromSelectedBlockchain(selectedDerivation) - - savedCurrencies.forEach { wrappedCurrency -> - when (wrappedCurrency) { - is DomainWrapped.Currency.Blockchain -> { - val isSameBlockchain = selectedNetwork == wrappedCurrency.blockchain - val isSameDerivationPath = derivationPath?.rawPath == wrappedCurrency.derivationPath - if (isSameBlockchain && isSameDerivationPath) return true - } - - is DomainWrapped.Currency.Token -> Unit - } - } - return false - } - - private fun getDerivationPathFromSelectedBlockchain( - selectedDerivationBlockchain: Blockchain, - ): com.tangem.crypto.hdWallet.DerivationPath? = AddCustomTokenState.getDerivationPath( - mainNetwork = Network.getFieldValue(), - derivationNetwork = selectedDerivationBlockchain, - derivationStyle = hubState.cardDerivationStyle, - ) - - private suspend fun fillTokenFields(token: CoinsResponse.Coin, coinNetwork: CoinsResponse.Coin.Network) { - val blockchain = Blockchain.fromNetworkId(coinNetwork.networkId) ?: Blockchain.Unknown - Network.setFieldValue(Field.Data(blockchain, false)) - Name.setFieldValue(Field.Data(token.name, false)) - Symbol.setFieldValue(Field.Data(token.symbol, false)) - Decimals.setFieldValue(Field.Data(coinNetwork.decimalCount.toString(), false)) - dispatchOnMain(UpdateForm(hubState)) - } - - private suspend fun clearTokenDetailsFields() { - Name.setFieldValue(Field.Data("", false)) - Symbol.setFieldValue(Field.Data("", false)) - Decimals.setFieldValue(Field.Data("", false)) - dispatchOnMain(UpdateForm(hubState)) - } - - private suspend fun enableTokenDetailFields() { - enableDisableTokenDetailFields(true) - } - - private suspend fun disableTokenDetailFields() { - enableDisableTokenDetailFields(false) - } - - private suspend fun enableDisableTokenDetailFields(isEnabled: Boolean) { - val state = hubState - val action = Screen.UpdateTokenFields( - listOf( - Name to state.screenState.name.copy(isEnabled = isEnabled), - Symbol to state.screenState.symbol.copy(isEnabled = isEnabled), - Decimals to state.screenState.decimals.copy(isEnabled = isEnabled), - ), - ) - dispatchOnMain(action) - } - - private suspend fun enableAddButton() { - enableDisableAddButton(true) - } - - private suspend fun disableAddButton() { - enableDisableAddButton(false) - } - - private suspend fun enableDisableAddButton(isEnabled: Boolean) { - dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(isEnabled))) - } - - private fun tokenIsSupported(blockchain: Blockchain): Boolean = when (blockchain) { - Blockchain.Unknown -> true - else -> { - val scanResponse = globalState.scanResponse - scanResponse?.card?.canHandleToken( - blockchain = blockchain, - cardTypesResolver = scanResponse.cardTypesResolver, - ) ?: false - } - } - - @Throws - private fun throwUnAppropriateInitialization(objName: String) { - throw AddCustomTokenError.UnAppropriateInitialization( - "AddCustomTokenHub", - "$objName must be not NULL", - ) - } - - private suspend fun CustomTokenFieldId.addError(error: AddCustomTokenError) { - dispatchOnMain(FieldError.Add(this, error)) - } - - private suspend fun CustomTokenFieldId.removeError() { - dispatchOnMain(FieldError.Remove(this)) - } - - private inline fun CustomTokenFieldId.getField(): T { - val state = hubState - val value = when (this) { - ContractAddress -> state.getField(this) - Network -> state.getField(this) - Name -> state.getField(this) - Symbol -> state.getField(this) - Decimals -> state.getField(this) - DerivationPath -> state.getField(this) - } - return value as T - } - - private inline fun CustomTokenFieldId.getFieldValue(): T { - val value = when (this) { - ContractAddress -> getField().data.value - Network -> getField().data.value - Name -> getField().data.value - Symbol -> getField().data.value - Decimals -> getField().data.value - DerivationPath -> getField().data.value - } - return value as T - } - - private fun CustomTokenFieldId.setFieldValue(fieldData: Field.Data<*>) { - when (this) { - ContractAddress -> getField().data = fieldData as Field.Data - Network -> getField().data = fieldData as Field.Data - Name -> getField().data = fieldData as Field.Data - Symbol -> getField().data = fieldData as Field.Data - Decimals -> getField().data = fieldData as Field.Data - DerivationPath -> getField().data = fieldData as Field.Data - } - } - - private fun CustomTokenFieldId.validateValue(value: Any): AddCustomTokenError? { - return when (this) { - ContractAddress -> { - val contractAddressValidator: TokenContractAddressValidator = hubState.getValidator(ContractAddress) - contractAddressValidator.nextValidationFor(Network.getFieldValue()) - contractAddressValidator.validate(value as String) - } - Network, DerivationPath -> { - hubState.getValidator(Network).validate(value as Blockchain) - } - Name -> { - hubState.getValidator(Name).validate(value as String) - } - Symbol -> { - hubState.getValidator(Symbol).validate(value as String) - } - Decimals -> { - hubState.getValidator(Decimals).validate(value as String) - } - } - } - - private fun CustomTokenFieldId.isFilled(): Boolean { - return when (this) { - ContractAddress -> getFieldValue().isNotEmpty() - Network -> getFieldValue() != Blockchain.Unknown - Name -> getFieldValue().isNotEmpty() - Symbol -> getFieldValue().isNotEmpty() - Decimals -> getFieldValue().isNotEmpty() - DerivationPath -> getFieldValue() != Blockchain.Unknown - } - } - - private suspend fun AddCustomTokenError.Warning.add() { - dispatchOnMain(Warning.Add(setOf(this))) - } - - private suspend fun AddCustomTokenError.Warning.remove() { - dispatchOnMain(Warning.Remove(setOf(this))) - } -} - -@Suppress("ComplexMethod") -private class AddCustomTokenReducer( - private val globalState: DomainGlobalState, -) : ReStoreReducer { - - @Suppress("LongMethod") - override fun reduceAction(action: Action, state: AddCustomTokenState): AddCustomTokenState { - return when (action) { - is Init.SetAddedCurrencies -> { - state.copy(appSavedCurrencies = action.addedCurrencies) - } - is Init.SetOnAddTokenCallback -> { - state.copy(onTokenAddCallback = action.callback) - } - is OnCreate -> { - val scanResponse = requireNotNull(globalState.scanResponse) - val card = globalState.scanResponse.card - val supportedTokenNetworkIds = card.supportedBlockchains(scanResponse.cardTypesResolver) - .filter(Blockchain::canHandleTokens) - .map(Blockchain::toNetworkId) - - val tangemTechServiceManager = AddCustomTokenService( - tangemTechApi = globalState.networkServices.tangemTechService.api, - dispatchers = AppCoroutineDispatcherProvider(), - supportedTokenNetworkIds = supportedTokenNetworkIds, - ) - - state.copy( - cardDerivationStyle = globalState.scanResponse.derivationStyleProvider.getDerivationStyle(), - form = Form( - AddCustomTokenState.createFormFields( - cardTypesResolver = globalState.scanResponse.cardTypesResolver, - card = card, - type = CustomTokenType.Blockchain, - ), - ), - tangemTechServiceManager = tangemTechServiceManager, - screenState = createInitialScreenState(card.settings.isHDWalletAllowed), - ) - } - is OnDestroy -> { - val scanResponse = requireNotNull(globalState.scanResponse) - val card = scanResponse.card - state.reset(scanResponse.cardTypesResolver, card) - } - is UpdateForm -> { - updateFormState(action.state) - } - is OnTokenContractAddressChanged -> { - val field: TokenField = state.getField(ContractAddress) - field.data = action.contractAddress - updateFormState(state) - } - is OnTokenNetworkChanged -> { - val field: TokenBlockchainField = state.getField(Network) - field.data = action.blockchainNetwork - updateFormState(state) - } - is OnTokenNameChanged -> { - val field: TokenField = state.getField(Name) - field.data = action.tokenName - updateFormState(state) - } - is OnTokenSymbolChanged -> { - val field: TokenField = state.getField(Symbol) - field.data = action.tokenSymbol - updateFormState(state) - } - is OnTokenDecimalsChanged -> { - val field: TokenField = state.getField(Decimals) - field.data = action.tokenDecimals - updateFormState(state) - } - is OnTokenDerivationPathChanged -> { - val field: TokenDerivationPathField = state.getField(DerivationPath) - field.data = action.blockchainDerivationPath - updateFormState(state) - } - is FieldError.Add -> { - val newMap = state.formErrors.toMutableMap().apply { this[action.id] = action.error } - state.copy(formErrors = newMap) - } - is FieldError.Remove -> { - val newMap = state.formErrors.toMutableMap().apply { remove(action.id) } - state.copy(formErrors = newMap) - } - is SetFoundTokenInfo -> { - state.copy(foundToken = action.foundToken) - } - is Warning.Add -> { - val newList = state.warnings.toMutableSet().apply { addAll(action.warnings) } - state.copy(warnings = newList.toSet()) - } - is Warning.Remove -> { - val newList = state.warnings.toMutableSet().apply { removeAll(action.warnings) } - state.copy(warnings = newList.toSet()) - } - is Warning.Replace -> { - val newList = state.warnings.toMutableSet().apply { - removeAll(action.remove) - addAll(action.add) - } - state.copy(warnings = newList.toSet()) - } - is Screen.UpdateTokenFields -> { - var newScreenState = state.screenState - action.pairs.forEach { - newScreenState = when (it.first) { - ContractAddress -> { - if (state.screenState.contractAddressField == it.second) { - newScreenState - } else { - newScreenState.copy(contractAddressField = it.second) - } - } - Network -> { - if (state.screenState.network == it.second) { - newScreenState - } else { - newScreenState.copy(network = it.second) - } - } - Name -> { - if (state.screenState.name == it.second) { - newScreenState - } else { - newScreenState.copy(name = it.second) - } - } - Symbol -> { - if (state.screenState.symbol == it.second) { - newScreenState - } else { - newScreenState.copy(symbol = it.second) - } - } - Decimals -> { - if (state.screenState.decimals == it.second) { - newScreenState - } else { - newScreenState.copy(decimals = it.second) - } - } - DerivationPath -> { - if (state.screenState.derivationPath == it.second) { - newScreenState - } else { - newScreenState.copy(derivationPath = it.second) - } - } - else -> newScreenState - } - } - if (state.screenState == newScreenState) { - state - } else { - state.copy(screenState = newScreenState) - } - } - is Screen.UpdateAddButton -> { - val newScreenState = if (state.screenState.addButton == action.addButton) { - state.screenState - } else { - state.screenState.copy(addButton = action.addButton) - } - if (newScreenState == state.screenState) { - state - } else { - state.copy(screenState = newScreenState) - } - } - else -> state - } - } - - private fun updateFormState(state: AddCustomTokenState): AddCustomTokenState { - return state.copy(form = Form(state.form.fieldList)) - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt deleted file mode 100644 index 6d1b3fd2b0..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ /dev/null @@ -1,311 +0,0 @@ -package com.tangem.domain.features.addCustomToken.redux - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.DomainWrapped -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.extensions.isSupportedInApp -import com.tangem.domain.common.extensions.supportedBlockchains -import com.tangem.domain.common.extensions.supportedTokens -import com.tangem.domain.common.form.* -import com.tangem.domain.features.addCustomToken.* -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.redux.DomainState -import com.tangem.domain.redux.state.StringActionStateConverter -import org.rekotlin.Action -import org.rekotlin.StateType - -data class AddCustomTokenState( - val appSavedCurrencies: List? = null, - val onTokenAddCallback: ((CustomCurrency) -> Unit)? = null, - val cardDerivationStyle: DerivationStyle? = null, - val form: Form = Form(listOf()), - val formValidators: Map> = createFormValidators(), - val formErrors: Map = emptyMap(), - val foundToken: CoinsResponse.Coin? = null, - val warnings: Set = emptySet(), - val screenState: ScreenState = createInitialScreenState(), - val tangemTechServiceManager: AddCustomTokenService? = null, -) : StateType { - - inline fun getField(id: FieldId): T = form.getField(id) as T - - fun setField(field: DataField<*>) { - form.setField(field) - } - - inline fun getValidator(id: FieldId): T = formValidators[id] as T - - fun getError(id: FieldId): AddCustomTokenError? = formErrors[id] - - inline fun visitDataConverter(converter: FieldDataConverter): T { - form.visitDataConverter(converter) - return converter.getConvertedData() - } - - fun blockchainToName(blockchain: Blockchain, isDerivationPath: Boolean = false): String? { - return when { - isDerivationPath -> blockchain.derivationPath(DerivationStyle.LEGACY)?.rawPath - else -> { - when (blockchain) { - Blockchain.Unknown -> null - else -> blockchain.fullName - } - } - } - } - - // except network - fun tokensFieldsIsFilled(): Boolean { - val idsToCheck = listOf(ContractAddress, Name, Symbol, Decimals) - val fieldsToCheck = form.fieldList.filter { idsToCheck.contains(it.id) } - val validator = StringIsNotEmptyValidator() - fieldsToCheck.forEach { field -> - val error = validator.validate(field.data.value?.toString()) - if (error != null) return false - } - return true - } - - // except network - fun tokensAnyFieldsIsFilled(): Boolean { - val idsToCheck = listOf(ContractAddress, Name, Symbol, Decimals) - val fieldsToCheck = form.fieldList.filter { idsToCheck.contains(it.id) } - val validator = StringIsEmptyValidator() - val errorsList = fieldsToCheck.mapNotNull { field -> - validator.validate(field.data.value?.toString()) - } - return errorsList.isNotEmpty() - } - - fun networkIsSelected(): Boolean { - val network = getField(Network) - return network.data.value != Blockchain.Unknown - } - - fun derivationPathIsSelected(): Boolean { - val network = getField(DerivationPath) - return network.data.value != Blockchain.Unknown - } - - fun getCustomTokenType(): CustomTokenType { - return if (tokensAnyFieldsIsFilled() || tokensFieldsIsFilled()) { - CustomTokenType.Token - } else { - CustomTokenType.Blockchain - } - } - - fun gatherUserToken(): CustomCurrency.CustomToken? = try { - getToken() - } catch (ex: Exception) { - null - } - - fun gatherBlockchain(): CustomCurrency.CustomBlockchain? = try { - getBlockchain() - } catch (ex: Exception) { - null - } - - fun reset(cardTypesResolver: CardTypesResolver, card: CardDTO): AddCustomTokenState { - return this.copy( - appSavedCurrencies = null, - onTokenAddCallback = null, - cardDerivationStyle = null, - form = Form(createFormFields(cardTypesResolver, card, CustomTokenType.Blockchain)), - formErrors = emptyMap(), - foundToken = null, - warnings = emptySet(), - screenState = createInitialScreenState(card.settings.isHDWalletAllowed), - tangemTechServiceManager = null, - ) - } - - private fun getToken(): CustomCurrency.CustomToken { - return CustomCurrency.CustomToken.Converter(foundToken?.id, cardDerivationStyle) - .apply { visitDataConverter(this) } - .getConvertedData() - } - - private fun getBlockchain(): CustomCurrency.CustomBlockchain { - return CustomCurrency.CustomBlockchain.Converter(cardDerivationStyle) - .apply { visitDataConverter(this) } - .getConvertedData() - } - - companion object { - - /** - * If an user select derivation path (derivationNetwork) as Blockchain.Unknown, - * then we should use a blockchain from the mainNetwork to determine a DerivationPath - */ - internal fun getDerivationPath( - mainNetwork: Blockchain, - derivationNetwork: Blockchain, - derivationStyle: DerivationStyle?, - ): com.tangem.crypto.hdWallet.DerivationPath? { - // If we allow user to select derivations, we need to provide different derivations - // (Legacy style derivations). - // But the mainNetwork derivation depends on whether a user has a card - // with legacy derivations or new style derivations. - val derivationStyleToUse = if (derivationNetwork == Blockchain.Unknown) { - derivationStyle - } else { - DerivationStyle.LEGACY - } - return when (derivationNetwork) { - Blockchain.Unknown -> mainNetwork - else -> derivationNetwork - }.derivationPath(derivationStyleToUse) - } - - internal fun createFormFields( - cardTypesResolver: CardTypesResolver, - card: CardDTO, - type: CustomTokenType, - ): List> { - return listOf( - TokenField(ContractAddress), - TokenBlockchainField(Network, getNetworksList(cardTypesResolver, card, type)), - TokenField(Name), - TokenField(Symbol), - TokenField(Decimals), - TokenDerivationPathField(DerivationPath, getSupportedDerivations(card)), - ) - } - - /** - * Serves to determine the networks (blockchains & tokens) that can be selected by Form.Networks. - * Blockchain.Unknown - is the default selection - */ - private fun getNetworksList( - cardTypesResolver: CardTypesResolver, - card: CardDTO, - type: CustomTokenType, - ): List { - val evmBlockchains = Blockchain.values() - .filter { it.isEvm() } - .filter { card.isTestCard == it.isTestnet() } - - val additionalBlockchains = listOf( - Blockchain.Binance, - Blockchain.BinanceTestnet, - Blockchain.Solana, - Blockchain.SolanaTestnet, - Blockchain.Tron, - Blockchain.TronTestnet, - ) - - val supportedByCard = when (type) { - CustomTokenType.Blockchain -> card.supportedBlockchains(cardTypesResolver) - CustomTokenType.Token -> card.supportedTokens(cardTypesResolver) - } - val typedNetworksList = (evmBlockchains + additionalBlockchains) - .filter { supportedByCard.contains(it) } - .toMutableList() - - val default = Blockchain.Unknown - typedNetworksList.add(0, default) - - return typedNetworksList.sortByName() - } - - private fun createFormValidators(): Map> { - return mapOf( - ContractAddress to TokenContractAddressValidator(), - Network to TokenNetworkValidator(), - Name to TokenNameValidator(), - Symbol to TokenSymbolValidator(), - Decimals to TokenDecimalsValidator(), - ) - } - - private fun getSupportedDerivations(card: CardDTO): List { - val evmBlockchains = Blockchain.values() - .filter { card.isTestCard == it.isTestnet() && it.isEvm() } - .filter { it.isSupportedInApp() } - - return (listOf(Blockchain.Unknown) + evmBlockchains).sortByName() - } - - internal fun createInitialScreenState(showDerivationPathField: Boolean = false): ScreenState { - return ScreenState( - contractAddressField = ViewStates.TokenField(), - network = ViewStates.TokenField(), - name = ViewStates.TokenField(isEnabled = false), - symbol = ViewStates.TokenField(isEnabled = false), - decimals = ViewStates.TokenField(isEnabled = false), - derivationPath = ViewStates.TokenField(isVisible = showDerivationPathField), - addButton = ViewStates.AddButton(isEnabled = false), - ) - } - } - - class Converter : StringActionStateConverter { - private val jsonConverter: MoshiJsonConverter = MoshiJsonConverter.INSTANCE - private var builder: StringBuilder = StringBuilder() - - override fun convert(action: Action, stateHolder: DomainState): String? { - if (action !is AddCustomTokenAction) return null - - val state = stateHolder.addCustomTokensState - val fieldConverter = - FieldToJsonConverter( - listOf( - ContractAddress, - Network, - Name, - Symbol, - Decimals, - DerivationPath, - ), - jsonConverter, - ) - state.visitDataConverter(fieldConverter) - val errors = state.formErrors.map { - "${it.key}: ${it.value::class.java.simpleName}" - } - val warnings = state.warnings.map { it::class.java.simpleName } - - printAction(action, state) - printStateValue("fields", fieldConverter.getConvertedData()) - printStateValue("fieldErrors", toJson(errors)) - printStateValue("warnings", toJson(warnings)) - printStateValue("screenState", toJson(state.screenState)) - printMessage("------------------------------------------------------") - - val printed = builder.toString() - builder = StringBuilder() - - return printed - } - - private fun printStateValue(name: String, value: String) { - printMessage("$name: $value") - } - - private fun printAction(action: AddCustomTokenAction, state: AddCustomTokenState) { - printMessage("action: $action, state: ${state::class.java.simpleName}") - } - - private fun toJson(value: Any): String { - return jsonConverter.prettyPrint(value) - } - - private fun printMessage(message: String) { - builder.append("$message\n") - } - } -} - -private fun List.sortByName(): List = this.sortedBy { it.fullName } - -enum class CustomTokenType { - Token, Blockchain -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/Models.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/Models.kt deleted file mode 100644 index 444255ef7f..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/Models.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.tangem.domain.features.addCustomToken.redux - -/** -[REDACTED_AUTHOR] - */ -// describes state the screen, except the form fields -data class ScreenState( - val contractAddressField: ViewStates.TokenField, - val network: ViewStates.TokenField, - val name: ViewStates.TokenField, - val symbol: ViewStates.TokenField, - val decimals: ViewStates.TokenField, - val derivationPath: ViewStates.TokenField, - val addButton: ViewStates.AddButton, -) - -sealed class ViewStates { - data class TokenField( - val isLoading: Boolean = false, - val isEnabled: Boolean = true, - val isVisible: Boolean = true, - ) : ViewStates() - - data class AddButton( - val isEnabled: Boolean = true, - ) : ViewStates() -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/DomainState.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/DomainState.kt index 49cc235412..67be91d2dd 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/DomainState.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/DomainState.kt @@ -1,13 +1,9 @@ package com.tangem.domain.redux -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState import com.tangem.domain.redux.global.DomainGlobalState import org.rekotlin.StateType /** [REDACTED_AUTHOR] */ -data class DomainState( - val globalState: DomainGlobalState = DomainGlobalState(), - val addCustomTokensState: AddCustomTokenState = AddCustomTokenState(), -) : StateType \ No newline at end of file +data class DomainState(val globalState: DomainGlobalState = DomainGlobalState()) : StateType \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/DomainStore.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/DomainStore.kt index fbd4fe3735..a141cd5f9a 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/DomainStore.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/DomainStore.kt @@ -1,7 +1,5 @@ package com.tangem.domain.redux -import com.tangem.domain.DomainLayer -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenHub import com.tangem.domain.redux.global.DomainGlobalHub import org.rekotlin.Action import org.rekotlin.Store @@ -9,10 +7,7 @@ import org.rekotlin.Store /** [REDACTED_AUTHOR] */ -private val RE_STORE_HUBS: List> = listOf( - DomainGlobalHub(), - AddCustomTokenHub(), -) +private val RE_STORE_HUBS: List> = listOf(DomainGlobalHub()) val domainStore = Store( state = DomainState(), @@ -37,7 +32,6 @@ private fun reduce(action: Action, domainState: DomainState?): DomainState { assembleReducedDomainState } } - DomainLayer.actionStateLogger.log(reducedStatesByAction) return assembleReducedDomainState } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalAction.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalAction.kt index 2c9edb34a5..90bf41578e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalAction.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalAction.kt @@ -1,6 +1,5 @@ package com.tangem.domain.redux.global -import com.tangem.domain.DomainDialog import com.tangem.domain.models.scan.ScanResponse import org.rekotlin.Action @@ -10,5 +9,4 @@ import org.rekotlin.Action // TODO: refactoring: is alias for the GlobalAction sealed class DomainGlobalAction : Action { data class SaveScanNoteResponse(val scanResponse: ScanResponse) : DomainGlobalAction() - data class ShowDialog(val stateDialog: DomainDialog?) : DomainGlobalAction() } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt index f22a14aa64..e552550257 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalHub.kt @@ -47,9 +47,6 @@ private class DomainGlobalReducer : ReStoreReducer { ) state.copy(scanResponse = action.scanResponse) } - is DomainGlobalAction.ShowDialog -> { - state.copy(dialog = action.stateDialog) - } else -> state } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalState.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalState.kt index 36a97859c9..a424fbb683 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalState.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/global/DomainGlobalState.kt @@ -2,7 +2,6 @@ package com.tangem.domain.redux.global import com.tangem.datasource.api.paymentology.PaymentologyApiService import com.tangem.datasource.api.tangemTech.TangemTechService -import com.tangem.domain.DomainDialog import com.tangem.domain.models.scan.ScanResponse /** @@ -14,7 +13,6 @@ data class DomainGlobalState( val scanResponse: ScanResponse? = null, // val networkServices: NetworkServices = NetworkServices(), - val dialog: DomainDialog? = null, ) data class NetworkServices( diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/state/StateConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/state/StateConverter.kt deleted file mode 100644 index 8e99e0f538..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/state/StateConverter.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.domain.redux.state - -import com.tangem.domain.redux.DomainState -import org.rekotlin.Action - -/** -[REDACTED_AUTHOR] - */ -interface StringStateConverter { - fun convert(stateHolder: StateHolder): String -} - -interface StringActionStateConverter { - fun convert(action: Action, stateHolder: StateHolder): String? -} - -class ActionStateConvertersFactory { - private val stateConverters = mutableMapOf, StringActionStateConverter>() - - fun addConverter(classOfAction: Class, converter: StringActionStateConverter) { - stateConverters[classOfAction] = converter - } - - fun getConverter(action: Action): StringActionStateConverter? { - val converter = stateConverters.firstNotNullOfOrNull { (classOfAction, converter) -> - if (classOfAction.isAssignableFrom(action::class.java)) { - converter - } else { - null - } - } ?: return null - - return converter - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/state/StateLogger.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/state/StateLogger.kt deleted file mode 100644 index 576d398ea8..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/state/StateLogger.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.domain.redux.state - -import com.tangem.domain.features.BuildConfig -import com.tangem.domain.redux.DomainState -import org.rekotlin.Action -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - * Use it only in debug mode! - */ -internal interface ActionStateLogger { - fun log(reducedSates: List>) -} - -internal class ActionStateLoggerImpl : ActionStateLogger { - - val actionStateConvertersFactory = ActionStateConvertersFactory() - - override fun log(reducedSates: List>) { - if (!BuildConfig.LOG_ENABLED) return - - logStates(reducedSates) - } - - private fun logStates(reducedSates: List>) { - reducedSates.forEach { (action, domainState) -> - val messageToPrint = actionStateConvertersFactory.getConverter(action) - ?.convert(action, domainState) - ?: return@forEach - - Timber.d(messageToPrint) - } - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/state/StringStateConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/state/StringStateConverter.kt new file mode 100644 index 0000000000..f0660f284f --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/state/StringStateConverter.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.redux.state + +/** +[REDACTED_AUTHOR] + */ +interface StringStateConverter { + fun convert(stateHolder: StateHolder): String +} \ No newline at end of file