diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 8f2af51f2d..9d98629279 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -5,6 +5,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt index ad6edeb6d9..e1c1c81256 100644 --- a/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/BaseScenarios.kt @@ -9,7 +9,7 @@ import com.tangem.tap.domain.sdk.mocks.MockContent import com.tangem.tap.domain.sdk.mocks.MockProvider import io.qameta.allure.kotlin.Allure.step -fun BaseTestCase.openMainScreen( +fun BaseTestCase.scanCard( productType: ProductType? = null, mockContent: MockContent? = null, alreadyActivatedDialogIsShown : Boolean = false @@ -32,6 +32,20 @@ fun BaseTestCase.openMainScreen( AlreadyUsedWalletDialogPageObject { thisIsMyWalletButton.click() } } } +} + +fun BaseTestCase.openMainScreen( + productType: ProductType? = null, + mockContent: MockContent? = null, + alreadyActivatedDialogIsShown: Boolean = false +) { + step("Scan card") { + scanCard( + productType = productType, + mockContent = mockContent, + alreadyActivatedDialogIsShown = alreadyActivatedDialogIsShown + ) + } step("Assert 'Main' screen is displayed") { onMainScreen { screenContainer.assertIsDisplayed() } } diff --git a/app/src/androidTest/kotlin/com/tangem/scenarios/OnboardingScenarios.kt b/app/src/androidTest/kotlin/com/tangem/scenarios/OnboardingScenarios.kt new file mode 100644 index 0000000000..f13cbf8b67 --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/scenarios/OnboardingScenarios.kt @@ -0,0 +1,112 @@ +package com.tangem.scenarios + +import com.tangem.common.BaseTestCase +import com.tangem.screens.onOnboardingScreen +import io.qameta.allure.kotlin.Allure.step + +fun BaseTestCase.checkBackupScreen() { + step("Assert 'Creating a backup' top bar title is displayed") { + onOnboardingScreen { creatingBackupTobBarTitle.assertIsDisplayed() } + } + step("Assert 'Prepare your scan or ring' title is displayed") { + onOnboardingScreen { prepareYouCardOrRingTitle.assertIsDisplayed() } + } + step("Assert 'Start backup' text is displayed") { + onOnboardingScreen { startBackupText.assertIsDisplayed() } + } + step("Assert 'Scan card' button is displayed") { + onOnboardingScreen { scanCardButton.assertIsDisplayed() } + } + step("Assert 'Skip for later' button is not displayed") { + onOnboardingScreen { skipForLaterButton.assertIsNotDisplayed() } + } +} + +fun BaseTestCase.checkCreateWalletScreenForWalletNoWallets() { + step("Assert 'Create wallet' tob bar title is displayed") { + onOnboardingScreen { createWalletTopBarTitle.assertIsDisplayed() } + } + step("Assert top bar 'Back' button is displayed") { + onOnboardingScreen { topBarBackButton.assertIsDisplayed() } + } + step("Assert top bar 'More' button is displayed") { + onOnboardingScreen { topBarMoreButton.assertIsDisplayed() } + } + step("Assert 'Create wallet' title is displayed") { + onOnboardingScreen { createWalletTitle.assertIsDisplayed() } + } + step("Assert 'Create wallet' text is displayed") { + onOnboardingScreen { createWalletText.assertIsDisplayed() } + } + step("Assert 'Create wallet' button is displayed") { + onOnboardingScreen { createWalletButton.assertIsDisplayed() } + } +} + +fun BaseTestCase.openAndCheckBackupScreenForWalletNoWallets() { + step("Click on 'Create wallet' button") { + onOnboardingScreen { createWalletButton.performClick() } + } + step("Assert 'Getting started' top bar title is displayed") { + onOnboardingScreen { gettingStartedTobBarTitle.assertIsDisplayed() } + } + step("Assert 'Backup wallet' title is displayed") { + onOnboardingScreen { backupWalletTitle.assertIsDisplayed() } + } + step("Assert 'Backup wallet' text is displayed") { + onOnboardingScreen { backupWalletText.assertIsDisplayed() } + } + step("Assert 'Backup now' button is displayed") { + onOnboardingScreen { backupWalletButton.assertIsDisplayed() } + } + step("Assert 'Skip for later' button is not displayed") { + onOnboardingScreen { skipForLaterButton.assertIsNotDisplayed() } + } +} + +fun BaseTestCase.checkCreateWalletScreenForWallet2NoWallets() { + step("Assert 'Create a wallet' top bar title is displayed") { + onOnboardingScreen { createWalletTopBarTitle.assertIsDisplayed() } + } + step("Assert top bar 'Back' button is displayed") { + onOnboardingScreen { topBarBackButton.assertIsDisplayed() } + } + step("Assert top bar 'More' button is displayed") { + onOnboardingScreen { topBarMoreButton.assertIsDisplayed() } + } + step("Assert 'Generate keys privately' title is displayed") { + onOnboardingScreen { generateKeysPrivatelyTitle.assertIsDisplayed() } + } + step("Assert 'Generate keys privately' text is displayed") { + onOnboardingScreen { generateKeysPrivatelyText.assertIsDisplayed() } + } + step("Assert 'Create wallet' button is displayed") { + onOnboardingScreen { createWalletButton.assertIsDisplayed() } + } + step("Assert 'Other options' button is displayed") { + onOnboardingScreen { otherOptionsButton.assertIsDisplayed() } + } +} +fun BaseTestCase.openAndCheckBackupScreenForWallet2NoWallets() { + step("Click on 'Create wallet' button") { + onOnboardingScreen { createWalletButton.performClick() } + } + step("Assert 'Creating a backup' top bar title is displayed") { + onOnboardingScreen { creatingBackupTobBarTitle.assertIsDisplayed() } + } + step("Assert 'No backup devices' title is displayed") { + onOnboardingScreen { noBackupDevicesTitle.assertIsDisplayed() } + } + step("Assert 'No backup devices' text is displayed") { + onOnboardingScreen { noBackupDevicesText.assertIsDisplayed() } + } + step("Assert 'Add card or ring' button is displayed") { + onOnboardingScreen { addCardOrRingButton.assertIsDisplayed() } + } + step("Assert 'Finalize backup' button is displayed") { + onOnboardingScreen { finalizeBackupButton.assertIsDisplayed() } + } + step("Assert 'Skip for later' button is not displayed") { + onOnboardingScreen { skipForLaterButton.assertIsNotDisplayed() } + } +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/OnboardingPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/OnboardingPageObject.kt new file mode 100644 index 0000000000..a49cb3063b --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/screens/OnboardingPageObject.kt @@ -0,0 +1,151 @@ +package com.tangem.screens + +import androidx.compose.ui.test.SemanticsNodeInteractionsProvider +import com.tangem.common.BaseTestCase +import com.tangem.core.ui.test.BaseButtonTestTags +import com.tangem.core.ui.test.StoriesScreenTestTags +import com.tangem.core.ui.test.TopAppBarTestTags +import com.tangem.wallet.R +import io.github.kakaocup.compose.node.element.ComposeScreen +import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen +import io.github.kakaocup.compose.node.element.KNode +import io.github.kakaocup.kakao.common.utilities.getResourceString +import io.github.kakaocup.kakao.common.views.KView +import io.github.kakaocup.kakao.text.KButton +import com.tangem.features.onboarding.v2.impl.R as OnboardingImplR + +class OnboardingPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : + ComposeScreen(semanticsProvider = semanticsProvider) { + + val topBarBackButton: KNode = child { + hasTestTag(TopAppBarTestTags.CLOSE_BUTTON) + useUnmergedTree = true + } + + val createWalletTopBarTitle: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_header)) + useUnmergedTree = true + } + + val createWalletTitle: KNode = child { + hasTestTag(StoriesScreenTestTags.TITLE) + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_header)) + useUnmergedTree = true + } + + val createWalletText: KNode = child { + hasTestTag(StoriesScreenTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_body)) + useUnmergedTree = true + } + + val gettingStartedTobBarTitle: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(OnboardingImplR.string.onboarding_getting_started)) + useUnmergedTree = true + } + + val creatingBackupTobBarTitle: KNode = child { + hasTestTag(TopAppBarTestTags.TITLE) + hasText(getResourceString(OnboardingImplR.string.onboarding_navbar_title_creating_backup)) + useUnmergedTree = true + } + + val prepareYouCardOrRingTitle: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_title_scan_origin_card)) + useUnmergedTree = true + } + + val startBackupText: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_subtitle_scan_primary)) + useUnmergedTree = true + } + + val topBarMoreButton: KNode = child { + hasTestTag(TopAppBarTestTags.MORE_BUTTON) + } + + val backupWalletTitle: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_wallet_info_title_first)) + useUnmergedTree = true + } + + val backupWalletText: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_wallet_info_subtitle_first)) + useUnmergedTree = true + } + + val generateKeysPrivatelyTitle: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_options_title)) + useUnmergedTree = true + } + + val generateKeysPrivatelyText: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_options_message)) + useUnmergedTree = true + } + + val noBackupDevicesTitle: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_subtitle_no_backup_cards)) + useUnmergedTree = true + } + + val noBackupDevicesText: KNode = child { + hasText(getResourceString(OnboardingImplR.string.onboarding_title_no_backup_cards)) + useUnmergedTree = true + } + + val createWalletButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_button_create_wallet)) + useUnmergedTree = true + } + + val backupWalletButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_button_backup_now)) + useUnmergedTree = true + } + + val scanCardButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_button_backup_card)) + useUnmergedTree = true + } + + val otherOptionsButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_create_wallet_options_button_options)) + useUnmergedTree = true + } + + val addCardOrRingButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_button_add_backup_card)) + useUnmergedTree = true + } + + val finalizeBackupButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_button_finalize_backup)) + useUnmergedTree = true + } + + val skipForLaterButton: KNode = child { + hasTestTag(BaseButtonTestTags.TEXT) + hasText(getResourceString(OnboardingImplR.string.onboarding_button_skip_backup)) + useUnmergedTree = true + } + + val enableNFCAlert: KView = KView { + withId(R.id.alertTitle) + } + + val cancelButton: KButton = KButton { + withId(android.R.id.button2) + } +} + +internal fun BaseTestCase.onOnboardingScreen(function: OnboardingPageObject.() -> Unit) = + onComposeScreen(composeTestRule, function) \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/tangem/screens/StoriesPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/StoriesPageObject.kt index 63934bb015..f9666ceeb4 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/StoriesPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/StoriesPageObject.kt @@ -3,18 +3,12 @@ package com.tangem.screens import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import com.tangem.common.BaseTestCase import com.tangem.core.ui.test.StoriesScreenTestTags -import com.tangem.wallet.R import io.github.kakaocup.compose.node.element.ComposeScreen import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen import io.github.kakaocup.compose.node.element.KNode -import io.github.kakaocup.kakao.common.views.KView -import io.github.kakaocup.kakao.text.KButton class StoriesPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : - ComposeScreen( - semanticsProvider = semanticsProvider, - viewBuilderAction = { hasTestTag(StoriesScreenTestTags.SCREEN_CONTAINER) } - ) { + ComposeScreen(semanticsProvider = semanticsProvider) { val scanButton: KNode = child { hasTestTag(StoriesScreenTestTags.SCAN_BUTTON) @@ -23,14 +17,6 @@ class StoriesPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : val orderButton: KNode = child { hasTestTag(StoriesScreenTestTags.ORDER_BUTTON) } - - val enableNFCAlert: KView = KView { - withId(R.id.alertTitle) - } - - val cancelButton: KButton = KButton { - withId(android.R.id.button2) - } } internal fun BaseTestCase.onStoriesScreen(function: StoriesPageObject.() -> Unit) = diff --git a/app/src/androidTest/kotlin/com/tangem/tests/OnboardingTest.kt b/app/src/androidTest/kotlin/com/tangem/tests/OnboardingTest.kt new file mode 100644 index 0000000000..c1e7d608de --- /dev/null +++ b/app/src/androidTest/kotlin/com/tangem/tests/OnboardingTest.kt @@ -0,0 +1,64 @@ +package com.tangem.tests + +import com.tangem.common.BaseTestCase +import com.tangem.scenarios.* +import com.tangem.tap.domain.sdk.mocks.content.ShibaNoBackupMockContent +import com.tangem.tap.domain.sdk.mocks.content.ShibaNoBackupNoWalletsMockContent +import com.tangem.tap.domain.sdk.mocks.content.Wallet2NoBackupMockContent +import com.tangem.tap.domain.sdk.mocks.content.Wallet2NoBackupNoWalletsMockContent +import dagger.hilt.android.testing.HiltAndroidTest +import io.qameta.allure.kotlin.AllureId +import io.qameta.allure.kotlin.junit4.DisplayName +import org.junit.Test + +@HiltAndroidTest +class OnboardingTest : BaseTestCase() { + + @AllureId("248") + @DisplayName("Onboarding: 'Shiba' no wallets backup screen test") + @Test + fun shibaNoWalletsBackupScreenTest() { + setupHooks().run { + scanCard(mockContent = ShibaNoBackupNoWalletsMockContent) + checkCreateWalletScreenForWalletNoWallets() + openAndCheckBackupScreenForWalletNoWallets() + } + } + + @AllureId("3989") + @DisplayName("Onboarding: 'Shiba' with wallets backup screen test") + @Test + fun shibaBackupScreenTest() { + setupHooks().run { + scanCard( + mockContent = ShibaNoBackupMockContent, + alreadyActivatedDialogIsShown = true + ) + checkBackupScreen() + } + } + + @AllureId("246") + @DisplayName("Onboarding: 'Wallet 2' no wallets backup screen test") + @Test + fun wallet2NoWalletsBackupScreenTest() { + setupHooks().run { + scanCard(mockContent = Wallet2NoBackupNoWalletsMockContent) + checkCreateWalletScreenForWallet2NoWallets() + openAndCheckBackupScreenForWallet2NoWallets() + } + } + + @AllureId("3990") + @DisplayName("Onboarding: 'Wallet 2' with wallets backup screen test") + @Test + fun wallet2BackupScreenTest() { + setupHooks().run { + scanCard( + mockContent = Wallet2NoBackupMockContent, + alreadyActivatedDialogIsShown = true + ) + checkBackupScreen() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index c047e66353..b1ab10db8d 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -263,7 +263,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { tangemSdkManager = injectedTangemSdkManager backupServiceHolder.createAndSetService(cardSdkConfigRepository.sdk, this) - backupService = backupServiceHolder.backupService.get()!! // will be deleted eventually + backupService = requireNotNull(backupServiceHolder.backupService.get()) // will be deleted eventually lockUserWalletsTimer = LockUserWalletsTimer( context = this, @@ -370,8 +370,8 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - val fromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) ?: false - if (fromPush) { + val isFromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) == true + if (isFromPush) { analyticsEventsHandler.send(Push.PushNotificationOpened) } @@ -402,8 +402,8 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { } override fun dispatchTouchEvent(event: MotionEvent): Boolean { - val result = WindowObscurationObserver.dispatchTouchEvent(event, analyticsEventsHandler) - return if (result) super.dispatchTouchEvent(event) else false + val isHandled = WindowObscurationObserver.dispatchTouchEvent(event, analyticsEventsHandler) + return if (isHandled) super.dispatchTouchEvent(event) else false } override fun dispatchKeyEvent(event: KeyEvent): Boolean { @@ -447,9 +447,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { getPolkadotCheckHasImmortalUseCase() .flowWithLifecycle(lifecycle, minActiveState = Lifecycle.State.CREATED) .distinctUntilChanged() - .collect { + .collect { (_, hasImmortalTransaction) -> analyticsEventsHandler.send( - WalletScreenAnalyticsEvent.Token.PolkadotImmortalTransactions(it.second), + WalletScreenAnalyticsEvent.Token.PolkadotImmortalTransactions(hasImmortalTransaction), ) } } diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 9a3f617366..164150ae73 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -88,7 +88,7 @@ lateinit var store: Store lateinit var foregroundActivityObserver: ForegroundActivityObserver internal lateinit var derivationsFinder: DerivationsFinder -abstract class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider { +open class TangemApplication : Application(), ImageLoaderFactory, Configuration.Provider { // region DI private val entryPoint: ApplicationEntryPoint diff --git a/app/src/main/java/com/tangem/tap/common/TestActions.kt b/app/src/main/java/com/tangem/tap/common/TestActions.kt index 5dc923cf58..95edcd851e 100644 --- a/app/src/main/java/com/tangem/tap/common/TestActions.kt +++ b/app/src/main/java/com/tangem/tap/common/TestActions.kt @@ -7,7 +7,7 @@ package com.tangem.tap.common object TestActions { // It used only for the test actions in debug or debug_beta builds - var testAmountInjectionForWalletManagerEnabled = false + var isTestAmountInjectionForWalletManagerEnabled = false } typealias TestAction = Pair Unit> \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt index 4f32eee854..a94aa24beb 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt @@ -58,7 +58,7 @@ private class BlockchainSdkErrorConverter( if (value.customMessage.contains(DemoTransactionSender.ID)) return emptyMap() if (value is BlockchainSdkError.WrappedTangemError) { - return (value.tangemError as? TangemSdkError)?.let { cardSdkErrorConverter.convert(it) } ?: emptyMap() + return (value.tangemError as? TangemSdkError)?.let { cardSdkErrorConverter.convert(it) }.orEmpty() } return mapOf( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Chat.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Chat.kt index f4b9dbc61f..e239d1ccc6 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Chat.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Chat.kt @@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent */ sealed class Chat( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent("Chat", event, params) { class ScreenOpened : Chat("Chat Screen Opened") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt index f35a636601..a4fcb32fe0 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt @@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent sealed class Onboarding( category: String, event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent(category, event, params) { class Started : Onboarding("Onboarding", "Onboarding Started") @@ -16,7 +16,7 @@ sealed class Onboarding( sealed class CreateWallet( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Onboarding("Onboarding / Create Wallet", event, params) { class ScreenOpened : CreateWallet("Create Wallet Screen Opened") @@ -36,7 +36,7 @@ sealed class Onboarding( sealed class Backup( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Onboarding("Onboarding / Backup", event, params) { class ScreenOpened : Backup("Backup Screen Opened") @@ -64,7 +64,7 @@ sealed class Onboarding( sealed class Topup( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Onboarding("Onboarding / Top Up", event, params) { class ScreenOpened : Topup("Activation Screen Opened") @@ -79,7 +79,7 @@ sealed class Onboarding( sealed class Twins( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Onboarding("Onboarding / Twins", event, params) { class ScreenOpened : Twins("Twinning Screen Opened") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt index 4234256e8c..3ac65172d2 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt @@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent sealed class Settings( category: String = "Settings", event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent(category, event, params) { class ScreenOpened : Settings(event = "Settings Screen Opened") @@ -17,7 +17,7 @@ sealed class Settings( sealed class CardSettings( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Settings("Settings / Card Settings", event, params) { class ButtonFactoryReset : CardSettings("Button - Factory Reset") @@ -54,7 +54,7 @@ sealed class Settings( sealed class AppSettings( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Settings(category = "Settings / App Settings", event = event, params = params) { class SaveWalletSwitcherChanged(state: AnalyticsParam.OnOffState) : AppSettings( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt index a9a52c5e25..d34334ec34 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt @@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent */ sealed class SignIn( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent("Sign In", event, params) { class ScreenOpened : SignIn(event = "Sign In Screen Opened") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt index 9f6214a78a..bac4bcc6d2 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt @@ -9,12 +9,12 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM sealed class Token( category: String, event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent(category, event, params) { sealed class Receive( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Token("Token / Receive", event, params) { class ScreenOpened( @@ -29,7 +29,7 @@ sealed class Token( sealed class Topup( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Token("Token / Topup", event, params) { class ScreenOpened : Topup("Top Up Screen Opened") @@ -38,7 +38,7 @@ sealed class Token( sealed class Withdraw( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : Token("Token / Withdraw", event, params) { class ScreenOpened : Withdraw("Withdraw Screen Opened") diff --git a/app/src/main/java/com/tangem/tap/common/clipboard/DefaultClipboardManager.kt b/app/src/main/java/com/tangem/tap/common/clipboard/DefaultClipboardManager.kt index 73f2ebd5ef..1c33fe0909 100644 --- a/app/src/main/java/com/tangem/tap/common/clipboard/DefaultClipboardManager.kt +++ b/app/src/main/java/com/tangem/tap/common/clipboard/DefaultClipboardManager.kt @@ -19,6 +19,7 @@ internal class DefaultClipboardManager(private val clipboardManager: AndroidClip clipboardManager.setPrimaryClip(clip) } + @Suppress("UseIsNullOrEmpty") override fun getText(default: String?): String? { val clip = clipboardManager.primaryClip diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt index 5d48278690..62bf0eb950 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt @@ -90,6 +90,6 @@ fun Store.dispatchNavigationAction(action: AppRouter.() -> Unit) { inline fun Store.inject(getDependency: DaggerGraphState.() -> T?): T { return requireNotNull(state.daggerGraphState.getDependency()) { - "${T::class.simpleName} isn't initialized " + "${T::class.simpleName.orEmpty()} isn't initialized " } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 55ebaa1ea3..3aeeb7d9cd 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -22,9 +22,9 @@ import timber.log.Timber ) @Suppress("MagicNumber") suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result = try { - if (isDemoCard || TestActions.testAmountInjectionForWalletManagerEnabled) { + if (isDemoCard || TestActions.isTestAmountInjectionForWalletManagerEnabled) { delay(500) - TestActions.testAmountInjectionForWalletManagerEnabled = false + TestActions.isTestAmountInjectionForWalletManagerEnabled = false Result.Success(wallet) } else { update() @@ -35,7 +35,7 @@ suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result = try val networkConnectionManager = store.inject(DaggerGraphState::networkConnectionManager) if (!networkConnectionManager.isOnline) { - Result.Failure(TapError.NoInternetConnection) + Result.Failure(TapError.NoInternetConnection()) } else { val blockchain = wallet.blockchain val amountToCreateAccount = blockchain.amountToCreateAccount(this, wallet.getFirstToken()) diff --git a/app/src/main/java/com/tangem/tap/common/log/TangemAppLoggerInitializer.kt b/app/src/main/java/com/tangem/tap/common/log/TangemAppLoggerInitializer.kt index a2011ff5e7..924cbede00 100644 --- a/app/src/main/java/com/tangem/tap/common/log/TangemAppLoggerInitializer.kt +++ b/app/src/main/java/com/tangem/tap/common/log/TangemAppLoggerInitializer.kt @@ -44,6 +44,7 @@ class TangemAppLoggerInitializer( } } + @Suppress("BooleanPropertyNaming") private companion object { val IS_LOG_ENABLED: Boolean = BuildConfig.LOG_ENABLED val PERMITTED_PRIORITY = listOf(Log.ERROR, Log.INFO) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 090cbc72f8..598ad3be24 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -20,6 +20,6 @@ data class GlobalState( typealias CryptoCurrencyName = String data class OnboardingState( - val onboardingStarted: Boolean = false, + val isOnboardingStarted: Boolean = false, val shouldResetOnCreate: Boolean = false, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/core/navigation/email/AndroidEmailSender.kt b/app/src/main/java/com/tangem/tap/core/navigation/email/AndroidEmailSender.kt index 8d7beb3948..12e69c91b8 100644 --- a/app/src/main/java/com/tangem/tap/core/navigation/email/AndroidEmailSender.kt +++ b/app/src/main/java/com/tangem/tap/core/navigation/email/AndroidEmailSender.kt @@ -61,9 +61,9 @@ internal class AndroidEmailSender : EmailSender { .setSubject(email.subject) .setText(email.message) - email.attachment?.let { + email.attachment?.let { file -> builder.setStream( - FileProvider.getUriForFile(activity, "${activity.packageName}.provider", it), + FileProvider.getUriForFile(activity, "${activity.packageName}.provider", file), ) } diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt index dffd82ec11..67e1652a6e 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -39,7 +39,7 @@ class TangemSigner( TangemSignerResponse( totalSignedHashes = result.data.totalSignedHashes, remainingSignatures = result.data.remainingSignatures, - isRing = result.data.batchId?.let(::isRing) ?: false, + isRing = result.data.batchId?.let(::isRing) == true, ), ) if (continuation.isActive) { @@ -85,7 +85,7 @@ class TangemSigner( TangemSignerResponse( totalSignedHashes = result.data.totalSignedHashes, remainingSignatures = result.data.remainingSignatures, - isRing = result.data.batchId?.let(::isRing) ?: false, + isRing = result.data.batchId?.let(::isRing) == true, ), ) if (continuation.isActive) { diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 1fc37a0267..2f1677fc5d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -20,23 +20,23 @@ sealed class TapError( override val args: List? = null, ) : Throwable(), TapErrors, ArgError { - object UnknownError : TapError(R.string.send_error_unknown) + class UnknownError : TapError(R.string.send_error_unknown) open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) - object NoInternetConnection : TapError(R.string.wallet_notification_no_internet) + class NoInternetConnection : TapError(R.string.wallet_notification_no_internet) sealed class WalletManager { class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount) class InternalError(message: String) : CustomError(message) - object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) + class BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) } } sealed class TapSdkError(override val messageResId: Int?) : TangemError(code = 50100) { override var customMessage: String = code.toString() - object CardForDifferentApp : TapSdkError(R.string.alert_unsupported_card) - object CardNotSupportedByRelease : TapSdkError(R.string.error_wrong_card_type) + class CardForDifferentApp : TapSdkError(R.string.alert_unsupported_card) + class CardNotSupportedByRelease : TapSdkError(R.string.error_wrong_card_type) } fun TapErrors.assembleErrors(): MutableList?>> { diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt index b86f57ce33..cd94802690 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt +++ b/app/src/main/java/com/tangem/tap/domain/card/DefaultResetCardUseCase.kt @@ -70,7 +70,7 @@ internal class DefaultResetCardUseCase( null } - type?.let { + if (type != null) { tangemSdkManager.setUserCodeRequestPolicy(policy = UserCodeRequestPolicy.Always(type)) } } diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index 5b44a91292..17ce65a238 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -119,14 +119,14 @@ internal class LegacyScanProcessor @Inject constructor( } private fun sendAnalytics(analyticsEvent: AnalyticsEvent?, scanResponse: ScanResponse) { - analyticsEvent?.let { + analyticsEvent?.let { event -> // this workaround needed to send CardWasScannedEvent without adding a context val interceptor = CardContextInterceptor(scanResponse) - val params = it.params.toMutableMap() + val params = event.params.toMutableMap() interceptor.intercept(params) - it.params = params.toMap() + event.params = params.toMap() - Analytics.send(it) + Analytics.send(event) } } diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt index abf3f1f23d..6076dd07d3 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/UseCaseScanProcessor.kt @@ -33,8 +33,8 @@ internal object UseCaseScanProcessor { return scanCardUseCase(cardId, allowsRequestAccessCodeFromRepository) .fold( - ifLeft = { - val error = scanCardExceptionConverter.convertBack(it) + ifLeft = { scanCardException -> + val error = scanCardExceptionConverter.convertBack(scanCardException) Analytics.sendErrorEvent(TangemSdkErrorEvent(error)) CompletionResult.Failure(error) diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt index b36fb89f0e..cd7a62cab3 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/ScanChainException.kt @@ -8,10 +8,10 @@ sealed class ScanChainException : ScanCardException.ChainException() { /** * May be returned from [DisclaimerChain] * */ - data object DisclaimerWasCanceled : ScanChainException() { + class DisclaimerWasCanceled : ScanChainException() { @Suppress("UnusedPrivateMember") - private fun readResolve(): Any = DisclaimerWasCanceled + private fun readResolve(): Any = DisclaimerWasCanceled() } /** diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/MockProvider.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/MockProvider.kt index 3019dbeba0..c184799348 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/MockProvider.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/MockProvider.kt @@ -13,19 +13,19 @@ object MockProvider { private var content: MockContent = getMockContent(ProductType.Wallet) - private var emulateError: Boolean = false + private var isEmulatingError: Boolean = false private var emulatedError: TangemError = TangemSdkError.TagLost() fun setEmulateError(error: TangemError? = null) { - emulateError = true + isEmulatingError = true error?.let { emulatedError = it } } fun resetEmulateError() { - emulateError = false + isEmulatingError = false } fun setMocks(productType: ProductType) { @@ -74,7 +74,7 @@ object MockProvider { } private fun CompletionResult.Success.orFailure(): CompletionResult { - return if (emulateError) { + return if (isEmulatingError) { CompletionResult.Failure(emulatedError) } else { this diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/BackupWalletMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/BackupWalletMockContent.kt index c2c8d9ba23..284c59ef1a 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/BackupWalletMockContent.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/BackupWalletMockContent.kt @@ -168,52 +168,52 @@ object BackupWalletMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch - publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), - chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge - publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), - chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( - publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), - chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), ) @@ -234,24 +234,24 @@ object BackupWalletMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), primaryCard = primaryCard, ) diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/DevWalletMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/DevWalletMockContent.kt index 29b0ec5fee..e37fa805a0 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/DevWalletMockContent.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/DevWalletMockContent.kt @@ -168,52 +168,52 @@ object DevWalletMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch - publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), - chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge - publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), - chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( - publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), - chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), ) @@ -234,24 +234,24 @@ object DevWalletMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), primaryCard = primaryCard, ) diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/ShibaNoBackupMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/ShibaNoBackupMockContent.kt new file mode 100644 index 0000000000..fc03dab84c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/ShibaNoBackupMockContent.kt @@ -0,0 +1,270 @@ +package com.tangem.tap.domain.sdk.mocks.content + +import com.tangem.common.SuccessResponse +import com.tangem.common.card.* +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ProductType +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.operations.attestation.Attestation +import com.tangem.operations.backup.PrimaryCard +import com.tangem.operations.derivation.DerivationTaskResponse +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.sdk.api.CreateProductWalletTaskResponse +import com.tangem.tap.domain.sdk.mocks.MockContent +import java.util.Date + +object ShibaNoBackupMockContent : MockContent { + + private val primaryCard = PrimaryCard( + cardId = "AF02010000000002", + batchId = "AF02", + cardPublicKey = byteArrayOf(2, 48, 4, 54, -34, -33, 22, 53, -92, 8, 72, 50, 69, 1, 105, -53, 9, -126, -23, -61, -62, -85, -24, -112, -32, 76, -88, -66, -25, -83, -70, -95, 90), + linkingKey = byteArrayOf( + 2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121, + -57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91, + ), + existingWalletsCount = 3, isHDWalletAllowed = true, + issuer = Card.Issuer( + name = "TANGEM SDK", + publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34), + ), + manufacturer = Card.Manufacturer( + name = "TANGEM", + manufactureDate = Date(1743759687), + signature = byteArrayOf(44, 8, 116, 111, -57, 20, -93, 101, -104, 97, -104, -15, 58, -8, 41, -53, 95, 99, 107, 47, -29, -118, 41, 64, 22, 94, 33, -81, 114, -47, 10, -16, 97, 14, 94, -36, -82, -108, 74, -9, 35, -38, 66, 67, -116, -55, 65, -30, -58, -33, 31, 120, -45, 42, -3, -120, -74, -97, 97, -102, -13, -28, 29, -41), + ), + walletCurves = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519, EllipticCurve.Bls12381G2Aug), + firmwareVersion = FirmwareVersion( + major = 6, + minor = 21, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + isKeysImportAllowed = false, + certificate = null, + ) + + override val cardDto = CardDTO( + cardId = "AF02010000000002", + batchId = "AF02", + cardPublicKey = byteArrayOf(2, 48, 4, 54, -34, -33, 22, 53, -92, 8, 72, 50, 69, 1, 105, -53, 9, -126, -23, -61, -62, -85, -24, -112, -32, 76, -88, -66, -25, -83, -70, -95, 90), + firmwareVersion = CardDTO.FirmwareVersion( + major = 6, + minor = 21, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + manufacturer = CardDTO.Manufacturer( + name = "TANGEM SDK", + manufactureDate = Date(1649635200000), + signature = byteArrayOf(-20, 98, -101, 94, -23, 73, 122, -21, 74, 76, 79, -55, -102, -62, -30, 44, -38, 118, 75, 121, -36, 118, -62, 60, -38, -63, 33, -14, -98, -69, 112, 22, 48, -43, 47, 65, -61, -56, 38, -94, -45, 44, 95, -22, 6, -31, -40, -25, 42, 38, 94, -99, 71, 98, -33, 27, -102, 30, 52, -7, 51, -27, 84, 66), + ), + issuer = CardDTO.Issuer( + name = "TANGEM", + publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2), + ), + settings = CardDTO.Settings( + securityDelay = 15000, + maxWalletsCount = 20, + isSettingAccessCodeAllowed = true, + isSettingPasscodeAllowed = false, + isResettingUserCodesAllowed = true, + isLinkedTerminalEnabled = true, + isBackupAllowed = true, + supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None), + isFilesAllowed = true, + isHDWalletAllowed = true, + isKeysImportAllowed = true, + ), + userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true), + linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None, + isAccessCodeSet = true, + isPasscodeSet = false, + supportedCurves = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Secp256r1, + EllipticCurve.Bls12381G2, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Bls12381G2Pop, + EllipticCurve.Bip0340, + ), + wallets = listOf( + CardDTO.Wallet( + publicKey = byteArrayOf(2, -74, 50, 45, -105, 119, 115, 87, -7, -110, 33, 10, -41, -9, -41, 1, 63, -73, 31, -77, 86, 66, -47, -31, 16, 32, 63, 122, 87, 94, 41, 68, 125), + chainCode = byteArrayOf(74, -90, -85, -117, -116, -78, 98, 63, 83, 12, 43, -69, 43, -54, -119, -62, 80, 107, -127, 53, 73, 108, 114, -102, 81, -123, 68, 92, 65, -25, 107, 86), + curve = EllipticCurve.Secp256k1, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 0, + remainingSignatures = null, + index = 0, + hasBackup = false, + derivedKeys = mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + ), + DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + ), + DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + ), + ), + extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(2, -74, 50, 45, -105, 119, 115, 87, -7, -110, 33, 10, -41, -9, -41, 1, 63, -73, 31, -77, 86, 66, -47, -31, 16, 32, 63, 122, 87, 94, 41, 68, 125), + chainCode = byteArrayOf(80, 13, -8, -108, -35, 116, -92, 125, -65, -28, 85, 72, -113, 59, 83, 13, 5, -83, -102, 123, 124, -22, 94, 108, -71, 95, 65, -2, 38, 38, -108, 14), + ), + isImported = false, + ), + CardDTO.Wallet( + publicKey = byteArrayOf(-123, -48, -77, -113, -63, -124, -18, 36, -14, -127, 104, 89, 48, 103, 69, -12, 107, -103, 64, -2, 97, 126, -78, -99, -9, -72, 94, -68, 30, 43, 38, 42), + chainCode = byteArrayOf(104, 109, 56, -52, 125, -119, -49, -51, -46, -122, -111, 75, 51, 103, 15, 25, -101, 72, -101, 101, -128, -2, -51, 3, 74, 62, -49, -6, -95, 91, -104, -58), + curve = EllipticCurve.Ed25519, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 0, + remainingSignatures = null, + index = 1, + hasBackup = false, + derivedKeys = emptyMap(), + extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(-65, -53, -62, -12, -57, -32, -38, -9, -128, -52, -83, -61, 73, 39, 41, 15, -74, -97, 38, 52, -101, 63, 74, -56, -20, 15, 57, -127, 114, -93, -17, -109), + chainCode = byteArrayOf(-81, 15, 125, 28, -115, -22, 87, 81, 87, -123, -25, -74, 86, 2, 1, 110, -115, 65, -110, 63, -64, 83, -93, -97, -104, 123, 12, -26, 94, 27, 84, -6), + ), + isImported = false, + ), + ), + attestation = Attestation( + cardKeyAttestation = Attestation.Status.Verified, + walletKeysAttestation = Attestation.Status.Skipped, + firmwareAttestation = Attestation.Status.Skipped, + cardUniquenessAttestation = Attestation.Status.Skipped, + ), + backupStatus = CardDTO.BackupStatus.NoBackup, + ) + + override val scanResponse = ScanResponse( + card = cardDto, + productType = ProductType.Wallet, + walletData = null, + secondTwinPublicKey = null, + derivedKeys = emptyMap(), + primaryCard = null, + ) + + override val derivationTaskResponse = DerivationTaskResponse( + entries = mapOf( + ByteArrayKey( + byteArrayOf(2, -74, 50, 45, -105, 119, 115, 87, -7, -110, 33, 10, -41, -9, -41, 1, 63, -73, 31, -77, 86, 66, -47, -31, 16, 32, 63, 122, 87, 94, 41, 68, 125), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + ) + + override val extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(3, 2, 95, 53, 40, -87, -60, 11, -8, -47, 41, 37, 100, 15, -69, 1, -122, 127, -20, -81, -32, -20, -24, 5, -28, 113, 106, -90, -59, -30, -27, -110, -110), + chainCode = byteArrayOf(-95, -87, -95, -25, 27, 96, -57, -92, -69, -106, -45, 10, 85, 4, -92, -68, 49, -24, -28, -50, -49, -77, -20, 118, -50, -27, 104, -93, 115, -50, -46, -34), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ) + + override val successResponse = SuccessResponse(cardId = "AF02010000000002") + + override val createProductWalletTaskResponse = CreateProductWalletTaskResponse( + card = cardDto, + derivedKeys = mapOf( + ByteArrayKey( + byteArrayOf(2, -74, 50, 45, -105, 119, 115, 87, -7, -110, 33, 10, -41, -9, -41, 1, 63, -73, 31, -77, 86, 66, -47, -31, 16, 32, 63, 122, 87, 94, 41, 68, 125), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + primaryCard = primaryCard, + ) + + override val importWalletResponse: CreateProductWalletTaskResponse + get() = error("Available only for Wallet 2") + + override val createFirstTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val createSecondTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val finalizeTwinResponse: ScanResponse + get() = error("Available only for Twin") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/ShibaNoBackupNoWalletsMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/ShibaNoBackupNoWalletsMockContent.kt new file mode 100644 index 0000000000..a1b1b15b20 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/ShibaNoBackupNoWalletsMockContent.kt @@ -0,0 +1,223 @@ +package com.tangem.tap.domain.sdk.mocks.content + +import com.tangem.common.SuccessResponse +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.EncryptionMode +import com.tangem.common.card.FirmwareVersion +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ProductType +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.operations.attestation.Attestation +import com.tangem.operations.backup.PrimaryCard +import com.tangem.operations.derivation.DerivationTaskResponse +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.sdk.api.CreateProductWalletTaskResponse +import com.tangem.tap.domain.sdk.mocks.MockContent +import java.util.Date + +object ShibaNoBackupNoWalletsMockContent : MockContent { + + private val primaryCard = PrimaryCard( + cardId = "AF02010000000002", + batchId = "AF02", + cardPublicKey = byteArrayOf(2, 48, 4, 54, -34, -33, 22, 53, -92, 8, 72, 50, 69, 1, 105, -53, 9, -126, -23, -61, -62, -85, -24, -112, -32, 76, -88, -66, -25, -83, -70, -95, 90), + linkingKey = byteArrayOf( + 2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121, + -57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91, + ), + existingWalletsCount = 3, isHDWalletAllowed = true, + issuer = Card.Issuer( + name = "TANGEM SDK", + publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34), + ), + manufacturer = Card.Manufacturer( + name = "TANGEM", + manufactureDate = Date(1743759687), + signature = byteArrayOf(44, 8, 116, 111, -57, 20, -93, 101, -104, 97, -104, -15, 58, -8, 41, -53, 95, 99, 107, 47, -29, -118, 41, 64, 22, 94, 33, -81, 114, -47, 10, -16, 97, 14, 94, -36, -82, -108, 74, -9, 35, -38, 66, 67, -116, -55, 65, -30, -58, -33, 31, 120, -45, 42, -3, -120, -74, -97, 97, -102, -13, -28, 29, -41), + ), + walletCurves = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519, EllipticCurve.Bls12381G2Aug), + firmwareVersion = FirmwareVersion( + major = 6, + minor = 21, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + isKeysImportAllowed = false, + certificate = null, + ) + + override val cardDto = CardDTO( + cardId = "AF02010000000002", + batchId = "AF02", + cardPublicKey = byteArrayOf(2, 48, 4, 54, -34, -33, 22, 53, -92, 8, 72, 50, 69, 1, 105, -53, 9, -126, -23, -61, -62, -85, -24, -112, -32, 76, -88, -66, -25, -83, -70, -95, 90), + firmwareVersion = CardDTO.FirmwareVersion( + major = 6, + minor = 21, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + manufacturer = CardDTO.Manufacturer( + name = "TANGEM SDK", + manufactureDate = Date(1649635200000), + signature = byteArrayOf(-20, 98, -101, 94, -23, 73, 122, -21, 74, 76, 79, -55, -102, -62, -30, 44, -38, 118, 75, 121, -36, 118, -62, 60, -38, -63, 33, -14, -98, -69, 112, 22, 48, -43, 47, 65, -61, -56, 38, -94, -45, 44, 95, -22, 6, -31, -40, -25, 42, 38, 94, -99, 71, 98, -33, 27, -102, 30, 52, -7, 51, -27, 84, 66), + ), + issuer = CardDTO.Issuer( + name = "TANGEM", + publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2), + ), + settings = CardDTO.Settings( + securityDelay = 15000, + maxWalletsCount = 20, + isSettingAccessCodeAllowed = true, + isSettingPasscodeAllowed = false, + isResettingUserCodesAllowed = true, + isLinkedTerminalEnabled = true, + isBackupAllowed = true, + supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None), + isFilesAllowed = true, + isHDWalletAllowed = true, + isKeysImportAllowed = true, + ), + userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true), + linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None, + isAccessCodeSet = true, + isPasscodeSet = false, + supportedCurves = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Secp256r1, + EllipticCurve.Bls12381G2, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Bls12381G2Pop, + EllipticCurve.Bip0340, + ), + wallets = emptyList(), + attestation = Attestation( + cardKeyAttestation = Attestation.Status.Verified, + walletKeysAttestation = Attestation.Status.Skipped, + firmwareAttestation = Attestation.Status.Skipped, + cardUniquenessAttestation = Attestation.Status.Skipped, + ), + backupStatus = CardDTO.BackupStatus.NoBackup, + ) + + override val scanResponse = ScanResponse( + card = cardDto, + productType = ProductType.Wallet, + walletData = null, + secondTwinPublicKey = null, + derivedKeys = emptyMap(), + primaryCard = null, + ) + + override val derivationTaskResponse = DerivationTaskResponse( + entries = mapOf( + ByteArrayKey( + byteArrayOf(2, -74, 50, 45, -105, 119, 115, 87, -7, -110, 33, 10, -41, -9, -41, 1, 63, -73, 31, -77, 86, 66, -47, -31, 16, 32, 63, 122, 87, 94, 41, 68, 125), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + ) + + override val extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(3, 2, 95, 53, 40, -87, -60, 11, -8, -47, 41, 37, 100, 15, -69, 1, -122, 127, -20, -81, -32, -20, -24, 5, -28, 113, 106, -90, -59, -30, -27, -110, -110), + chainCode = byteArrayOf(-95, -87, -95, -25, 27, 96, -57, -92, -69, -106, -45, 10, 85, 4, -92, -68, 49, -24, -28, -50, -49, -77, -20, 118, -50, -27, 104, -93, 115, -50, -46, -34), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ) + + override val successResponse = SuccessResponse(cardId = "AF02010000000002") + + override val createProductWalletTaskResponse = CreateProductWalletTaskResponse( + card = cardDto, + derivedKeys = mapOf( + ByteArrayKey( + byteArrayOf(2, -74, 50, 45, -105, 119, 115, 87, -7, -110, 33, 10, -41, -9, -41, 1, 63, -73, 31, -77, 86, 66, -47, -31, 16, 32, 63, 122, 87, 94, 41, 68, 125), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + primaryCard = primaryCard, + ) + + override val importWalletResponse: CreateProductWalletTaskResponse + get() = error("Available only for Wallet 2") + + override val createFirstTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val createSecondTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val finalizeTwinResponse: ScanResponse + get() = error("Available only for Twin") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2MockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2MockContent.kt index 9b8fcc752f..3738bc3689 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2MockContent.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2MockContent.kt @@ -219,38 +219,38 @@ object Wallet2MockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch - publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), - chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge - publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), - chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), ) @@ -271,24 +271,24 @@ object Wallet2MockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), primaryCard = primaryCard, ) diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2NoBackupMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2NoBackupMockContent.kt new file mode 100644 index 0000000000..fd3fe78bbc --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2NoBackupMockContent.kt @@ -0,0 +1,307 @@ +package com.tangem.tap.domain.sdk.mocks.content + +import com.tangem.common.SuccessResponse +import com.tangem.common.card.* +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ProductType +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.operations.attestation.Attestation +import com.tangem.operations.backup.PrimaryCard +import com.tangem.operations.derivation.DerivationTaskResponse +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.sdk.api.CreateProductWalletTaskResponse +import com.tangem.tap.domain.sdk.mocks.MockContent +import java.util.Date + +object Wallet2NoBackupMockContent : MockContent { + + private val primaryCard = PrimaryCard( + cardId = "AF05888888880018", + batchId = "AF05", + cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), + linkingKey = byteArrayOf( // + 2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121, + -57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91, + ), + existingWalletsCount = 5, isHDWalletAllowed = true, + issuer = Card.Issuer( + name = "TANGEM SDK", + publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34), + ), + manufacturer = Card.Manufacturer( + name = "TANGEM", + manufactureDate = Date(1743759687), + signature = byteArrayOf(), + ), + walletCurves = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Secp256r1, + EllipticCurve.Ed25519Slip0010, + EllipticCurve.Bls12381G2, + EllipticCurve.Bls12381G2Pop, + EllipticCurve.Bip0340, + ), + firmwareVersion = FirmwareVersion( + major = 6, + minor = 33, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + isKeysImportAllowed = false, + certificate = null, + ) + + override val cardDto = CardDTO( + cardId = "AF05888888880018", + batchId = "AF05", + cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), + firmwareVersion = CardDTO.FirmwareVersion( + major = 6, + minor = 33, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + manufacturer = CardDTO.Manufacturer( + name = "TANGEM", + manufactureDate = Date(1698094800000), + signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3), + ), + issuer = CardDTO.Issuer( + name = "TANGEM SDK", + publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34), + ), + settings = CardDTO.Settings( + securityDelay = 15000, + maxWalletsCount = 20, + isSettingAccessCodeAllowed = true, + isSettingPasscodeAllowed = true, + isResettingUserCodesAllowed = false, + isLinkedTerminalEnabled = true, + isBackupAllowed = true, + supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None), + isFilesAllowed = true, + isHDWalletAllowed = true, + isKeysImportAllowed = true, + ), + userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true), + linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None, + isAccessCodeSet = true, + isPasscodeSet = false, + supportedCurves = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Secp256r1, + EllipticCurve.Ed25519Slip0010, + EllipticCurve.Bls12381G2, + EllipticCurve.Bls12381G2Pop, + EllipticCurve.Bip0340, + ), + wallets = listOf( + CardDTO.Wallet( + publicKey = byteArrayOf(3, 17, 59, -102, -56, 66, 10, 36, 97, -106, -92, -117, -105, -88, -2, -3, -95, -75, -54, -2, 57, 27, -90, -19, 82, 103, -12, -52, -126, -105, -120, -55, -2), + chainCode = byteArrayOf(-34, -28, -28, -12, 80, -109, -90, -31, -74, 36, -78, -21, 39, -125, -39, -91, 63, 40, -26, 3, 66, 27, -62, -55, -97, 52, -65, -11, 26, -80, 33, -45), + curve = EllipticCurve.Secp256k1, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 0, + remainingSignatures = null, + index = 0, + hasBackup = false, + derivedKeys = mapOf( + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, 55, -114, -94, -73, -61, -50, 51, -115, 55, -79, 63, -96, -44, -64, -24, -36, -122, -123, -38, 81, -15, -127, -97, -42, 72, -85, -62, -98, 46, 119, 16, -55), + chainCode = byteArrayOf(31, 17, 71, -28, -29, 17, 72, -29, -98, 112, 31, -8, 72, -75, 4, -11, 60, -100, 9, 35, 58, -42, -38, 96, -71, -68, 24, -119, -43, -18, -122, 72), + ), + DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -26, 103, 96, 112, 127, -125, 0, 7, 53, 30, -12, -82, 45, 14, 107, -9, 126, 75, 104, -67, -49, 35, -12, -82, -90, 101, -101, 125, -76, 88, -54, 99), + chainCode = byteArrayOf(-42, 36, 97, 65, -64, -113, 76, -91, -9, 11, 89, 123, -9, -3, 21, 103, -113, -60, 48, -31, -34, 108, 111, -38, -110, -80, 109, 17, -29, 2, 45, -71), + ), + DerivationPath("m/44'/1'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -96, -3, -83, 101, 63, -25, -125, -4, -65, -42, -56, 24, -52, 118, -11, -104, -105, 40, -59, 20, -109, -97, 29, -95, -6, -80, 2, 67, 103, -80, -22, -94), + chainCode = byteArrayOf(-125, 27, -91, 38, -66, 109, -92, 16, -37, 93, 107, -29, -128, -1, 115, -64, 108, -63, 17, 27, 58, 78, -2, 39, 88, -39, 44, 89, 32, -38, -16, -38), + ), + ), + extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123), + chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127), + ), + isImported = false, + ), + CardDTO.Wallet( + publicKey = byteArrayOf(-109, 42, -80, -115, -12, 44, 48, -118, -89, 10, 21, 59, 98, -110, -86, -14, 123, 105, -30, -49, -40, -4, -7, 32, 60, 67, -88, -52, -96, -10, -14, 123), + chainCode = byteArrayOf(-101, -10, -92, 72, 19, 121, -38, 105, -55, -82, -68, 13, -6, 17, 66, -10, -75, 58, 119, -127, 74, 68, 82, 25, 45, -110, 3, 3, 108, -97, -51, -127), + curve = EllipticCurve.Ed25519, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 0, + remainingSignatures = null, + index = 1, + hasBackup = false, + derivedKeys = emptyMap(), + extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(-64, 18, -128, 121, -89, -37, -99, 44, -125, -72, -111, -79, 7, 85, 40, 67, -39, 117, 123, 11, 105, -6, -5, -79, 19, -10, -29, 20, -14, -40, 5, 90), + chainCode = byteArrayOf(33, -97, 53, -112, 61, 112, -24, 74, -87, -85, -124, -4, 103, -94, -97, 76, -41, -27, 118, 33, 55, 121, -17, -52, 60, 122, 27, 25, 29, -76, 78, 11), + ), + isImported = false, + ), + CardDTO.Wallet( + publicKey = byteArrayOf(-118, -77, -109, 8, -119, 101, -91, 46, -44, 15, 52, -64, -92, 5, -102, 126, 52, 121, 63, -76, -54, 80, -82, -5, 31, -35, 105, -119, -96, -54, 38, 2, -6, 109, 117, -26, -47, -20, 108, 106, -69, 72, -37, -117, -30, -9, 104, -123), + chainCode = null, + curve = EllipticCurve.Bls12381G2Aug, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 1, + remainingSignatures = null, + index = 2, + hasBackup = false, + derivedKeys = emptyMap(), + extendedPublicKey = null, + isImported = false, + ), + CardDTO.Wallet( + publicKey = byteArrayOf(17, -78, 53, 101, 96, -110, -10, -48, -84, 88, -64, 58, -26, -13, -2, -6, -25, 23, -79, -45, 58, 77, 52, -75, -123, 121, 32, 92, 84, -74, -111, -8), + chainCode = byteArrayOf(-110, 73, -124, -65, -1, -70, 64, -89, 95, -74, 15, 46, -110, 87, 15, -61, 120, -51, 111, 111, -45, 37, 123, -114, 65, -116, 21, 39, -77, 86, -3, -26), + curve = EllipticCurve.Bip0340, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 0, + remainingSignatures = null, + index = 3, + hasBackup = false, + derivedKeys = emptyMap(), + extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(115, 14, 11, 0, -93, 81, -103, -95, -75, -84, 18, -120, -31, 76, -83, -81, 91, 25, -75, 36, -99, -53, -25, -15, -1, -57, 14, -39, 98, -116, -63, -123), + chainCode = byteArrayOf(23, 5, 38, -48, 67, -42, -31, -21, 89, 11, 22, -28, 44, -19, -115, -78, 123, -27, 57, 57, -24, -86, 55, 15, 104, 114, -36, 80, 81, -108, -41, 112), + ), + isImported = false, + ), + CardDTO.Wallet( + publicKey = byteArrayOf(-52, -92, 64, 108, -8, -100, 87, -86, -60, 18, -18, -81, 114, -84, 76, -24, 84, 52, -34, 79, -30, -66, -112, -55, -35, -119, 127, -109, 35, 18, -29, -25), + chainCode = byteArrayOf(94, -33, -94, -63, 40, -20, -26, 71, 111, 9, 87, -36, -42, -33, 40, -53, 85, 31, -36, -29, 65, -121, 2, -23, -119, -51, 114, -17, -39, -74, 23, -97), + curve = EllipticCurve.Ed25519Slip0010, + settings = CardWallet.Settings(isPermanent = false), + totalSignedHashes = 0, + remainingSignatures = null, + index = 4, + hasBackup = false, + derivedKeys = emptyMap(), + extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(-43, 1, -81, -47, -8, -103, -66, 42, 37, -7, 65, 54, 57, -24, 127, -89, 69, -112, 42, -46, -128, 36, -117, -28, 30, -48, 37, 52, 93, -47, 92, -47), + chainCode = byteArrayOf(4, -97, 81, -37, 76, -67, -87, -4, -82, -36, -45, -28, -117, -59, -62, 93, -73, 50, 65, -91, -83, 25, -95, 89, -64, -40, 113, 28, 59, 113, -99, 89), + ), + isImported = false, + ), + ), + attestation = Attestation( + cardKeyAttestation = Attestation.Status.Verified, + walletKeysAttestation = Attestation.Status.Skipped, + firmwareAttestation = Attestation.Status.Skipped, + cardUniquenessAttestation = Attestation.Status.Skipped, + ), + backupStatus = CardDTO.BackupStatus.NoBackup, + ) + + override val scanResponse = ScanResponse( + card = cardDto, + productType = ProductType.Wallet2, + walletData = null, + secondTwinPublicKey = null, + derivedKeys = emptyMap(), + primaryCard = null, + ) + + override val derivationTaskResponse = DerivationTaskResponse( + entries = mapOf( + ByteArrayKey( + byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + ) + + override val extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8), + chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ) + + override val successResponse = SuccessResponse(cardId = "AF05888888880018") + + override val createProductWalletTaskResponse = CreateProductWalletTaskResponse( + card = cardDto, + derivedKeys = mapOf( + ByteArrayKey( + byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + primaryCard = primaryCard, + ) + + override val importWalletResponse: CreateProductWalletTaskResponse + get() = TODO("Not yet implemented") + + override val createFirstTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val createSecondTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val finalizeTwinResponse: ScanResponse + get() = error("Available only for Twin") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2NoBackupNoWalletsMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2NoBackupNoWalletsMockContent.kt new file mode 100644 index 0000000000..682794f29b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2NoBackupNoWalletsMockContent.kt @@ -0,0 +1,219 @@ +package com.tangem.tap.domain.sdk.mocks.content + +import com.tangem.common.SuccessResponse +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.EncryptionMode +import com.tangem.common.card.FirmwareVersion +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey +import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.models.scan.ProductType +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.operations.attestation.Attestation +import com.tangem.operations.backup.PrimaryCard +import com.tangem.operations.derivation.DerivationTaskResponse +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.operations.wallet.CreateWalletResponse +import com.tangem.sdk.api.CreateProductWalletTaskResponse +import com.tangem.tap.domain.sdk.mocks.MockContent +import java.util.Date + +object Wallet2NoBackupNoWalletsMockContent : MockContent { + + private val primaryCard = PrimaryCard( + cardId = "AF05888888880018", + batchId = "AF05", + cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), + linkingKey = byteArrayOf( // + 2, 121, 98, 127, -70, 14, 5, -23, -76, 115, -30, -26, 111, 17, 110, 34, -100, -121, + -57, -123, 74, 3, -91, 56, -20, 56, 50, -40, -101, 96, 82, 70, -91, + ), + existingWalletsCount = 5, isHDWalletAllowed = true, + issuer = Card.Issuer( + name = "TANGEM SDK", + publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34), + ), + manufacturer = Card.Manufacturer( + name = "TANGEM", + manufactureDate = Date(1743759687), + signature = byteArrayOf(), + ), + walletCurves = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Secp256r1, + EllipticCurve.Ed25519Slip0010, + EllipticCurve.Bls12381G2, + EllipticCurve.Bls12381G2Pop, + EllipticCurve.Bip0340, + ), + firmwareVersion = FirmwareVersion( + major = 6, + minor = 33, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + isKeysImportAllowed = false, + certificate = null, + ) + + override val cardDto = CardDTO( + cardId = "AF05888888880018", + batchId = "AF05", + cardPublicKey = byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), + firmwareVersion = CardDTO.FirmwareVersion( + major = 6, + minor = 33, + patch = 0, + type = FirmwareVersion.FirmwareType.Release, + ), + manufacturer = CardDTO.Manufacturer( + name = "TANGEM", + manufactureDate = Date(1698094800000), + signature = byteArrayOf(51, -12, 14, -56, 7, -39, 5, 63, 59, 24, 102, 99, -126, 124, -127, -108, -118, 71, -19, -71, 4, -47, -121, -46, 49, -51, -31, 100, -56, -15, 96, -37, 25, 82, 94, -88, 48, 98, -105, -97, -40, 41, 27, 116, 65, 26, 78, -85, 66, -94, -92, 15, 50, -2, 7, -69, 41, 56, -75, 59, 86, 68, -38, -3), + ), + issuer = CardDTO.Issuer( + name = "TANGEM SDK", + publicKey = byteArrayOf(2, 95, 22, -67, 29, 46, -81, -28, 99, -26, 42, 51, 90, 9, -26, -78, -69, -53, -48, 68, 82, 82, 104, -123, -53, 103, -97, -60, -46, 122, -15, -67, 34), + ), + settings = CardDTO.Settings( + securityDelay = 15000, + maxWalletsCount = 20, + isSettingAccessCodeAllowed = true, + isSettingPasscodeAllowed = true, + isResettingUserCodesAllowed = false, + isLinkedTerminalEnabled = true, + isBackupAllowed = true, + supportedEncryptionModes = listOf(EncryptionMode.Strong, EncryptionMode.Fast, EncryptionMode.None), + isFilesAllowed = true, + isHDWalletAllowed = true, + isKeysImportAllowed = true, + ), + userSettings = CardDTO.UserSettings(isUserCodeRecoveryAllowed = true), + linkedTerminalStatus = CardDTO.LinkedTerminalStatus.None, + isAccessCodeSet = true, + isPasscodeSet = false, + supportedCurves = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Secp256r1, + EllipticCurve.Ed25519Slip0010, + EllipticCurve.Bls12381G2, + EllipticCurve.Bls12381G2Pop, + EllipticCurve.Bip0340, + ), + wallets = emptyList(), + attestation = Attestation( + cardKeyAttestation = Attestation.Status.Verified, + walletKeysAttestation = Attestation.Status.Skipped, + firmwareAttestation = Attestation.Status.Skipped, + cardUniquenessAttestation = Attestation.Status.Skipped, + ), + backupStatus = CardDTO.BackupStatus.NoBackup, + ) + + override val scanResponse = ScanResponse( + card = cardDto, + productType = ProductType.Wallet2, + walletData = null, + secondTwinPublicKey = null, + derivedKeys = emptyMap(), + primaryCard = null, + ) + + override val derivationTaskResponse = DerivationTaskResponse( + entries = mapOf( + ByteArrayKey( + byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + ) + + override val extendedPublicKey = ExtendedPublicKey( + publicKey = byteArrayOf(2, -93, -36, -105, 121, -52, -30, -43, -67, -7, -31, -26, -35, 25, 99, 25, -20, 118, -20, -125, -89, 12, -101, -86, 74, -91, 23, -24, 93, -86, 20, -53, -8), + chainCode = byteArrayOf(32, 60, 63, -96, 97, 58, 121, 108, 75, 59, 63, -113, 60, -49, 47, 33, 15, -65, -69, 45, -7, -26, 65, -5, -91, -55, -42, -102, -127, -104, -111, 96), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ) + + override val successResponse = SuccessResponse(cardId = "AF05888888880018") + + override val createProductWalletTaskResponse = CreateProductWalletTaskResponse( + card = cardDto, + derivedKeys = mapOf( + ByteArrayKey( + byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), + ) + to + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + ), + ), + ), + primaryCard = primaryCard, + ) + + override val importWalletResponse: CreateProductWalletTaskResponse + get() = TODO("Not yet implemented") + + override val createFirstTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val createSecondTwinResponse: CreateWalletResponse + get() = error("Available only for Twin") + + override val finalizeTwinResponse: ScanResponse + get() = error("Available only for Twin") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2WithSeedPhraseMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2WithSeedPhraseMockContent.kt index 06d7e7db26..d0625bd95a 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2WithSeedPhraseMockContent.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/Wallet2WithSeedPhraseMockContent.kt @@ -219,38 +219,38 @@ object Wallet2WithSeedPhraseMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch - publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), - chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge - publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), - chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), ) @@ -271,24 +271,24 @@ object Wallet2WithSeedPhraseMockContent : MockContent { byteArrayOf(3, 23, -112, -57, 109, 60, -82, -36, 45, -14, -34, -12, 10, -89, 14, 37, 38, 36, -102, 37, 93, 90, 69, -113, -117, 120, -29, 12, -125, 43, -40, -31, 5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), primaryCard = primaryCard, ) diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/WalletMockContent.kt b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/WalletMockContent.kt index 1db7cd1bb9..1ce637b289 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/WalletMockContent.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/mocks/content/WalletMockContent.kt @@ -168,59 +168,59 @@ object WalletMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch - publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), - chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey( // xrp - publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), - chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge - publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), - chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( - publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), - chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/84'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/145'/0'/0/0") to ExtendedPublicKey( // bch + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/144'/0'/0/0") to ExtendedPublicKey( // xrp + publicKey = byteArrayOf(2, 38, -6, 92, -37, -91, -59, -108, -18, -119, -55, 41, 38, -33, 44, 59, 24, -79, -14, -38, -10, -123, 106, 56, 39, 8, 112, 29, -41, 99, 70, -104, -121), + chainCode = byteArrayOf(105, -21, -61, -50, 68, -89, 119, 53, -96, -40, 119, 77, -122, 121, 16, 40, -50, -48, -105, -101, -74, -7, -94, -59, -90, 96, 59, 99, 43, -91, 115, -29), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/3'/0'/0/0") to ExtendedPublicKey( // doge + publicKey = byteArrayOf(3, -25, -24, -97, -124, 24, -89, 44, 75, 123, 92, -86, -73, -93, 25, -90, -89, -95, 88, 3, 107, 37, -1, -85, -32, -57, -123, -41, 108, -9, -96, 77, -124), + chainCode = byteArrayOf(119, 3, 41, 112, 71, 54, 72, 30, 39, 25, 25, -104, 92, 46, -109, 63, 93, 67, 43, -102, -87, 39, -95, 106, 45, 67, 109, -29, -35, 10, -107, 104), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/195'/0'/0/0") to ExtendedPublicKey( + publicKey = byteArrayOf(2, -40, 115, -15, 80, 104, 100, -29, 80, 29, 112, -35, -54, 64, -98, 45, 115, 108, 93, 113, -71, 89, 17, 72, 98, 21, 126, 82, -50, 50, -12, -87, -62), + chainCode = byteArrayOf(-27, 56, 57, 54, 27, -40, 65, 109, 119, -54, -94, 88, -29, -115, -45, -112, -31, 57, 53, 56, 118, 87, 34, -16, -64, -45, 105, 77, 91, -106, -92, 41), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), ) @@ -241,24 +241,24 @@ object WalletMockContent : MockContent { byteArrayOf(2, -109, 28, -27, -124, -58, -97, -61, 43, -84, 90, -9, -5, 4, 90, 17, 112, -125, -108, 44, 19, -79, -60, -23, 34, -20, -20, 61, 84, 113, 120, -90, -5), ) to - ExtendedPublicKeysMap( - mapOf( - DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc - publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), - chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), - DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth - publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), - chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), - depth = 0, - parentFingerprint = byteArrayOf(0, 0, 0, 0), - childNumber = 0, - ), + ExtendedPublicKeysMap( + mapOf( + DerivationPath("m/44'/0'/0'/0/0") to ExtendedPublicKey( // btc + publicKey = byteArrayOf(3, 45, 58, -110, -52, -51, -83, -4, -45, -118, 119, 37, 123, -17, 66, -83, 61, -106, 115, 47, 121, 66, 84, -122, -57, -45, 7, -79, 70, -13, 28, -125, -52), + chainCode = byteArrayOf(93, 51, 52, -66, -39, -38, 34, -84, 50, 1, -127, -20, 80, -20, -30, -72, 2, 1, -78, -81, -17, 51, -52, -25, 12, 108, 50, 89, -66, 18, 65, 70), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, + ), + DerivationPath("m/44'/60'/0'/0/0") to ExtendedPublicKey( // eth + publicKey = byteArrayOf(2, 34, 6, 119, -106, 5, -119, 111, -22, 8, 23, -108, -72, -56, 6, 77, -17, -61, -101, -85, 16, 28, 18, 3, -3, -89, -81, -108, 48, -7, -86, -82, -67), + chainCode = byteArrayOf(-75, 55, 107, -106, -37, -81, -15, 72, -102, 94, 55, -39, 9, -112, 1, 90, -50, 103, 53, 120, -92, -36, -85, -39, -65, 1, 88, 46, 92, 104, -13, -109), + depth = 0, + parentFingerprint = byteArrayOf(0, 0, 0, 0), + childNumber = 0, ), ), + ), ), primaryCard = primaryCard, ) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt index 8d637b7c78..7e559abf8e 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashesTask.kt @@ -42,7 +42,12 @@ class SignHashesTask( is CompletionResult.Failure -> { when { response.error is TangemSdkError.WalletNotFound && pairWalletPublicKey != null -> { - sign(session, pairWalletPublicKey, publicKey.derivationPath, callback) + sign( + session = session, + publicKey = pairWalletPublicKey, + derivationPath = publicKey.derivationPath, + callback = callback, + ) } else -> callback(CompletionResult.Failure(response.error)) } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index fba95394c0..ac172d1b9c 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -64,22 +64,28 @@ class CreateProductWalletTask( cardTypesResolver.isTangemTwins() -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") - else -> CreateWalletTangemWallet(mnemonic, passphrase, shouldReset, derivationStyleProvider, cardDto) + else -> CreateWalletTangemWallet( + mnemonic = mnemonic, + passphrase = passphrase, + shouldReset = shouldReset, + derivationStyleProvider = derivationStyleProvider, + cardDTO = cardDto, + ) } - commandProcessor.proceed(cardDto, session) { - when (it) { + commandProcessor.proceed(cardDto, session) { result -> + when (result) { is CompletionResult.Success -> { - val result = when (commandProcessor) { + val createProductWalletTaskResponse = when (commandProcessor) { is CreateWalletTangemWallet -> { - it.data as CreateProductWalletTaskResponse + result.data as CreateProductWalletTaskResponse } - else -> CreateProductWalletTaskResponse(card = session.environment.card!!) + else -> CreateProductWalletTaskResponse(card = requireNotNull(session.environment.card)) } - callback(CompletionResult.Success(result)) + callback(CompletionResult.Success(createProductWalletTaskResponse)) } - is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error)) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } @@ -156,7 +162,12 @@ private class CreateWalletTangemWallet( CreateWalletsTask(cardConfig.mandatoryCurves, mnemonic, passphrase).run(session) { result -> when (result) { is CompletionResult.Success -> { - checkIfAllWalletsCreated(card, session, result.data, callback) + checkIfAllWalletsCreated( + card = card, + session = session, + createResponse = result.data, + callback = callback, + ) } is CompletionResult.Failure -> { callback(CompletionResult.Failure(result.error)) @@ -210,12 +221,16 @@ private class CreateWalletTangemWallet( callback: (result: CompletionResult) -> Unit, ) { val resetCommand = ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository = false) - resetCommand.run(session) { - when (it) { + resetCommand.run(session) { result -> + when (result) { is CompletionResult.Success -> { - createMultiWallet(card, session, callback) + createMultiWallet( + card = card, + session = session, + callback = callback, + ) } - is CompletionResult.Failure -> callback(CompletionResult.Failure(it.error)) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } @@ -228,17 +243,27 @@ private class CreateWalletTangemWallet( ) { when { card.settings.isBackupAllowed -> { - linkPrimaryCard(card, createWalletResponses, session, callback) + linkPrimaryCard( + card = card, + createWalletResponses = createWalletResponses, + session = session, + callback = callback, + ) } card.settings.isHDWalletAllowed -> { - deriveKeys(card, createWalletResponses, session, callback) + deriveKeys( + card = card, + createWalletResponses = createWalletResponses, + session = session, + callback = callback, + ) } else -> { callback( CompletionResult.Success( - CreateProductWalletTaskResponse(card = session.environment.card!!), + CreateProductWalletTaskResponse(card = requireNotNull(session.environment.card)), ), ) } @@ -247,7 +272,7 @@ private class CreateWalletTangemWallet( private fun linkPrimaryCard( card: CardDTO, - createWalletResponse: List, + createWalletResponses: List, session: CardSession, callback: (result: CompletionResult) -> Unit, ) { @@ -257,14 +282,19 @@ private class CreateWalletTangemWallet( primaryCard = result.data when { card.settings.isHDWalletAllowed -> { - deriveKeys(card, createWalletResponse, session, callback) + deriveKeys( + card = card, + createWalletResponses = createWalletResponses, + session = session, + callback = callback, + ) } else -> { callback( CompletionResult.Success( CreateProductWalletTaskResponse( - card = session.environment.card!!, + card = requireNotNull(session.environment.card), primaryCard = primaryCard, ), ), @@ -282,13 +312,13 @@ private class CreateWalletTangemWallet( private fun deriveKeys( card: CardDTO, - createWalletResponse: List, + createWalletResponses: List, session: CardSession, callback: (result: CompletionResult) -> Unit, ) { val map = mutableMapOf>() var isBlockchainsForCurvesExist = false - createWalletResponse.forEach { response -> + createWalletResponses.forEach { response -> val blockchainsForCurve = getBlockchains(response.cardId, card).filter { it.getSupportedCurves().contains(response.wallet.curve) } diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt index c0ddcdeea3..628dbf9005 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt @@ -42,7 +42,7 @@ class CreateWalletsTask( callback: (result: CompletionResult) -> Unit, ) { val extendedPrivateKey = mnemonic?.let { - AnyMasterKeyFactory(mnemonic = it, passphrase = passphrase ?: "").makeMasterKey(curve) + AnyMasterKeyFactory(mnemonic = it, passphrase = passphrase.orEmpty()).makeMasterKey(curve) } CreateWalletTask(curve, extendedPrivateKey).run(session) { result -> when (result) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt index fa255fdacb..54df91df2f 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt @@ -34,12 +34,10 @@ internal class DerivationsFinder( val userWalletId = UserWalletIdBuilder.card(card).build() ?: return emptySet() val derivationStyle = derivationStyleProvider.getDerivationStyle() - var blockchains = withContext(dispatchers.io) { + val blockchains = withContext(dispatchers.io) { getBlockchains(userWalletId) - } - - if (blockchains.isEmpty()) { - blockchains = if (DemoHelper.isDemoCardId(card.cardId)) { + }.ifEmpty { + if (DemoHelper.isDemoCardId(card.cardId)) { getDemoBlockchains(derivationStyle) } else { getDefaultBlockchains(derivationStyle) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt index 51ff0b2d80..c00639f1bd 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ResetToFactorySettingsTask.kt @@ -19,10 +19,15 @@ class ResetToFactorySettingsTask( } private fun deleteWallets(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val wallet = session.environment.card?.wallets?.lastOrNull().guard { - resetBackup(session, callback) - return - } + val wallet = session + .environment + .card + ?.wallets + ?.lastOrNull() + .guard { + resetBackup(session, callback) + return + } PurgeWalletCommand(wallet.publicKey).run(session) { result -> when (result) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index df05ce2f7e..27bc6e7e13 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -111,13 +111,13 @@ internal class ScanProductTask( } private fun getErrorIfExcludedCard(cardDto: CardDTO, card: Card): TangemError? { - if (cardDto.isExcluded) return TapSdkError.CardForDifferentApp - if (cardDto.isNotSupportedInThatRelease) return TapSdkError.CardNotSupportedByRelease + if (cardDto.isExcluded) return TapSdkError.CardForDifferentApp() + if (cardDto.isNotSupportedInThatRelease) return TapSdkError.CardNotSupportedByRelease() // according ios decline card lower Ed25519Slip0010Available version and contains imported wallets if (card.firmwareVersion < FirmwareVersion.Ed25519Slip0010Available && card.wallets.any { it.isImported } ) { - return TapSdkError.CardNotSupportedByRelease + return TapSdkError.CardNotSupportedByRelease() } return null } @@ -253,12 +253,13 @@ private class ScanWalletProcessor( callback: (result: CompletionResult) -> Unit, ) { mainScope.launch { - val activationInProgress = store.inject(DaggerGraphState::cardRepository) + val isActivationInProgress = store.inject(DaggerGraphState::cardRepository) .isActivationInProgress(card.cardId) @Suppress("ComplexCondition") - if (card.backupStatus == CardDTO.BackupStatus.NoBackup && card.wallets.isNotEmpty() && - activationInProgress + if (card.backupStatus == CardDTO.BackupStatus.NoBackup && + card.wallets.isNotEmpty() && + isActivationInProgress ) { StartPrimaryCardLinkingTask().run(session) { linkingResult -> when (linkingResult) { @@ -372,8 +373,8 @@ private class ScanTwinProcessor : ProductCommandProcessor { return@run } - val verified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) - val response = if (verified) { + val isVerified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) + val response = if (isVerified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) val walletData = session.environment.walletData ScanResponse( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt index 9dd041b2ea..547876b22f 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt @@ -153,9 +153,9 @@ class VisaCardActivationTask @AssistedInject constructor( val otpTaskDeferred = async { createOTP() } val dataToSign = dataToSignDeferred.await() - .getOrElse { + .getOrElse { error -> otpTaskDeferred.cancel() - return@coroutineScope CompletionResult.Failure(it) + return@coroutineScope CompletionResult.Failure(error) } otpTaskDeferred.await() diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt index 4fa773c18c..64fc98c01d 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt @@ -115,8 +115,8 @@ class VisaCustomerWalletApproveTask( extendedPublicKey = extendedPublicKey, ) - validationResult.onLeft { - callback(CompletionResult.Failure(it.tangemError)) + validationResult.onLeft { error -> + callback(CompletionResult.Failure(error.tangemError)) return } @@ -137,8 +137,8 @@ class VisaCustomerWalletApproveTask( val publicKey = findKeyWithoutDerivation( targetAddress = visaDataForApprove.targetAddress, card = CardDTO(card), - ).getOrElse { - callback(CompletionResult.Failure(it.tangemError)) + ).getOrElse { error -> + callback(CompletionResult.Failure(error.tangemError)) return } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index d55d7d7d32..b466017130 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -37,7 +37,7 @@ class CreateSecondTwinWalletTask( } if (!TwinsHelper.isTwinsCompatible(firstCardId, card.cardId)) { - callback(CompletionResult.Failure(IncompatibleTwinCard)) + callback(CompletionResult.Failure(IncompatibleTwinCard())) return } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index 8d2c83906c..2b20696ee9 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -24,7 +24,7 @@ class FinalizeTwinTask( when (readResult) { is CompletionResult.Success -> ScanProductTask( - readResult.data, + card = readResult.data, derivationsFinder = null, visaCardScanHandler = null, visaCoroutineScope = null, diff --git a/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt b/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt index 10afdb020a..a9c4f33eb4 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt @@ -4,7 +4,7 @@ import com.tangem.common.core.TangemError import com.tangem.tap.tangemSdkManager import com.tangem.wallet.R -object IncompatibleTwinCard : TangemError(code = 50005) { +class IncompatibleTwinCard : TangemError(code = 50005) { override var customMessage: String = tangemSdkManager.getString( R.string.twin_error_wrong_twin, ) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index 2a9dcc2d23..b503705f7e 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -41,7 +41,7 @@ class TwinCardsManager(card: CardDTO) { creatingWalletMessage: Message, ): CompletionResult { val response = tangemSdkManager.createSecondTwinWallet( - firstPublicKey = currentCardPublicKey!!, + firstPublicKey = requireNotNull(currentCardPublicKey), firstCardId = firstCardId, issuerKeys = getIssuerKeys(), preparingMessage = preparingMessage, @@ -58,7 +58,7 @@ class TwinCardsManager(card: CardDTO) { suspend fun complete(message: Message): CompletionResult { val response = tangemSdkManager.finalizeTwin( - secondCardPublicKey = secondCardPublicKey!!.hexToBytes(), + secondCardPublicKey = requireNotNull(secondCardPublicKey).hexToBytes(), issuerKeyPair = getIssuerKeys(), cardId = firstCardId, initialMessage = message, diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt index 7eae636918..7dc2b0adff 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt @@ -21,8 +21,9 @@ class WriteProtectedIssuerDataTask( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { SignHashCommand( - twinPublicKey.calculateSha256(), - session.environment.card!!.wallets.first().publicKey, + hash = twinPublicKey.calculateSha256(), + walletPublicKey = requireNotNull(session.environment.card) + .wallets.first().publicKey, ) .run(session) { signResult -> when (signResult) { @@ -31,12 +32,12 @@ class WriteProtectedIssuerDataTask( when (readResult) { is CompletionResult.Success -> { writeIssuerData( - twinPublicKey, - issuerKeys, - signResult.data.signature, - readResult.data, - session, - callback, + twinPublicKey = twinPublicKey, + issuerKeys = issuerKeys, + cardSignature = signResult.data.signature, + readResponse = readResult.data, + session = session, + callback = callback, ) } is CompletionResult.Failure -> callback( @@ -78,7 +79,7 @@ class WriteProtectedIssuerDataTask( ) WriteIssuerDataCommand( issuerData = data, - issuerDataSignature = signedByIssuer.finalizingSignature!!, + issuerDataSignature = requireNotNull(signedByIssuer.finalizingSignature), issuerDataCounter = counter, issuerPublicKey = issuerKeys.publicKey, ).run(session, callback) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index 956ce71605..ae13a2ec94 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -57,12 +57,12 @@ internal class BiometricUserWalletsListManager( override val selectedUserWalletSync: UserWallet? get() = findSelectedUserWallet() - override val isLocked: Flow + override val lockedState: Flow get() = state .mapLatest { it.isLocked } .distinctUntilChanged() - override val isLockedSync: Boolean + override val isLocked: Boolean get() = state.value.isLocked override val hasUserWallets: Boolean @@ -103,7 +103,9 @@ internal class BiometricUserWalletsListManager( override suspend fun select(userWalletId: UserWalletId): CompletionResult = catching { if (state.value.selectedUserWalletId == userWalletId) { - return@catching findSelectedUserWallet()!! + return@catching requireNotNull(findSelectedUserWallet()) { + "Wallet is not found" + } } selectedUserWalletRepository.set(userWalletId) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt index 4ae134f570..f38d94f5c8 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/GeneralUserWalletsListManager.kt @@ -93,23 +93,23 @@ internal class GeneralUserWalletsListManager( override val walletsCount: Int get() = requireImplementation.walletsCount - override val isLocked: Flow + override val lockedState: Flow get() = implementation.transformLatest { impl -> if (impl == null) return@transformLatest if (impl is UserWalletsListManager.Lockable) { - emitAll(impl.isLocked) + emitAll(impl.lockedState) } else { error("RuntimeUserWalletsListManager is not lockable") } } - override val isLockedSync: Boolean + override val isLocked: Boolean get() { val impl = requireImplementation return if (impl is UserWalletsListManager.Lockable) { - impl.isLockedSync + impl.isLocked } else { error("RuntimeUserWalletsListManager is not lockable") } @@ -173,10 +173,10 @@ internal class GeneralUserWalletsListManager( } if (possibleManager == implementation.value) { - Timber.e("Switch to the same manager ${possibleManager::class.simpleName}") + Timber.e("Switch to the same manager ${possibleManager::class.simpleName.orEmpty()}") } - Timber.i("Switch to ${possibleManager::class.simpleName}") + Timber.i("Switch to ${possibleManager::class.simpleName.orEmpty()}") val previousManager = implementation.value implementation.value = copySelectedUserWallet( diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt index 900c4bb7bf..31ad6301f0 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt @@ -74,11 +74,13 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager { ?.takeIf { it.walletId == userWalletId } ?: walletNotFound() - state.updateAndGet { prevState -> - prevState.copy( - userWallet = update(wallet), - ) - }.userWallet!! + requireNotNull( + state.updateAndGet { prevState -> + prevState.copy( + userWallet = update(wallet), + ) + }.userWallet, + ) { "User wallet is null after update" } } override suspend fun delete(userWalletIds: List): CompletionResult = clear() diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt index b4019602d9..bc5d0c26ac 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt @@ -21,6 +21,7 @@ internal data class UserWalletSensitiveInformation( val mobileWallets: List? = null, ) +@Suppress("BooleanPropertyNaming") @JsonClass(generateAdapter = true) internal data class UserWalletPublicInformation( // Common diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt index c4a0b716f7..92f7bb6ac9 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt @@ -125,7 +125,7 @@ internal class DefaultUserWalletsListRepository( // update the userWallets state and add if it doesn't exist updateWallets { currentWallets -> - val wallets = currentWallets ?: emptyList() + val wallets = currentWallets.orEmpty() if (wallets.any { it.walletId == userWallet.walletId }) { wallets.map { if (it.walletId == userWallet.walletId) userWallet else it } } else { @@ -332,12 +332,12 @@ internal class DefaultUserWalletsListRepository( raise(LockWalletsError.NothingToLock) } - updateWallets { - it?.map { - if (it.walletId !in unsecuredWalletIds) { - it.lock() + updateWallets { wallets -> + wallets?.map { wallet -> + if (wallet.walletId !in unsecuredWalletIds) { + wallet.lock() } else { - it + wallet } } } @@ -395,17 +395,17 @@ internal class DefaultUserWalletsListRepository( } private suspend fun hasBiometry(): Boolean { - val useBiometricAuthentication = appPreferencesStore.getSyncOrDefault( + val isBiometricAuthenticationUsed = appPreferencesStore.getSyncOrDefault( key = PreferencesKeys.USE_BIOMETRIC_AUTHENTICATION_KEY, default = false, ) - return tangemSdkManagerProvider.invoke().canUseBiometry && useBiometricAuthentication + return tangemSdkManagerProvider.invoke().canUseBiometry && isBiometricAuthenticationUsed } private fun updateWallets(block: (List?) -> List?) { - userWallets.update { - val updated = block(it) + userWallets.update { wallets -> + val updated = block(wallets) selectedUserWallet.update { currentSelected -> if (currentSelected == null) return@update null updated?.find { it.walletId == currentSelected.walletId } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletEncryptionKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletEncryptionKeysRepository.kt index 104cfc0945..faf5553d90 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletEncryptionKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletEncryptionKeysRepository.kt @@ -137,8 +137,7 @@ internal class UserWalletEncryptionKeysRepository( private suspend fun UserWalletEncryptionKey.encode(): ByteArray { return withContext(dispatchers.default) { - this@encode - .let(encryptionKeyAdapter::toJson) + encryptionKeyAdapter.toJson(this@encode) .encodeToByteArray(throwOnInvalidSequence = true) } } @@ -153,8 +152,7 @@ internal class UserWalletEncryptionKeysRepository( private suspend fun List.encode(): ByteArray { return withContext(dispatchers.default) { - this@encode - .let(userWalletsIdsListAdapter::toJson) + userWalletsIdsListAdapter.toJson(this@encode) .encodeToByteArray(throwOnInvalidSequence = true) } } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt index 1a3d1c3b38..6f82167edd 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt @@ -164,8 +164,7 @@ internal class BiometricUserWalletsKeysRepository( private suspend fun UserWalletEncryptionKey.encode(): ByteArray { return withContext(Dispatchers.Default) { - this@encode - .let(encryptionKeyAdapter::toJson) + encryptionKeyAdapter.toJson(this@encode) .encodeToByteArray(throwOnInvalidSequence = true) } } @@ -180,8 +179,7 @@ internal class BiometricUserWalletsKeysRepository( private suspend fun List.encode(): ByteArray { return withContext(Dispatchers.Default) { - this@encode - .let(userWalletsIdsListAdapter::toJson) + userWalletsIdsListAdapter.toJson(this@encode) .encodeToByteArray(throwOnInvalidSequence = true) } } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt index a66ba5bdbb..c3e8f86072 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt @@ -80,8 +80,7 @@ internal class DefaultUserWalletsPublicInformationRepository( @JvmName("saveWithPublicInformation") private suspend fun save(publicInformation: List): CompletionResult = catching { withContext(Dispatchers.IO) { - publicInformation - .let(publicInformationAdapter::toJson) + publicInformationAdapter.toJson(publicInformation) .encodeToByteArray(throwOnInvalidSequence = true) .also { secureStorage.store(it, StorageKey.UserWalletPublicInformation.name) } } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt index 23d1e3e069..f4a2c32083 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt @@ -110,18 +110,18 @@ internal class DefaultUserWalletsSensitiveInformationRepository( private suspend fun ByteArray.decodeToEncryptedSensitiveInformation(): Map? { return withContext(Dispatchers.Default) { - this@decodeToEncryptedSensitiveInformation - .decodeToString(throwOnInvalidSequence = true) - .let(encryptedSensitiveInformationMapAdapter::fromJson) + encryptedSensitiveInformationMapAdapter.fromJson( + this@decodeToEncryptedSensitiveInformation.decodeToString(throwOnInvalidSequence = true), + ) } } private suspend fun ByteArray.decodeToSensitiveInformation(): UserWalletSensitiveInformation? { return withContext(Dispatchers.Default) { try { - this@decodeToSensitiveInformation - .decodeToString(throwOnInvalidSequence = true) - .let(sensitiveInformationAdapter::fromJson) + sensitiveInformationAdapter.fromJson( + this@decodeToSensitiveInformation.decodeToString(throwOnInvalidSequence = true), + ) } catch (e: CharacterCodingException) { Timber.e(e, "Unable to decode sensitive information") @@ -132,16 +132,14 @@ internal class DefaultUserWalletsSensitiveInformationRepository( private suspend fun UserWalletSensitiveInformation.encode(): ByteArray { return withContext(Dispatchers.Default) { - this@encode - .let(sensitiveInformationAdapter::toJson) + sensitiveInformationAdapter.toJson(this@encode) .encodeToByteArray(throwOnInvalidSequence = true) } } private suspend fun Map.encode(): ByteArray { return withContext(Dispatchers.Default) { - this@encode - .let(encryptedSensitiveInformationMapAdapter::toJson) + encryptedSensitiveInformationMapAdapter.toJson(this@encode) .encodeToByteArray(throwOnInvalidSequence = true) } } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt index ec627a4e34..ea08934377 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/Mapper.kt @@ -54,14 +54,14 @@ internal fun UserWalletPublicInformation.toUserWallet(): UserWallet { walletId = walletId, hotWalletId = hotWalletId, wallets = null, - backedUp = backedUp!!, + backedUp = requireNotNull(backedUp), ) } else { UserWallet.Cold( name = name, walletId = walletId, cardsInWallet = cardsInWallet, - scanResponse = scanResponse!!, + scanResponse = requireNotNull(scanResponse), isMultiCurrency = isMultiCurrency, hasBackupError = hasBackupError, ) @@ -78,7 +78,7 @@ internal fun UserWallet.updateWith(sensitiveInformation: UserWalletSensitiveInfo copy( scanResponse = scanResponse.copy( card = scanResponse.card.copy( - wallets = sensitiveInformation.wallets!!, + wallets = requireNotNull(sensitiveInformation.wallets), ), visaCardActivationStatus = sensitiveInformation.visaCardActivationStatus, ), diff --git a/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt b/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt index d074006981..931d258b38 100644 --- a/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt +++ b/app/src/main/java/com/tangem/tap/domain/visa/VisaCardScanHandler.kt @@ -87,9 +87,9 @@ internal class VisaCardScanHandler @Inject constructor( cardId = card.cardId, // This is the wallet public key, not the address and it's alright, as the API expects it in this format cardWalletAddress = wallet.publicKey.toHexString(), - ).getOrElse { + ).getOrElse { error -> Timber.i("Failed to get Access token for Wallet public key authorization") - return CompletionResult.Failure(it.tangemError) + return CompletionResult.Failure(error.tangemError) } val signChallengeResult = signChallengeWithWallet( @@ -123,16 +123,16 @@ internal class VisaCardScanHandler @Inject constructor( signedChallenge: VisaAuthSignedChallenge, ): CompletionResult { val authorizationTokensResponse = visaAuthRemoteDataSource.getAccessTokens(signedChallenge = signedChallenge) - .getOrElse { + .getOrElse { error -> Timber.i("Failed to get Access token for Wallet public key authorization.") return if ( - it is VisaApiError.ProductInstanceIsNotActivated || - it is VisaApiError.ProductInstanceNotFoundActivationRequired + error is VisaApiError.ProductInstanceIsNotActivated || + error is VisaApiError.ProductInstanceNotFoundActivationRequired ) { Timber.i("Proceeding with card authorization.") handleCardAuthorization(cardWalletAddress = cardWalletAddress) } else { - CompletionResult.Failure(it.tangemError) + CompletionResult.Failure(error.tangemError) } } @@ -152,9 +152,9 @@ internal class VisaCardScanHandler @Inject constructor( val challengeResponse = visaAuthRemoteDataSource.getCardAuthChallenge( cardId = card.cardId, cardPublicKey = card.cardPublicKey.toHexString(), - ).getOrElse { - Timber.e("Failed to get challenge for Card authorization. Plain error: ${it.errorCode}") - return CompletionResult.Failure(it.tangemError) + ).getOrElse { error -> + Timber.e("Failed to get challenge for Card authorization. Plain error: ${error.errorCode}") + return CompletionResult.Failure(error.tangemError) } Timber.i("Received challenge to sign: ${challengeResponse.challenge}") @@ -179,9 +179,9 @@ internal class VisaCardScanHandler @Inject constructor( signedChallenge = attestCardKeyResponse.cardSignature.toHexString(), salt = attestCardKeyResponse.salt.toHexString(), ), - ).getOrElse { - Timber.e("Failed to sign challenge with Card public key. Plain error: ${it.errorCode}") - return CompletionResult.Failure(it.tangemError) + ).getOrElse { error -> + Timber.e("Failed to sign challenge with Card public key. Plain error: ${error.errorCode}") + return CompletionResult.Failure(error.tangemError) } visaAuthTokenStorage.store( @@ -189,9 +189,9 @@ internal class VisaCardScanHandler @Inject constructor( tokens = authorizationTokensResponse, ) - val activationRemoteState = visaActivationRepository.getActivationRemoteState().getOrElse { - Timber.e("Failed to sign challenge with Card public key. Plain error: ${it.errorCode}") - return CompletionResult.Failure(it.tangemError) + val activationRemoteState = visaActivationRepository.getActivationRemoteState().getOrElse { error -> + Timber.e("Failed to sign challenge with Card public key. Plain error: ${error.errorCode}") + return CompletionResult.Failure(error.tangemError) } val error = when (activationRemoteState) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index d398da42c9..0c647de6c4 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -6,6 +6,7 @@ import com.tangem.domain.models.scan.ScanResponse import kotlinx.coroutines.CoroutineScope import org.rekotlin.Action +@Suppress("BooleanPropertyNaming") sealed class DetailsAction : Action { data class PrepareScreen( @@ -32,7 +33,7 @@ sealed class DetailsAction : Action { data object EnrollBiometrics : AppSettings() data class BiometricsStatusChanged( - val needEnrollBiometrics: Boolean, + val isEnrollBiometricsNeeded: Boolean, ) : AppSettings() data class ChangeAppThemeMode( @@ -40,7 +41,7 @@ sealed class DetailsAction : Action { ) : AppSettings() data class ChangeBalanceHiding( - val hideBalance: Boolean, + val shouldHideBalance: Boolean, ) : AppSettings() data class ChangeAppCurrency( diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 2ebe0494ab..d42bb74f37 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -86,7 +86,7 @@ class DetailsMiddleware { changeAppThemeMode(action.appThemeMode) } is DetailsAction.AppSettings.ChangeBalanceHiding -> { - changeBalanceHiding(action.hideBalance) + changeBalanceHiding(action.shouldHideBalance) } is DetailsAction.AppSettings.ChangeAppCurrency -> { store.dispatch(GlobalAction.ChangeAppCurrency(action.currency)) @@ -153,9 +153,9 @@ class DetailsMiddleware { private suspend fun setBiometricLockForAllWallets() { val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository) val userWallets = userWalletsListRepository.userWalletsSync() - userWallets.forEach { + userWallets.forEach { wallet -> userWalletsListRepository.setLock( - userWalletId = it.walletId, + userWalletId = wallet.walletId, lockMethod = LockMethod.Biometric, changeUnsecured = false, ) @@ -174,11 +174,11 @@ class DetailsMiddleware { deleteSavedAccessCodes() val userWalletsListRepository = store.inject(DaggerGraphState::userWalletsListRepository) val tangemHotSdk = store.inject(DaggerGraphState::tangemHotSdk) - userWalletsListRepository.userWalletsSync().forEach { - if (it is UserWallet.Hot) { + userWalletsListRepository.userWalletsSync().forEach { wallet -> + if (wallet is UserWallet.Hot) { userWalletsListRepository.saveWithoutLock( - userWallet = it.copy( - hotWalletId = tangemHotSdk.removeBiometryAuthIfPresented(it.hotWalletId), + userWallet = wallet.copy( + hotWalletId = tangemHotSdk.removeBiometryAuthIfPresented(wallet.hotWalletId), ), ) } @@ -188,10 +188,10 @@ class DetailsMiddleware { private fun observeBiometricsStatusChanges(scope: CoroutineScope) { val needEnrollBiometricsFlow = flow { do { - val needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() + val isEnrollBiometricsNeeded = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull() - if (needEnrollBiometrics != null) { - emit(needEnrollBiometrics) + if (isEnrollBiometricsNeeded != null) { + emit(isEnrollBiometricsNeeded) } delay(timeMillis = 200) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index e783c18e37..5b219a6c22 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -84,7 +84,7 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail ) is DetailsAction.AppSettings.BiometricsStatusChanged -> state.copy( appSettingsState = state.appSettingsState.copy( - needEnrollBiometrics = action.needEnrollBiometrics, + needEnrollBiometrics = action.isEnrollBiometricsNeeded, ), ) is DetailsAction.AppSettings.ChangeAppThemeMode -> state.copy( @@ -99,7 +99,7 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail ) is DetailsAction.AppSettings.ChangeBalanceHiding -> state.copy( appSettingsState = state.appSettingsState.copy( - isHidingEnabled = action.hideBalance, + isHidingEnabled = action.shouldHideBalance, ), ) // state should be copied to avoid concurrent modifications from different sources diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 980cacb286..f7e7eaa6dc 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -11,6 +11,7 @@ data class DetailsState( val appSettingsState: AppSettingsState = AppSettingsState(), ) : StateType +@Suppress("BooleanPropertyNaming") data class AppSettingsState( @Deprecated("Delete after hot wallet release") val saveWallets: Boolean = false, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorScreen.kt index c4cfcfd7ff..2ccdbdd1f0 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appcurrency/AppCurrencySelectorScreen.kt @@ -341,16 +341,18 @@ private class AppCurrencySelectorStateProvider : CollectionPreviewParameterProvi .mapIndexed { index, s -> Currency(index.toString(), s) } .toPersistentList() - AppCurrencySelectorState.Loading(onBackClick = {}).let(::add) - AppCurrencySelectorState.Default( + this + AppCurrencySelectorState.Loading(onBackClick = {}) + + this + AppCurrencySelectorState.Default( selectedId = "0", items = items, scrollToSelected = consumedEvent(), onCurrencyClick = {}, onBackClick = {}, onTopBarActionClick = {}, - ).let(::add) - AppCurrencySelectorState.Search( + ) + + this + AppCurrencySelectorState.Search( selectedId = "0", items = items, scrollToSelected = consumedEvent(), @@ -358,7 +360,7 @@ private class AppCurrencySelectorStateProvider : CollectionPreviewParameterProvi onBackClick = {}, onSearchInputChange = {}, onTopBarActionClick = {}, - ).let(::add) + ) }, ) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt index f3d0b721c9..b721c113d5 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item import com.tangem.tap.features.details.ui.appsettings.components.* @@ -27,16 +27,16 @@ import kotlinx.collections.immutable.persistentListOf @Composable internal fun AppSettingsScreen(state: AppSettingsScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) { SettingsScreensScaffold( + onBackClick = onBackClick, modifier = modifier, + titleRes = R.string.app_settings_title, + addBottomInsets = false, content = { when (state) { is AppSettingsScreenState.Content -> AppSettings(state = state) is AppSettingsScreenState.Loading -> Unit } }, - titleRes = R.string.app_settings_title, - onBackClick = onBackClick, - addBottomInsets = false, ) } @@ -103,10 +103,10 @@ private class AppSettingsScreenStateProvider : CollectionPreviewParameterProvide itemsFactory.createSelectThemeModeButton(AppThemeMode.DEFAULT, {}), ) - AppSettingsScreenState.Content( + this + AppSettingsScreenState.Content( items = items, dialog = null, - ).let(::add) + ) }, ) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt index 29290619e6..327cb900a1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsAlertDialog.kt @@ -47,8 +47,8 @@ private class AlertDialogProvider : CollectionPreviewParameterProvider( collection = buildList { val itemsFactory = AppSettingsItemsFactory() - itemsFactory.createEnrollBiometricsCard( - onClick = { /* no-op */ }, - ).let(::add) + this + itemsFactory.createEnrollBiometricsCard(onClick = { /* no-op */ }) }, ) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSwitchItem.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSwitchItem.kt index 9ad210cbef..a31eb1484d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSwitchItem.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/components/SettingsSwitchItem.kt @@ -17,8 +17,8 @@ import com.tangem.core.ui.components.SpacerH4 import com.tangem.core.ui.components.SpacerW32 import com.tangem.core.ui.components.TangemSwitch import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState.Item @@ -85,26 +85,29 @@ private class SwitchItemProvider : CollectionPreviewParameterProvider { - val items = buildList { - if (state.needEnrollBiometrics) { - itemsFactory.createEnrollBiometricsCard( - onClick = ::enrollBiometrics, - ).let(::add) - } + val items = buildList { + addIf( + condition = state.needEnrollBiometrics, + element = itemsFactory.createEnrollBiometricsCard(onClick = ::enrollBiometrics), + ) - itemsFactory.createSelectAppCurrencyButton( + this + itemsFactory.createSelectAppCurrencyButton( currentAppCurrencyName = state.selectedAppCurrency.name, onClick = ::showAppCurrencySelector, - ).let(::add) + ) if (hotWalletFeatureToggles.isHotWalletEnabled) { val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress - itemsFactory.createUseBiometricsSwitch( + this + itemsFactory.createUseBiometricsSwitch( isChecked = state.useBiometricAuthentication, isEnabled = canUseBiometrics, onCheckedChange = ::onBiometricAuthenticationToggled, - ).let(::add) + ) - itemsFactory.createRequireAccessCodeSwitch( + this + itemsFactory.createRequireAccessCodeSwitch( isChecked = state.requireAccessCode, isEnabled = canUseBiometrics && state.useBiometricAuthentication, onCheckedChange = ::onRequireAccessCodeToggled, - ).let(::add) + ) } else { if (state.isBiometricsAvailable) { val canUseBiometrics = !state.needEnrollBiometrics && !state.isInProgress - itemsFactory.createSaveWalletsSwitch( + this + itemsFactory.createSaveWalletsSwitch( isChecked = state.saveWallets, isEnabled = canUseBiometrics, onCheckedChange = ::onSaveWalletsToggled, - ).let(::add) + ) - itemsFactory.createSaveAccessCodeSwitch( + this + itemsFactory.createSaveAccessCodeSwitch( isChecked = state.saveAccessCodes, isEnabled = canUseBiometrics, onCheckedChange = ::onSaveAccessCodesToggled, - ).let(::add) + ) } } - itemsFactory.createFlipToHideBalanceSwitch( + this + itemsFactory.createFlipToHideBalanceSwitch( isChecked = state.isHidingEnabled, isEnabled = true, onCheckedChange = ::onFlipToHideBalanceToggled, - ).let(::add) + ) - itemsFactory.createSelectThemeModeButton( + this + itemsFactory.createSelectThemeModeButton( currentThemeMode = state.selectedThemeMode, onClick = { showThemeModeSelector(state.selectedThemeMode) }, - ).let(::add) + ) } return items.toImmutableList() @@ -280,7 +280,7 @@ internal class AppSettingsModel @Inject constructor( val param = AnalyticsParam.OnOffState(enable) analyticsEventHandler.send(Settings.AppSettings.HideBalanceChanged(param)) - store.dispatch(DetailsAction.AppSettings.ChangeBalanceHiding(hideBalance = enable)) + store.dispatch(DetailsAction.AppSettings.ChangeBalanceHiding(shouldHideBalance = enable)) } private fun dismissDialog() { @@ -290,10 +290,10 @@ internal class AppSettingsModel @Inject constructor( private fun bootstrapAppCurrencyUpdates() { appCurrencyRepository .getSelectedAppCurrency() - .onEach { - if (it.code == store.state.globalState.appCurrency.code) return@onEach + .onEach { appCurrency -> + if (appCurrency.code == store.state.globalState.appCurrency.code) return@onEach - store.dispatchWithMain(DetailsAction.AppSettings.ChangeAppCurrency(it)) + store.dispatchWithMain(DetailsAction.AppSettings.ChangeAppCurrency(appCurrency)) } .launchIn(scope) .saveIn(appCurrencyUpdatesJobHolder) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt index eb76dc0b7a..a81c2bdbd1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreen.kt @@ -26,19 +26,19 @@ import com.tangem.wallet.R @Composable internal fun CardSettingsScreen(state: CardSettingsScreenState, modifier: Modifier = Modifier) { - val needReadCard = state.cardDetails == null + val isCardReadingNeeded = state.cardDetails == null SettingsScreensScaffold( + onBackClick = state.onBackClick, modifier = modifier, + titleRes = R.string.card_settings_title, content = { - if (needReadCard) { + if (isCardReadingNeeded) { CardSettingsReadCard(state.onScanCardClick) } else { CardSettings(state = state) } }, - titleRes = R.string.card_settings_title, - onBackClick = state.onBackClick, ) } @@ -123,8 +123,8 @@ private fun CardSettings(state: CardSettingsScreenState) { .fillMaxWidth() .testTag(DeviceSettingsScreenTestTags.LAZY_LIST), ) { - items(state.cardDetails) { - val paddingBottom = when (it) { + items(state.cardDetails) { cardInfo -> + val paddingBottom = when (cardInfo) { is CardInfo.CardId, is CardInfo.Issuer -> TangemTheme.dimens.spacing12 is CardInfo.SignedHashes -> TangemTheme.dimens.spacing14 is CardInfo.SecurityMode -> TangemTheme.dimens.spacing16 @@ -132,7 +132,7 @@ private fun CardSettings(state: CardSettingsScreenState) { is CardInfo.AccessCodeRecovery -> TangemTheme.dimens.spacing16 is CardInfo.ResetToFactorySettings -> TangemTheme.dimens.spacing28 } - val paddingTop = when (it) { + val paddingTop = when (cardInfo) { is CardInfo.CardId -> TangemTheme.dimens.spacing0 is CardInfo.Issuer -> TangemTheme.dimens.spacing12 is CardInfo.SignedHashes -> TangemTheme.dimens.spacing12 @@ -145,8 +145,8 @@ private fun CardSettings(state: CardSettingsScreenState) { modifier = Modifier .fillMaxWidth() .clickable( - enabled = it.clickable, - onClick = { state.onElementClick(it) }, + enabled = cardInfo.isClickable, + onClick = { state.onElementClick(cardInfo) }, ) .padding( start = TangemTheme.dimens.spacing20, @@ -155,25 +155,25 @@ private fun CardSettings(state: CardSettingsScreenState) { top = paddingTop, ), ) { - val titleColor = if (it.clickable) { + val titleColor = if (cardInfo.isClickable) { TangemTheme.colors.text.primary1 } else { TangemTheme.colors.text.tertiary } - val subtitleColor = if (it.clickable) { + val subtitleColor = if (cardInfo.isClickable) { TangemTheme.colors.text.secondary } else { TangemTheme.colors.text.tertiary } Text( - text = it.titleRes.resolveReference(), + text = cardInfo.titleRes.resolveReference(), color = titleColor, style = TangemTheme.typography.subtitle1, modifier = Modifier.testTag(DeviceSettingsScreenTestTags.ITEM_TITLE), ) Spacer(modifier = Modifier.size(TangemTheme.dimens.size4)) Text( - text = it.subtitle.resolveReference(), + text = cardInfo.subtitle.resolveReference(), color = subtitleColor, style = TangemTheme.typography.body2, modifier = Modifier.testTag(DeviceSettingsScreenTestTags.ITEM_SUBTITLE), diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt index cbf005de4c..5f4415c577 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt @@ -18,7 +18,7 @@ internal data class CardSettingsScreenState( internal sealed class CardInfo( val titleRes: TextReference, val subtitle: TextReference, - val clickable: Boolean = false, + val isClickable: Boolean = false, ) { class CardId(subtitle: String) : CardInfo( titleRes = TextReference.Res(R.string.details_row_title_cid), @@ -38,29 +38,29 @@ internal sealed class CardInfo( class SecurityMode(securityOption: SecurityOption, clickable: Boolean) : CardInfo( titleRes = TextReference.Res(R.string.card_settings_security_mode), subtitle = TextReference.Res(securityOption.toTitleRes()), - clickable = clickable, + isClickable = clickable, ) data object ChangeAccessCode : CardInfo( titleRes = TextReference.Res(R.string.card_settings_change_access_code), subtitle = TextReference.Res(R.string.card_settings_change_access_code_footer), - clickable = true, + isClickable = true, ) - class AccessCodeRecovery(val enabled: Boolean) : CardInfo( + class AccessCodeRecovery(val isEnabled: Boolean) : CardInfo( titleRes = TextReference.Res(R.string.card_settings_access_code_recovery_title), - subtitle = if (enabled) { + subtitle = if (isEnabled) { TextReference.Res(R.string.common_enabled) } else { TextReference.Res(R.string.common_disabled) }, - clickable = true, + isClickable = true, ) class ResetToFactorySettings(description: TextReference) : CardInfo( titleRes = TextReference.Res(R.string.card_settings_reset_card_to_factory), subtitle = description, - clickable = true, + isClickable = true, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreen.kt index 8315639120..c40418cc3d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreen.kt @@ -16,8 +16,8 @@ import com.tangem.wallet.R @Composable fun AccessCodeRecoveryScreen(state: AccessCodeRecoveryScreenState, onBackClick: () -> Unit) { SettingsScreensScaffold( - content = { AccessCodeRecoveryOptions(state = state) }, onBackClick = onBackClick, + content = { AccessCodeRecoveryOptions(state = state) }, ) } @@ -38,13 +38,13 @@ fun AccessCodeRecoveryOptions(state: AccessCodeRecoveryScreenState) { DetailsRadioButtonElement( title = stringResourceSafe(id = R.string.common_enabled), subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_enabled_description), - selected = state.enabledSelection, + isSelected = state.isEnabledSelection, onClick = { state.onOptionClick(true) }, ) DetailsRadioButtonElement( title = stringResourceSafe(id = R.string.common_disabled), subtitle = stringResourceSafe(id = R.string.card_settings_access_code_recovery_disabled_description), - selected = !state.enabledSelection, + isSelected = !state.isEnabledSelection, onClick = { state.onOptionClick(false) }, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreenState.kt index b4b6c68535..2d2301ab70 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/AccessCodeRecoveryScreenState.kt @@ -1,15 +1,15 @@ package com.tangem.tap.features.details.ui.cardsettings.coderecovery /** - * @property enabledOnCard Indicates whether access code recovery is enabled on the card - * @property enabledSelection Represents the currently selected option in the app (not yet saved on the card) + * @property isEnabledOnCard Indicates whether access code recovery is enabled on the card + * @property isEnabledSelection Represents the currently selected option in the app (not yet saved on the card) * @property isSaveChangesEnabled Determines if the user is allowed to save their selection to the card * @property onSaveChangesClick Callback function called when the user wants to apply the selected option * @property onOptionClick Callback function called when the user selects an option * */ data class AccessCodeRecoveryScreenState( - val enabledOnCard: Boolean, - val enabledSelection: Boolean, + val isEnabledOnCard: Boolean, + val isEnabledSelection: Boolean, val isSaveChangesEnabled: Boolean, val onSaveChangesClick: () -> Unit, val onOptionClick: (Boolean) -> Unit, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/model/AccessCodeRecoveryModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/model/AccessCodeRecoveryModel.kt index 298d2ac7d7..ac083db4c6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/model/AccessCodeRecoveryModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/coderecovery/model/AccessCodeRecoveryModel.kt @@ -43,8 +43,8 @@ internal class AccessCodeRecoveryModel @Inject constructor( ) return AccessCodeRecoveryScreenState( - enabledOnCard = isEnabled, - enabledSelection = isEnabled, + isEnabledOnCard = isEnabled, + isEnabledSelection = isEnabled, isSaveChangesEnabled = false, onSaveChangesClick = ::saveChanges, onOptionClick = ::selectOption, @@ -52,7 +52,7 @@ internal class AccessCodeRecoveryModel @Inject constructor( } private fun saveChanges() = modelScope.launch { - val isEnabled = screenState.value.enabledSelection + val isEnabled = screenState.value.isEnabledSelection tangemSdkManager .setAccessCodeRecoveryEnabled(scannedScanResponse.card.cardId, isEnabled) @@ -78,10 +78,10 @@ internal class AccessCodeRecoveryModel @Inject constructor( } private fun selectOption(isEnabled: Boolean) { - screenState.update { - it.copy( - enabledSelection = isEnabled, - isSaveChangesEnabled = isEnabled != it.enabledOnCard, + screenState.update { state -> + state.copy( + isEnabledSelection = isEnabled, + isSaveChangesEnabled = isEnabled != state.isEnabledOnCard, ) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt index 73bbf10991..09eb4199ef 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/domain/CardSettingsInteractor.kt @@ -23,9 +23,9 @@ internal class CardSettingsInteractor @Inject constructor() { } fun update(transform: (ScanResponse) -> ScanResponse) { - _scannedScanResponse.update { - requireNotNull(it) - transform(it) + _scannedScanResponse.update { scanResponse -> + requireNotNull(scanResponse) + transform(scanResponse) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt index 1d5b58407d..08aa5ef0b0 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/model/CardSettingsModel.kt @@ -12,9 +12,9 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.card.ScanCardProcessor -import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.common.util.getBackupCardsCount +import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.requireColdWallet @@ -34,6 +34,7 @@ import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsIntera import com.tangem.tap.features.details.ui.common.utils.* import com.tangem.tap.store import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.addIf import com.tangem.wallet.R import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -57,7 +58,7 @@ internal class CardSettingsModel @Inject constructor( private val params = paramsContainer.require() - private var previousBiometricsRequestPolicy: Boolean = false + private var isBiometricsRequestPolicyPrevious: Boolean = false private val userWalletId = params.userWalletId @@ -77,20 +78,19 @@ internal class CardSettingsModel @Inject constructor( // Reset card scanned data cardSettingsInteractor.clear() // Restore the previous value of access code request policy - cardSdkConfigRepository.isBiometricsRequestPolicy = previousBiometricsRequestPolicy + cardSdkConfigRepository.isBiometricsRequestPolicy = isBiometricsRequestPolicyPrevious } private fun updateAccessCodeRequestPolicy() { runBlocking { // !!!IMPORTANT!!!: Do not forget to restore the previous value in onCleared() method - previousBiometricsRequestPolicy = cardSdkConfigRepository.isBiometricsRequestPolicy + isBiometricsRequestPolicyPrevious = cardSdkConfigRepository.isBiometricsRequestPolicy val userWallet = getUserWalletUseCase(userWalletId) .getOrElse { error("User wallet $userWalletId not found") } .requireColdWallet() - cardSdkConfigRepository.isBiometricsRequestPolicy = - userWallet.scanResponse.card.isAccessCodeSet && + cardSdkConfigRepository.isBiometricsRequestPolicy = userWallet.scanResponse.card.isAccessCodeSet && settingsRepository.shouldSaveAccessCodes() } } @@ -135,34 +135,39 @@ internal class CardSettingsModel @Inject constructor( ) val isResetCardAllowed = isResetToFactoryAllowedByCard(card, cardTypesResolver) - val cardDetails = buildList { - CardInfo.CardId(cardId).let(::add) - CardInfo.Issuer(card.issuer.name).let(::add) + val cardDetails: List = buildList { + add(CardInfo.CardId(cardId)) + add(CardInfo.Issuer(card.issuer.name)) - if (!cardTypesResolver.isTangemTwins()) { - CardInfo.SignedHashes(card.signedHashesCount().toString()).let(::add) - } + addIf( + condition = !cardTypesResolver.isTangemTwins(), + element = CardInfo.SignedHashes(card.signedHashesCount().toString()), + ) - CardInfo.SecurityMode( - currentSecurityOption, - clickable = allowedSecurityOptions.size > 1, - ).let(::add) + add( + CardInfo.SecurityMode( + securityOption = currentSecurityOption, + clickable = allowedSecurityOptions.size > 1, + ), + ) - if (card.backupStatus?.isActive == true && card.isAccessCodeSet) { - CardInfo.ChangeAccessCode.let(::add) - } + addIf( + condition = card.backupStatus?.isActive == true && card.isAccessCodeSet, + element = CardInfo.ChangeAccessCode, + ) - if (isAccessCodeRecoveryAllowed(cardTypesResolver)) { - CardInfo.AccessCodeRecovery(isAccessCodeRecoveryEnabled(cardTypesResolver, card)).let(::add) - } + addIf( + condition = isAccessCodeRecoveryAllowed(cardTypesResolver), + element = CardInfo.AccessCodeRecovery(isAccessCodeRecoveryEnabled(cardTypesResolver, card)), + ) - if (isResetCardAllowed) { + addIf(isResetCardAllowed) { CardInfo.ResetToFactorySettings( description = getResetToFactoryDescription( isActiveBackupStatus = card.backupStatus?.isActive == true, typesResolver = cardTypesResolver, ), - ).let(::add) + ) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt b/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt index 98c792c2ef..19afbf0689 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt @@ -19,11 +19,11 @@ import com.tangem.wallet.R @Composable internal fun SettingsScreensScaffold( onBackClick: () -> Unit, - content: @Composable () -> Unit, modifier: Modifier = Modifier, @StringRes titleRes: Int? = null, - snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, addBottomInsets: Boolean = true, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + content: @Composable () -> Unit, fab: @Composable () -> Unit = {}, ) { val backgroundColor = TangemTheme.colors.background.secondary @@ -129,18 +129,18 @@ internal fun DetailsMainButton( } @Composable -internal fun DetailsRadioButtonElement(title: String, subtitle: String, selected: Boolean, onClick: () -> Unit) { +internal fun DetailsRadioButtonElement(title: String, subtitle: String, isSelected: Boolean, onClick: () -> Unit) { Row( modifier = Modifier .fillMaxWidth() .selectable( - selected = selected, + selected = isSelected, onClick = { onClick() }, ) .padding(start = 20.dp, end = 20.dp, top = 16.dp, bottom = 16.dp), ) { RadioButton( - selected = selected, + selected = isSelected, onClick = null, modifier = Modifier.padding(end = 20.dp), colors = RadioButtonDefaults.colors( diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/AccessCodeRecovery.kt b/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/AccessCodeRecovery.kt index 73af54606b..6d6a21df15 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/AccessCodeRecovery.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/common/utils/AccessCodeRecovery.kt @@ -7,7 +7,7 @@ internal fun isAccessCodeRecoveryAllowed(typeResolver: CardTypesResolver): Boole internal fun isAccessCodeRecoveryEnabled(typeResolver: CardTypesResolver, card: CardDTO): Boolean = if (typeResolver.isWallet2()) { - card.userSettings?.isUserCodeRecoveryAllowed ?: false + card.userSettings?.isUserCodeRecoveryAllowed == true } else { false } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt index e81602a4d2..b6557e7f4c 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt @@ -27,11 +27,11 @@ import com.tangem.tap.features.details.ui.resetcard.ResetCardScreenState.Dialog @Composable internal fun ResetCardScreen(state: ResetCardScreenState, onBackClick: () -> Unit, modifier: Modifier = Modifier) { SettingsScreensScaffold( + onBackClick = onBackClick, modifier = modifier, content = { ResetCardView(state = state) }, - onBackClick = onBackClick, ) when (val dialog = state.dialog) { @@ -65,7 +65,7 @@ private fun ResetCardView(state: ResetCardScreenState) { Conditions(state) DynamicSpacer(scrollState = scrollState) SpacerH16() - ResetButton(enabled = state.resetButtonEnabled, onResetButtonClick = state.onResetButtonClick) + ResetButton(enabled = state.isResetButtonEnabled, onResetButtonClick = state.onResetButtonClick) SpacerH16() } } @@ -113,18 +113,18 @@ private fun Description(text: TextReference) { @Composable private fun Conditions(state: ResetCardScreenState) { - state.warningsToShow.forEach { - when (it) { + state.warningsToShow.forEach { warning -> + when (warning) { ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS -> { ConditionCheckBox( - checkedState = state.acceptCondition1Checked, + checkedState = state.isAcceptCondition1Checked, onCheckedChange = state.onAcceptCondition1ToggleClick, description = TextReference.Res(R.string.reset_card_to_factory_condition_1), ) } ResetCardScreenState.WarningsToReset.LOST_PASSWORD_RESTORE -> { ConditionCheckBox( - checkedState = state.acceptCondition2Checked, + checkedState = state.isAcceptCondition2Checked, onCheckedChange = state.onAcceptCondition2ToggleClick, description = TextReference.Res(R.string.reset_card_to_factory_condition_2), ) @@ -238,12 +238,12 @@ private fun ResetCardScreenSample(modifier: Modifier = Modifier) { ) { ResetCardScreen( state = ResetCardScreenState( - resetButtonEnabled = true, - showResetPasswordButton = false, + isResetButtonEnabled = true, + isResetPasswordButtonShown = false, warningsToShow = listOf(ResetCardScreenState.WarningsToReset.LOST_WALLET_ACCESS), descriptionText = TextReference.Res(R.string.reset_card_with_backup_to_factory_message), - acceptCondition1Checked = false, - acceptCondition2Checked = false, + isAcceptCondition1Checked = false, + isAcceptCondition2Checked = false, onAcceptCondition1ToggleClick = {}, onAcceptCondition2ToggleClick = {}, onResetButtonClick = {}, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt index 27dacf387f..32eb289aec 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt @@ -5,12 +5,12 @@ import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.wallet.R internal data class ResetCardScreenState( - val resetButtonEnabled: Boolean, + val isResetButtonEnabled: Boolean, val descriptionText: TextReference, val warningsToShow: List, - val showResetPasswordButton: Boolean, - val acceptCondition1Checked: Boolean, - val acceptCondition2Checked: Boolean, + val isResetPasswordButtonShown: Boolean, + val isAcceptCondition1Checked: Boolean, + val isAcceptCondition2Checked: Boolean, val onAcceptCondition1ToggleClick: (Boolean) -> Unit, val onAcceptCondition2ToggleClick: (Boolean) -> Unit, val onResetButtonClick: () -> Unit, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt index 6cab06fac6..eaa4ce0a9f 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/model/ResetCardModel.kt @@ -97,15 +97,15 @@ internal class ResetCardModel @Inject constructor( } return ResetCardScreenState( - resetButtonEnabled = false, + isResetButtonEnabled = false, descriptionText = getResetToFactoryDescription( isActiveBackupStatus = isActiveBackupPrimaryCard, typesResolver = currentCardTypesResolver, ), warningsToShow = warningsToShow, - showResetPasswordButton = shouldShowResetPasswordButton, - acceptCondition1Checked = false, - acceptCondition2Checked = false, + isResetPasswordButtonShown = shouldShowResetPasswordButton, + isAcceptCondition1Checked = false, + isAcceptCondition2Checked = false, onAcceptCondition1ToggleClick = ::toggleFirstCondition, onAcceptCondition2ToggleClick = ::toggleSecondCondition, onResetButtonClick = { showDialog(ResetCardDialog.StartResetDialog) }, @@ -121,26 +121,26 @@ internal class ResetCardModel @Inject constructor( private fun toggleFirstCondition(isAccepted: Boolean) { screenState.update { prevState -> - val resetButtonEnabled = if (prevState.showResetPasswordButton) { - isAccepted && prevState.acceptCondition2Checked + val isResetButtonEnabled = if (prevState.isResetPasswordButtonShown) { + isAccepted && prevState.isAcceptCondition2Checked } else { isAccepted } prevState.copy( - acceptCondition1Checked = isAccepted, - resetButtonEnabled = resetButtonEnabled, + isAcceptCondition1Checked = isAccepted, + isResetButtonEnabled = isResetButtonEnabled, ) } } private fun toggleSecondCondition(isAccepted: Boolean) { screenState.update { prevState -> - val resetButtonEnabled = prevState.acceptCondition1Checked && isAccepted + val isResetButtonEnabled = prevState.isAcceptCondition1Checked && isAccepted prevState.copy( - acceptCondition2Checked = isAccepted, - resetButtonEnabled = resetButtonEnabled, + isAcceptCondition2Checked = isAccepted, + isResetButtonEnabled = isResetButtonEnabled, ) } } @@ -183,14 +183,14 @@ internal class ResetCardModel @Inject constructor( modelScope.launch { resetCardUseCase(cardId = primaryCardId, params = currentUserCodeParams).onRight { deleteSavedAccessCodesUseCase(cardId = primaryCardId) - val hasUserWallets = deleteWalletUseCase(userWalletId = currentUserWalletId).getOrElse { - Timber.e("Unable to delete user wallet: $it") + val hasUserWallets = deleteWalletUseCase(userWalletId = currentUserWalletId).getOrElse { error -> + Timber.e("Unable to delete user wallet: $error") return@launch } if (hasUserWallets) { - val newSelectedWallet = getSelectedWalletSyncUseCase().getOrElse { - error("Failed to get selected wallet: $it") + val newSelectedWallet = getSelectedWalletSyncUseCase().getOrElse { error -> + error("Failed to get selected wallet: $error") } store.onUserWalletSelected(newSelectedWallet) @@ -269,7 +269,7 @@ internal class ResetCardModel @Inject constructor( if (hotWalletFeatureToggles.isHotWalletEnabled) { store.dispatchNavigationAction { replaceAll(AppRoute.Home()) } } else { - val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync }.isSuccess + val isLocked = runCatching { userWalletsListManager.asLockable()?.isLocked }.isSuccess if (isLocked && userWalletsListManager.hasUserWallets) { store.dispatchNavigationAction { popTo() } } else { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeScreen.kt index 3a86a4f67c..981cd1c76a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/SecurityModeScreen.kt @@ -22,10 +22,10 @@ internal fun SecurityModeScreen( modifier: Modifier = Modifier, ) { SettingsScreensScaffold( + onBackClick = onBackClick, modifier = modifier, content = { SecurityModeOptions(state = state) }, // titleRes = R.string.card_settings_security_mode, - onBackClick = onBackClick, ) } @@ -57,7 +57,7 @@ private fun SecurityModeOptions(state: SecurityModeScreenState) { @Composable private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenState) { - val selected = option == state.selectedSecurityMode + val isSelected = option == state.selectedSecurityMode val title = option.toTitleRes() @@ -70,7 +70,7 @@ private fun SecurityOption(option: SecurityOption, state: SecurityModeScreenStat DetailsRadioButtonElement( title = stringResourceSafe(id = title), subtitle = stringResourceSafe(id = subtitle), - selected = selected, + isSelected = isSelected, onClick = { state.onNewModeSelected(option) }, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/model/SecurityModeModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/model/SecurityModeModel.kt index ee9cf3dd51..36fdf29787 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/model/SecurityModeModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/securitymode/model/SecurityModeModel.kt @@ -77,9 +77,9 @@ internal class SecurityModeModel @Inject constructor( SecurityOption.AccessCode -> tangemSdkManager.setAccessCode(cardId) } - cardSettingsInteractor.update { - it.copy( - card = it.card.copy( + cardSettingsInteractor.update { scanResponse -> + scanResponse.copy( + card = scanResponse.card.copy( isAccessCodeSet = selectedOption == SecurityOption.AccessCode, isPasscodeSet = selectedOption == SecurityOption.PassCode, ), diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index 1734b45276..1753406d30 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -150,7 +150,7 @@ internal class MainViewModel @Inject constructor( prepareSelectedWalletFeedback() // await while initial route stack is initialized - appRouterConfig.isInitialized.first { it } + appRouterConfig.initializedState.first { it } isSplashScreenShown = false } @@ -179,11 +179,9 @@ internal class MainViewModel @Inject constructor( private fun prepareSelectedWalletFeedback() { getSelectedWalletUseCase.invoke() .mapLeft { emptyFlow() } - .onRight { - it.distinctUntilChanged() - .onEach { userWallet -> - Analytics.setContext(userWallet) - } + .onRight { wallet -> + wallet.distinctUntilChanged() + .onEach { Analytics.setContext(it) } .flowOn(dispatchers.io) .launchIn(viewModelScope) } @@ -208,7 +206,7 @@ internal class MainViewModel @Inject constructor( return MoonPayService( apiKey = environmentConfig.moonPayApiKey, secretKey = environmentConfig.moonPayApiSecretKey, - logEnabled = LogConfig.network.moonPayService, + isLogEnabled = LogConfig.network.moonPayService, userWalletProvider = { getSelectedWalletUseCase.sync().getOrNull() }, ) } @@ -224,8 +222,8 @@ internal class MainViewModel @Inject constructor( .filter { it.isBalanceHidingNotificationEnabled && it.isBalanceHidden } - .onEach { - if (!it.isUpdateFromToast) { + .onEach { settings -> + if (!settings.isUpdateFromToast) { listenToFlipsUseCase.changeUpdateEnabled(false) val message = BottomSheetMessage.invoke( @@ -354,6 +352,7 @@ internal class MainViewModel @Inject constructor( listenToFlipsUseCase.changeUpdateEnabled(isUpdateEnabled = true) } + @Suppress("NullableToStringCall") private fun sendKeyboardIdentifierEvent() { viewModelScope.launch { val keyboardId = keyboardValidator.getKeyboardId() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 231ce5256e..ec4e9e12ab 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -34,11 +34,11 @@ object OnboardingHelper { } response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> { - val emptyWallets = response.card.wallets.isEmpty() - val activationInProgress = cardRepository.isActivationInProgress(cardId) + val areWalletsEmpty = response.card.wallets.isEmpty() + val isActivationInProgress = cardRepository.isActivationInProgress(cardId) val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup && !DemoHelper.isDemoCard(response) - emptyWallets || activationInProgress || isNoBackup + areWalletsEmpty || isActivationInProgress || isNoBackup } response.card.wallets.isNotEmpty() -> cardRepository.isActivationInProgress(cardId) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 2aa7a3ef24..6cbc795c53 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -50,8 +50,8 @@ object TradeCryptoMiddleware { fiatCurrencyName = action.appCurrencyCode, walletAddress = networkAddress, isDarkTheme = MutableAppThemeModeHolder.isDarkThemeActive, - )?.let { - store.dispatchOpenUrl(it) + )?.let { url -> + store.dispatchOpenUrl(url) Analytics.send(Token.Withdraw.ScreenOpened()) } } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt index ae5afc8d33..f6e882e5fb 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt @@ -72,8 +72,8 @@ internal class WelcomeModel @Inject constructor( this.state.update { prevState -> prevState.copy( - showUnlockWithBiometricsProgress = state.isUnlockWithBiometricsInProgress, - showUnlockWithCardProgress = state.isUnlockWithCardInProgress, + isUnlockWithBiometricsProgressVisible = state.isUnlockWithBiometricsInProgress, + isUnlockWithCardProgressVisible = state.isUnlockWithCardInProgress, warning = warning, error = state.error ?.takeIf { !it.silent && warning == null } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt index 78fb0451e0..6d0e6b6183 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt @@ -13,7 +13,7 @@ internal sealed interface WelcomeAction : Action { object ProceedWithCard : WelcomeAction { object Success : WelcomeAction data class Error(val error: TangemError) : WelcomeAction - data class ChangeProgress(val showProgress: Boolean) : WelcomeAction + data class ChangeProgress(val isProgress: Boolean) : WelcomeAction } object CloseError : WelcomeAction diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index af60602c2b..be2955e491 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -141,13 +141,13 @@ internal class WelcomeMiddleware { onSuccess = { scanResponse -> scope.launch { onCardScanned(scanResponse) } }, - onFailure = { - when (it) { + onFailure = { error -> + when (error) { is TangemSdkError.ExceptionError -> { store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success) } else -> { - store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(it)) + store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(error)) } } }, diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt index 0d9645dea6..f324887d0a 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt @@ -25,7 +25,7 @@ internal object WelcomeReducer { isUnlockWithCardInProgress = false, ) is WelcomeAction.ProceedWithCard.ChangeProgress -> state.copy( - isUnlockWithCardInProgress = action.showProgress, + isUnlockWithCardInProgress = action.isProgress, ) is WelcomeAction.ProceedWithBiometrics.Success -> state.copy(isUnlockWithBiometricsInProgress = false) is WelcomeAction.ProceedWithCard.Success -> state.copy(isUnlockWithCardInProgress = false) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt index 3ede2ae446..7c7e43f2b5 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeScreenState.kt @@ -5,8 +5,8 @@ import com.tangem.tap.features.welcome.ui.model.WarningModel internal data class WelcomeScreenState( val onPopBack: () -> Unit = {}, - val showUnlockWithBiometricsProgress: Boolean = false, - val showUnlockWithCardProgress: Boolean = false, + val isUnlockWithBiometricsProgressVisible: Boolean = false, + val isUnlockWithCardProgressVisible: Boolean = false, val warning: WarningModel? = null, val error: TextReference? = null, val onUnlockClick: () -> Unit = {}, diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreen.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreen.kt index ee1a00bb48..de791e45da 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WelcomeScreen.kt @@ -39,8 +39,8 @@ internal fun WelcomeScreen(state: WelcomeScreenState, modifier: Modifier = Modif .systemBarsPadding(), ) { WelcomeScreenContent( - showUnlockProgress = state.showUnlockWithBiometricsProgress, - showScanCardProgress = state.showUnlockWithCardProgress, + showUnlockProgress = state.isUnlockWithBiometricsProgressVisible, + showScanCardProgress = state.isUnlockWithCardProgressVisible, onUnlockClick = state.onUnlockClick, onScanCardClick = state.onScanCardClick, ) @@ -57,8 +57,8 @@ internal fun WelcomeScreen(state: WelcomeScreenState, modifier: Modifier = Modif WarningDialog(warning) LaunchedEffect(errorMessage, state.onCloseError) { - errorMessage?.let { - snackbarHostState.showSnackbar(it) + errorMessage?.let { message -> + snackbarHostState.showSnackbar(message) state.onCloseError() } } @@ -82,8 +82,8 @@ private class WelcomeComponentPreviewProvider : PreviewParameterProvider { - return if (useNewListRepository) { + return if (shouldUseNewListRepository) { userWalletsListRepository.userWalletsSync() } else { userWalletsListManager.userWalletsSync @@ -47,7 +47,7 @@ internal class DefaultAuthProvider( } private suspend fun getSelectedWallet(): UserWallet? { - return if (useNewListRepository) { + return if (shouldUseNewListRepository) { userWalletsListRepository.selectedUserWalletSync() } else { userWalletsListManager.selectedUserWalletSync diff --git a/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt b/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt index 15e74c5498..47188c465b 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/DefaultExpressAuthProvider.kt @@ -6,7 +6,7 @@ import java.util.concurrent.atomic.AtomicReference internal class DefaultExpressAuthProvider : ExpressAuthProvider { - private var uuid = AtomicReference(UUID.randomUUID()) + private val uuid = AtomicReference(UUID.randomUUID()) override fun getSessionId(): String { return uuid.get().toString() diff --git a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt index 95004c11d8..d29722ec81 100644 --- a/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt +++ b/app/src/main/java/com/tangem/tap/network/auth/di/AuthModule.kt @@ -32,7 +32,7 @@ internal class AuthModule { return DefaultAuthProvider( userWalletsListManager = userWalletsListManager, userWalletsListRepository = userWalletsListRepository, - useNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled, + shouldUseNewListRepository = hotWalletFeatureToggles.isHotWalletEnabled, ) } diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt index 6c4c20e0cc..5cf37e3a1c 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt @@ -54,7 +54,7 @@ internal class DefaultRampManager( sendUnavailabilityReason: ScenarioUnavailabilityReason?, ): Either { return either { - val sellSupportedByService = catch( + val isSellSupportedByService = catch( block = { val serviceCurrency = cryptoCurrencyConverter.convertBack(status.currency) @@ -78,7 +78,7 @@ internal class DefaultRampManager( } } - ensure(condition = sellSupportedByService) { + ensure(condition = isSellSupportedByService) { ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name) } diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt index c8235a8f74..554a0b4265 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt @@ -34,6 +34,7 @@ data class MoonPayUserStatus( val stateCode: String, ) +@Suppress("BooleanPropertyNaming") @JsonClass(generateAdapter = true) data class MoonPayCurrencies( @Json(name = "type") val type: String, diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt index 02bc4321ae..1f291b109b 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt @@ -26,7 +26,7 @@ import javax.crypto.spec.SecretKeySpec class MoonPayService( private val apiKey: String, private val secretKey: String, - private val logEnabled: Boolean, + private val isLogEnabled: Boolean, private val userWalletProvider: () -> UserWallet?, ) : ExchangeService { @@ -39,7 +39,7 @@ class MoonPayService( private val api: MoonPayApi by lazy { createRetrofitInstance( baseUrl = MoonPayApi.MOOONPAY_BASE_URL, - logEnabled = logEnabled, + logEnabled = isLogEnabled, ).create(MoonPayApi::class.java) } @@ -104,24 +104,35 @@ class MoonPayService( override fun availableForSell(currency: Currency): Boolean { val userWallet = userWalletProvider() ?: return false - val checkCardExchange = userWallet !is UserWallet.Cold || !userWallet.scanResponse.card.isStart2Coin + val isExchangeSupported = userWallet !is UserWallet.Cold || !userWallet.scanResponse.card.isStart2Coin - if (!checkCardExchange) return false + if (!isExchangeSupported) return false if (!isSellAllowed()) return false val availableForSell = status?.availableForSell ?: return false val supportedCurrency = currency.blockchain.moonPaySupportedCurrency ?: return false - return availableForSell.any { + return availableForSell.any { availableCurrency -> when (currency) { is Currency.Blockchain -> { - it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && - it.currencyCode.equals(other = supportedCurrency.currencyCode, ignoreCase = true) + availableCurrency.networkCode.equals( + other = supportedCurrency.networkCode, + ignoreCase = true, + ) && availableCurrency.currencyCode.equals( + other = supportedCurrency.currencyCode, + ignoreCase = true, + ) } is Currency.Token -> { - it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && - it.contractAddress.equals(other = currency.token.contractAddress, ignoreCase = true) + availableCurrency.networkCode.equals( + other = supportedCurrency.networkCode, + ignoreCase = true, + ) && + availableCurrency.contractAddress.equals( + other = currency.token.contractAddress, + ignoreCase = true, + ) } } } @@ -137,15 +148,18 @@ class MoonPayService( if (blockchain.isTestnet()) return blockchain.getTestnetTopUpUrl() val supportedCurrency = blockchain.moonPaySupportedCurrency ?: return null - val moonpayCurrency = status?.availableForSell?.firstOrNull { + val moonpayCurrency = status?.availableForSell?.firstOrNull { availableCurrency -> when (cryptoCurrency) { is CryptoCurrency.Coin -> { - it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && - it.currencyCode.equals(other = supportedCurrency.currencyCode, ignoreCase = true) + availableCurrency.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && + availableCurrency.currencyCode.equals(other = supportedCurrency.currencyCode, ignoreCase = true) } is CryptoCurrency.Token -> { - it.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && - it.contractAddress.equals(other = cryptoCurrency.contractAddress, ignoreCase = true) + availableCurrency.networkCode.equals(other = supportedCurrency.networkCode, ignoreCase = true) && + availableCurrency.contractAddress.equals( + other = cryptoCurrency.contractAddress, + ignoreCase = true, + ) } } } ?: return null @@ -184,7 +198,7 @@ class MoonPayService( } private fun isSellAllowed(): Boolean { - return status?.responseUserStatus?.isSellAllowed ?: false + return status?.responseUserStatus?.isSellAllowed == true } private companion object { diff --git a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt index d11526b9f4..f8350e77a9 100644 --- a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt @@ -48,13 +48,13 @@ class UserWalletManagerImpl( override suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? { val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) - return walletManager.wallet.amounts.firstNotNullOfOrNull { - it.takeIf { it.key is AmountType.Coin } - }?.value?.let { + return walletManager.wallet.amounts.firstNotNullOfOrNull { amountEntry -> + amountEntry.takeIf { amountEntry.key is AmountType.Coin } + }?.value?.let { amount -> ProxyAmount( - it.currencySymbol, - it.value ?: BigDecimal.ZERO, - it.decimals, + amount.currencySymbol, + amount.value ?: BigDecimal.ZERO, + amount.decimals, ) } } diff --git a/app/src/main/java/com/tangem/tap/routing/RootContent.kt b/app/src/main/java/com/tangem/tap/routing/RootContent.kt index 5c009849bc..66b66199d4 100644 --- a/app/src/main/java/com/tangem/tap/routing/RootContent.kt +++ b/app/src/main/java/com/tangem/tap/routing/RootContent.kt @@ -31,20 +31,21 @@ import com.tangem.core.ui.message.EventMessageEffect import com.tangem.core.ui.res.LocalRootBackgroundColor import com.tangem.core.ui.res.LocalSnackbarHostState import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.security.ProvideSecureFlagController import com.tangem.tap.routing.component.RoutingComponent import com.tangem.tap.routing.transitions.RoutingTransitionAnimationFactory -@Suppress("LongParameterList") +@Suppress("LongParameterList", "ReusedModifierInstance") @OptIn(ExperimentalDecomposeApi::class) @Composable internal fun RootContent( stack: Value>, backHandler: BackHandler, uiDependencies: UiDependencies, - wcContent: @Composable (modifier: Modifier) -> Unit, - hotAccessCodeContent: @Composable (modifier: Modifier) -> Unit, onBack: () -> Unit, modifier: Modifier = Modifier, + wcContent: @Composable (modifier: Modifier) -> Unit, + hotAccessCodeContent: @Composable (modifier: Modifier) -> Unit, ) { val context = LocalContext.current @@ -52,39 +53,41 @@ internal fun RootContent( activity = context as Activity, uiDependencies = uiDependencies, ) { - val snackbarHostState = LocalSnackbarHostState.current + ProvideSecureFlagController { + val snackbarHostState = LocalSnackbarHostState.current - Box(Modifier.background(LocalRootBackgroundColor.current.value)) { - Children( - modifier = modifier, - animation = childrenAnimation(backHandler = backHandler, onBack = onBack), - stack = stack, - ) { child -> - when (val instance = child.instance) { - is RoutingComponent.Child.Initial -> Unit - is RoutingComponent.Child.ComposableComponent -> { - instance.component.Content(Modifier.fillMaxSize()) - } - is RoutingComponent.Child.LegacyIntent -> { - // TODO: Remove and use it's own router: [REDACTED_JIRA] - LaunchedEffect(instance) { - startActivity(context, instance.intent, Bundle.EMPTY) + Box(Modifier.background(LocalRootBackgroundColor.current.value)) { + Children( + modifier = modifier, + animation = childrenAnimation(backHandler = backHandler, onBack = onBack), + stack = stack, + ) { child -> + when (val instance = child.instance) { + is RoutingComponent.Child.Initial -> Unit + is RoutingComponent.Child.ComposableComponent -> { + instance.component.Content(Modifier.fillMaxSize()) + } + is RoutingComponent.Child.LegacyIntent -> { + // TODO: Remove and use it's own router: [REDACTED_JIRA] + LaunchedEffect(instance) { + startActivity(context, instance.intent, Bundle.EMPTY) + } } } } + + wcContent(Modifier.fillMaxSize()) + + hotAccessCodeContent(Modifier.fillMaxSize()) + + TangemSnackbarHost( + modifier = Modifier + .align(Alignment.BottomCenter) + .navigationBarsPadding() + .padding(all = 16.dp), + hostState = snackbarHostState, + ) } - - wcContent(Modifier.fillMaxSize()) - - hotAccessCodeContent(Modifier.fillMaxSize()) - - TangemSnackbarHost( - modifier = Modifier - .align(Alignment.BottomCenter) - .navigationBarsPadding() - .padding(all = 16.dp), - hostState = snackbarHostState, - ) } EventMessageEffect() } diff --git a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt index 8030c44f97..8be5a8edea 100644 --- a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt +++ b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt @@ -133,7 +133,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor( AppRoute.Wallet } }.also { - appRouterConfig.isInitialized.value = true + appRouterConfig.initializedState.value = true checkForUnfinishedBackup() } } @@ -141,13 +141,13 @@ internal class DefaultRoutingComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { RootContent( - modifier = modifier, stack = stack, + backHandler = backHandler, uiDependencies = uiDependencies, + onBack = router::pop, + modifier = modifier, wcContent = { wcRoutingComponent.Content(it) }, hotAccessCodeContent = { hotAccessCodeRequestComponent.Content(it) }, - backHandler = backHandler, - onBack = router::pop, ) } diff --git a/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt b/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt index 393ab3a7df..a5ee03adad 100644 --- a/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt +++ b/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt @@ -11,7 +11,7 @@ internal interface AppRouterConfig { var routerScope: CoroutineScope? var componentRouter: Router? var stack: List? - val isInitialized: MutableStateFlow + val initializedState: MutableStateFlow // TODO: Replace with UI message handler: [REDACTED_JIRA] var snackbarHandler: SnackbarHandler? diff --git a/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt b/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt index 776802e429..7831308228 100644 --- a/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt +++ b/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt @@ -11,5 +11,5 @@ internal class MutableAppRouterConfig : AppRouterConfig { override var componentRouter: Router? = null override var stack: List? = null override var snackbarHandler: SnackbarHandler? = null - override val isInitialized: MutableStateFlow = MutableStateFlow(false) + override val initializedState: MutableStateFlow = MutableStateFlow(false) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt b/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt index b1332ff1c8..a28e2ff15e 100644 --- a/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt @@ -4,13 +4,10 @@ import androidx.compose.animation.core.CubicBezierEasing import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.tween import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.layout import com.arkivanov.decompose.extensions.compose.stack.animation.* import com.tangem.common.routing.AppRoute -import kotlin.compareTo -import kotlin.times object RoutingTransitionAnimationFactory { @@ -58,7 +55,7 @@ object RoutingTransitionAnimationFactory { @Suppress("MagicNumber") private fun slideAndFade(directions: Set? = null): StackAnimator { - val easing = CubicBezierEasing(0.55f, 0.0f, 0.0f, 1f) + val easing = CubicBezierEasing(a = 0.55f, b = 0.0f, c = 0.0f, d = 1f) return stackAnimator( animationSpec = tween(durationMillis = 400, easing = easing), diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 4f4eca31b6..04197aacb2 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -183,10 +183,10 @@ internal class ChildFactory @Inject constructor( token = route.token, appCurrency = route.appCurrency, showPortfolio = route.showPortfolio, - analyticsParams = route.analyticsParams?.let { + analyticsParams = route.analyticsParams?.let { params -> MarketsTokenDetailsComponent.AnalyticsParams( - blockchain = it.blockchain, - source = it.source, + blockchain = params.blockchain, + source = params.source, ) }, ), diff --git a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt index 992380b05e..e0eafb470d 100644 --- a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt +++ b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt @@ -311,7 +311,6 @@ private fun MarketChartPreview( } val coroutineScope = rememberCoroutineScope() - val look by dataProducer.lookState.collectAsState() TangemThemePreview { val growingColor = TangemTheme.colors.icon.accent diff --git a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressStatusUM.kt b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressStatusUM.kt index 6fcb02cc30..b49384c027 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressStatusUM.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/expressStatus/state/ExpressStatusUM.kt @@ -50,5 +50,4 @@ enum class ExpressStatusItemState { Done, Warning, Error, - ; } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt index 9c80a6a1b7..da0b138077 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -3,6 +3,7 @@ package com.tangem.common.ui.tokens import com.tangem.common.ui.R import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter +import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter import com.tangem.core.ui.components.token.state.TokenItemState @@ -220,13 +221,12 @@ class TokenItemStateConverter( if (!status.getStakedBalance().isZero()) { TokenItemState.FiatAmountState.Content.IconUM( iconRes = R.drawable.ic_staking_24, - useAccentColor = true, + tint = IconTint.Accent, ).let(::add) } if (status.value.sources.total == StatusSource.ONLY_CACHE) { TokenItemState.FiatAmountState.Content.IconUM( iconRes = R.drawable.ic_error_sync_24, - useAccentColor = false, ).let(::add) } }.toImmutableList(), diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/DevApiConfigsManager.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/DevApiConfigsManager.kt index f0de0bfdda..08703b2212 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/DevApiConfigsManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/DevApiConfigsManager.kt @@ -26,10 +26,10 @@ internal class DevApiConfigsManager( ) : MutableApiConfigsManager() { override val configs: StateFlow> - field = MutableStateFlow(value = getInitialConfigs()) + field = MutableStateFlow(value = getInitialConfigs()) override val isInitialized: StateFlow - field = MutableStateFlow(value = false) + field = MutableStateFlow(value = false) override fun initialize() { isInitialized.value = false diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MockApiConfigsManager.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MockApiConfigsManager.kt index 5df19d76e6..c423afc4c3 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MockApiConfigsManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MockApiConfigsManager.kt @@ -22,7 +22,7 @@ internal class MockApiConfigsManager( ) : MutableApiConfigsManager() { override val configs: StateFlow> - field = MutableStateFlow(value = getInitialConfigs()) + field = MutableStateFlow(value = getInitialConfigs()) override val isInitialized: StateFlow = MutableStateFlow(value = true) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MutableApiConfigsManager.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MutableApiConfigsManager.kt index cdd9a6b0a2..d0998c8395 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MutableApiConfigsManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/managers/MutableApiConfigsManager.kt @@ -20,7 +20,7 @@ abstract class MutableApiConfigsManager : ApiConfigsManager { * These listeners are notified whenever an environment change occurs. */ protected val registerListeners: Set - field = mutableSetOf() + field = mutableSetOf() /** Change api environment [environment] by [id] */ abstract suspend fun changeEnvironment(id: String, environment: ApiEnvironment) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/pay/TangemPayApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/pay/TangemPayApi.kt index b753ffc387..87a22537d3 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/pay/TangemPayApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/pay/TangemPayApi.kt @@ -9,6 +9,8 @@ import retrofit2.http.Header import retrofit2.http.POST import retrofit2.http.Query +private const val TX_HISTORY_PAGING_DEFAULT_LIMIT = 20 + @Suppress("TooManyFunctions") interface TangemPayApi { @@ -107,6 +109,13 @@ interface TangemPayApi { @Query("offset") offset: Int, ): ApiResponse + @GET("v1/customer/transactions") + suspend fun getTangemPayTxHistory( + @Header("Authorization") authHeader: String, + @Query("cursor") cursor: String?, + @Query("limit") limit: Int = TX_HISTORY_PAGING_DEFAULT_LIMIT, + ): ApiResponse + @GET("v1/customer/kyc") suspend fun getKycAccess(@Header("Authorization") authHeader: String): ApiResponse diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/TangemPayTxHistoryResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/TangemPayTxHistoryResponse.kt new file mode 100644 index 0000000000..c6e87268e6 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/TangemPayTxHistoryResponse.kt @@ -0,0 +1,83 @@ +package com.tangem.datasource.api.pay.models.response + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.joda.time.DateTime +import java.math.BigDecimal + +@JsonClass(generateAdapter = true) +data class TangemPayTxHistoryResponse( + @Json(name = "error") val error: String?, + @Json(name = "result") val result: Result, +) { + @JsonClass(generateAdapter = true) + data class Result( + @Json(name = "transactions") val transactions: List, + ) + + @JsonClass(generateAdapter = true) + data class Transaction( + @Json(name = "id") val id: String, // UUID (used as cursor for pagination) + @Json(name = "type") val type: String, // "SPEND", "COLLATERAL", "PAYMENT", "FEE" + @Json(name = "spend") val spend: Spend? = null, + @Json(name = "collateral") val collateral: Collateral? = null, + @Json(name = "payment") val payment: Payment? = null, + @Json(name = "fee") val fee: Fee? = null, + ) + + @JsonClass(generateAdapter = true) + data class Spend( + @Json(name = "amount") val amount: BigDecimal, + @Json(name = "currency") val currency: String, + @Json(name = "local_amount") val localAmount: BigDecimal? = null, + @Json(name = "local_currency") val localCurrency: String? = null, + @Json(name = "authorized_amount") val authorizedAmount: BigDecimal? = null, + @Json(name = "authorization_method") val authorizationMethod: String? = null, + @Json(name = "memo") val memo: String? = null, + @Json(name = "receipt") val receipt: Boolean? = null, + @Json(name = "merchant_name") val merchantName: String? = null, + @Json(name = "merchant_category") val merchantCategory: String? = null, + @Json(name = "merchant_category_code") val merchantCategoryCode: String? = null, + @Json(name = "merchant_id") val merchantId: String? = null, + @Json(name = "enriched_merchant_icon") val enrichedMerchantIcon: String? = null, + @Json(name = "enriched_merchant_name") val enrichedMerchantName: String? = null, + @Json(name = "enriched_merchant_category") val enrichedMerchantCategory: String? = null, + @Json(name = "card_id") val cardId: String? = null, + @Json(name = "card_type") val cardType: String? = null, + @Json(name = "status") val status: String? = null, + @Json(name = "declined_reason") val declinedReason: String? = null, + @Json(name = "authorized_at") val authorizedAt: DateTime? = null, + @Json(name = "posted_at") val postedAt: DateTime? = null, + ) + + @JsonClass(generateAdapter = true) + data class Collateral( + @Json(name = "amount") val amount: BigDecimal, + @Json(name = "currency") val currency: String, + @Json(name = "memo") val memo: String? = null, + @Json(name = "chain_id") val chainId: Long? = null, + @Json(name = "wallet_address") val walletAddress: String? = null, + @Json(name = "transaction_hash") val transactionHash: String? = null, + @Json(name = "posted_at") val postedAt: DateTime? = null, + ) + + @JsonClass(generateAdapter = true) + data class Payment( + @Json(name = "amount") val amount: BigDecimal, + @Json(name = "currency") val currency: String, + @Json(name = "memo") val memo: String? = null, + @Json(name = "chain_id") val chainId: Long? = null, + @Json(name = "wallet_address") val walletAddress: String? = null, + @Json(name = "transaction_hash") val transactionHash: String? = null, + @Json(name = "status") val status: String? = null, + @Json(name = "posted_at") val postedAt: DateTime? = null, + ) + + @JsonClass(generateAdapter = true) + data class Fee( + @Json(name = "amount") val amount: BigDecimal, + @Json(name = "currency") val currency: String, + @Json(name = "description") val description: String? = null, + @Json(name = "posted_at") val postedAt: DateTime? = null, + ) +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt index b58c058063..b3cef83f29 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/YieldDTO.kt @@ -198,6 +198,7 @@ data class YieldDTO( enum class RewardTypeDTO { @Json(name = "apy") APY, // compound rate + @Json(name = "apr") APR, // simple rate, diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt index 3184e87ead..3ef25cc80f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/SeedPhraseNotificationDTO.kt @@ -24,7 +24,5 @@ data class SeedPhraseNotificationDTO(val status: Status) { @Json(name = "accepted") ACCEPTED, - - ; } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt index aa6dd40222..283ea6c473 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt @@ -3,6 +3,8 @@ package com.tangem.datasource.di import com.tangem.datasource.local.datastore.RuntimeDataStore import com.tangem.datasource.local.txhistory.DefaultTxHistoryItemsStore import com.tangem.datasource.local.txhistory.TxHistoryItemsStore +import com.tangem.datasource.local.visa.DefaultTangemPayTxHistoryItemsStore +import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -20,4 +22,12 @@ internal object TxHistoryItemsStoreModule { dataStore = RuntimeDataStore(), ) } + + @Provides + @Singleton + fun provideTangemPayTxHistoryItemsStore(): TangemPayTxHistoryItemsStore { + return DefaultTangemPayTxHistoryItemsStore( + dataStore = RuntimeDataStore(), + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/visa/DefaultTangemPayTxHistoryItemsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/visa/DefaultTangemPayTxHistoryItemsStore.kt new file mode 100644 index 0000000000..1a6014d355 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/visa/DefaultTangemPayTxHistoryItemsStore.kt @@ -0,0 +1,24 @@ +package com.tangem.datasource.local.visa + +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.visa.model.TangemPayTxHistoryItem + +internal class DefaultTangemPayTxHistoryItemsStore( + dataStore: StringKeyDataStore>>, +) : TangemPayTxHistoryItemsStore, + StringKeyDataStoreDecorator>>(dataStore) { + override fun provideStringKey(key: UserWalletId): String = key.stringValue + + override suspend fun getSyncOrNull(key: UserWalletId, cursor: String): List? { + val storedValue = getSyncOrNull(key) + return storedValue?.get(cursor) + } + + override suspend fun store(key: UserWalletId, cursor: String, value: List) { + val oldValue = getSyncOrNull(key).orEmpty() + val newValue = oldValue.toMutableMap().apply { put(cursor, value) } + store(key, newValue) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayTxHistoryItemsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayTxHistoryItemsStore.kt new file mode 100644 index 0000000000..c327023e6b --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/visa/TangemPayTxHistoryItemsStore.kt @@ -0,0 +1,13 @@ +package com.tangem.datasource.local.visa + +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.visa.model.TangemPayTxHistoryItem + +interface TangemPayTxHistoryItemsStore { + + suspend fun getSyncOrNull(key: UserWalletId, cursor: String): List? + + suspend fun remove(key: UserWalletId) + + suspend fun store(key: UserWalletId, cursor: String, value: List) +} \ No newline at end of file diff --git a/core/pagination/src/main/java/com/tangem/pagination/fetcher/CursorBatchFetcher.kt b/core/pagination/src/main/java/com/tangem/pagination/fetcher/CursorBatchFetcher.kt new file mode 100644 index 0000000000..e41f84d55d --- /dev/null +++ b/core/pagination/src/main/java/com/tangem/pagination/fetcher/CursorBatchFetcher.kt @@ -0,0 +1,92 @@ +package com.tangem.pagination.fetcher + +import com.tangem.pagination.BatchFetchResult +import com.tangem.pagination.exception.EndOfPaginationException +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * First page: cursor = null + * Next pages: cursor = cursorFromItem(lastItemOfPreviousPage) + */ +class CursorBatchFetcher( + private val prefetchDistance: Int, + private val batchSize: Int, + private val subFetcher: SubFetcher, + private val cursorFromItem: (TItem) -> String, +) : BatchFetcher> { + + data class Request( + val limit: Int, + val cursor: String?, + val params: TRequestParams, + ) + + fun interface SubFetcher { + suspend fun fetch( + request: Request, + lastResult: BatchFetchResult>?, + isFirstBatchFetching: Boolean, + ): BatchFetchResult> + } + + private val lastRequest = MutableStateFlow?>(null) + + override suspend fun fetchFirst(requestParams: TRequestParams): BatchFetchResult> { + val request = Request( + cursor = null, + limit = prefetchDistance, + params = requestParams, + ) + + val result = runCatching { + subFetcher.fetch(request = request, lastResult = null, isFirstBatchFetching = true) + }.getOrElse { + currentCoroutineContext().ensureActive() + return BatchFetchResult.Error(it) + } + + lastRequest.value = request + return result + } + + override suspend fun fetchNext( + overrideRequestParams: TRequestParams?, + lastResult: BatchFetchResult>, + ): BatchFetchResult> { + val lastRequest = requireNotNull(lastRequest.value) { "fetchFirst() must be called before fetchNext()" } + + if (lastResult is BatchFetchResult.Success && lastResult.last && overrideRequestParams == null) { + return BatchFetchResult.Error(EndOfPaginationException()) + } + + val nextReq: Request = + if (lastResult is BatchFetchResult.Success>) { + val items = lastResult.data + if (items.isEmpty()) { + return BatchFetchResult.Error(EndOfPaginationException()) + } + + val nextCursor = cursorFromItem(items.last()) + + Request( + cursor = nextCursor, + limit = batchSize, + params = overrideRequestParams ?: lastRequest.params, + ) + } else { + lastRequest.copy(limit = batchSize, params = overrideRequestParams ?: lastRequest.params) + } + + val result = runCatching { + subFetcher.fetch(request = nextReq, lastResult = lastResult, isFirstBatchFetching = false) + }.getOrElse { + currentCoroutineContext().ensureActive() + return BatchFetchResult.Error(it) + } + + this.lastRequest.value = nextReq + return result + } +} \ No newline at end of file diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 6b3712c0c4..60247b72bc 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -520,9 +520,12 @@ バックアップへ移動 アクセスコードを作成する前にウォレットをバックアップしてください。 まずバックアップを完了する + バックアップなし 秘密鍵をオフラインで安全に保存する物理デバイス。 リカバリーフレーズ + 受信取引の通知を受け取る 鍵はアプリに保存されます + 新機能やアップデートの最新情報を入手 シードフレーズのバックアップ モバイルウォレットを作成する このリカバリーフレーズはすでにインポートされています。 diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 2adc2175b5..6fd8cbba3b 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1,6 +1,5 @@ - Ваш кошелёк не защищён без кода доступа. Архив Вы архивируете свой аккаунт, но в любое время можете вернуть его обратно Аккаунт @@ -42,14 +41,12 @@ Токены в сети %1$s не поддерживаются этой картой или кольцом из-за ограничений прошивки. У вас возникли трудности со сканированием карты или кольца? Эта карта не предназначена для работы с этим приложением - Используйте %1$s, чтобы быстро и безопасно разблокировать кошелёк и выполнять чувствительные действия, например, подписывать транзакции. Для аппаратных кошельков всё ещё требуется карта или кольцо для подписи. Комиссия по-умолчанию Подключите функцию комиссии по умолчанию и при формировании транзакции на отправку средств комиссия будет выставлена автоматически, а экран комиссии пропущен. Вы всегда сможете на него вернуться. Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem Включите биометрическую аутентификацию Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены из приложения. - Эта опция отключает использование биометрии для выполнения чувствительных действий. Каждый раз, например при подписании транзакции, вам потребуется вводить код доступа. Сохранение кода доступа Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой или кольцом вместо кода доступа будет запрашиваться биометрическая аутентификация. Cохранение кошелька @@ -59,12 +56,6 @@ Как в системе Тема Настройки приложения - Эти слова невозможно восстановить, если они будут потеряны. Храните их в надёжном месте. - Ваша секретная фраза восстановления — это фиксированный набор из %s случайных слов для доступа к вашему кошельку и его восстановления. - Эти слова невозможно восстановить, если они будут потеряны. Храните их в безопасности. - Храните в безопасности - Никому не сообщайте эти слова. Tangem никогда не будет их спрашивать. Ниже приведены %s слов вашей фразы восстановления кошелька. Используйте их, чтобы восстановить кошелёк в случае потери устройства. - Запишите эти %s слов в указанном порядке и храните их в безопасности и в тайне. Чтобы скрыть или показать баланс, просто поверните ваше устройство вниз или отключите опцию его в разделе \"Настройки\" Больше не показывать Понятно @@ -446,12 +437,12 @@ Перейти к бэкапу Пожалуйста, создайте резервную копию вашего кошелька перед установкой кода доступа. Сначала завершите создание резервной копии - Не завершено + Нет бэкапа Физические устройства, которые надёжно хранят ваш приватный ключ офлайн. Фраза восстановления - Ваши приватные ключи надёжно зашифрованы и хранятся на вашем телефоне + Получайте уведомления о входящих транзакциях Ключи хранятся в приложении - Создайте или восстановите свой кошелёк с помощью фразы восстановления — вашей встроенной резервной копии + Будьте в курсе новых функций и новостей Резервная копия сид-фразы Создать мобильный кошелек Эта фраза восстановления уже была импортирована @@ -705,7 +696,7 @@ Добавление токенов Вы добавили одну резервную карту или кольцо. После того, как процесс будет завершен, Вы больше не сможете добавить еще. Если у Вас есть еще одна карта или кольцо, добавьте ее в резервную копию. Хотите продолжить? Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас. - Парольная фраза — это дополнительная функция безопасности, которая добавляет слово или фразу к вашей фразе восстановления, создавая новый набор адресов кошелька для дополнительной защиты. + Парольная фраза — это расширенная функция безопасности, которую используют криптокошельки. Она добавляет дополнительное слово или фразу по вашему выбору к уже существующей seed - фразе, чтобы разблокировать совершенно новый набор адресов. Добавить карту или кольцо Сканировать карту Сканировать карту #%d diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index f276d1abc5..f82afb47ad 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - Without an access code, your wallet is not secure. + Without an access code, your wallet isn\'t secure. Skip anyway Access code not set Enter access code @@ -77,7 +77,7 @@ Tokens in %1$s network are not supported by this card or ring due to firmware limitation. Are you having difficulty scanning your card or ring? This card is not designed to work with this app - Use %1$s to unlock your wallet and approve sensitive actions, like signing transactions. For hardware wallets, a card or ring is still required to sign. + Use %1$s to quickly and securely unlock your wallet and authorize all sensitive actions, such as signing transactions. For hardware wallets, you will still need a card to sign. Default Fee Enable Default Fee to set transaction fees automatically and skip the Fee page when sending funds. You can always go back to this page if necessary. Go to settings to enable biometric authentication in the Tangem App @@ -88,7 +88,7 @@ Removing the saved devices deletes all the saved wallets and their access codes from the app. This will delete all the saved wallet access codes. Any further interaction with the wallet will require submitting the access code. Require Access Code - This option turns off biometrics for sensitive actions. You’ll need to enter your access code each time you sign a transaction. + This option disables biometric authentication for sensitive actions. You will be required to enter your access code every time, such as when signing a transaction. Save Access Code Biometric authentication will be requested instead of the access code for interactions with your card or ring. Keep the wallet in the app @@ -528,12 +528,12 @@ Go to backup Please back up your wallet before creating an access code. Finalize backup first - Incomplete + No backup Physical devices that securely store your private key offline. Recovery phrase - Your private keys are securely encrypted and stored on your phone + Get notified of incoming transactions Keys are stored in the app - Create or restore your wallet using a recovery phrase — your built-in backup. + Stay informed about new features and updates Seed phrase backup Create Mobile Wallet This recovery phrase has already been imported @@ -549,7 +549,7 @@ Key Migration Scan device Start upgrade - You’re about to upgrade to our hardware wallet. This will keep your assets safe in cold storage. + You\'re about to upgrade to a Tangem device, where your assets stay safe in cold storage. Tangem Wallet Upgrade to Hardware Wallet Keep your crypto safe with Tangem\'s top-tier hardware wallet. @@ -778,7 +778,7 @@ Add tokens You\'ve added one backup card or ring. When backup process is finished you can\'t add more backup devices. If you have one more card or ring, add it to the backup. Would you like to continue the backup process? The backup process is partly complete. You can\'t exit it now. - A passphrase is an optional security feature that adds a word or phrase to your recovery phrase, creating a new set of wallet addresses for extra protection. + The passphrase is an advanced security feature that crypto wallets use. It adds an extra word or phrase of your own choosing to your already existing recovery phrase to unlock a brand-new set of addresses. Add a card or ring Scan card Scan card #%d @@ -1344,15 +1344,9 @@ Use %s or scan a card/ring to have access to your wallet Connection failed: This dApp uses Wallet Connect version 1.0, which is not supported. Please ensure the dApp supports Wallet Connect version 2.0 to connect successfully. Stay up to date with the latest features and news - Real-time alerts for transactions, exchanges, and critical updates. - Transaction Alerts Get notified of incoming transactions Be the first to know about new promotions - Early access to fresh features and exclusive offers. - Feature and News Updates Would you like to use\nPush-notifications? - Enable push notifications and we’ll notify you instantly when funds arrive\n - Don’t Miss a Transaction Add new wallet Are you sure you want to forget this wallet? An error has occurred, please scan your card or ring to log in @@ -1450,8 +1444,6 @@ Wrong card or ring selected in Tangem App Failed to create transaction from Dapp data. Code: %s We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support - Multiple transactions - You’ll need to tap your Tangem device a few times to complete this process. No opened WalletConnect sessions Ooops. No Sessions. Failed to pairing WalletConnect session: %1$s @@ -1463,8 +1455,6 @@ This card can\'t be used to establish WalletConnect session This network is not supported. Please select another network. Select network - We\'re processing the transaction - Sending your funds... WalletConnect Sessions Connect to dApps WalletConnect diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt index 27c758f8b7..c5807f7912 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt @@ -68,9 +68,9 @@ fun CardWithIcon( internal fun IconWithTitleAndDescription( title: String, description: String?, + iconBackground: Color = TangemTheme.colors.background.secondary, icon: @Composable () -> Unit, additionalContent: @Composable () -> Unit = {}, - iconBackground: Color = TangemTheme.colors.background.secondary, ) { Row( modifier = Modifier diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt b/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt index 03d9ba11c0..fcf6361bca 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt @@ -75,7 +75,7 @@ fun Modifier.bottomFade( ) enum class FadePosition { - TOP, BOTTOM, LEFT, RIGHT; + TOP, BOTTOM, LEFT, RIGHT } @Stable diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt index e5c28bf0a6..e42d2ad4fc 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlock.kt @@ -169,12 +169,12 @@ private fun Preview_Tree() { }, content = { ArrowRowItems( - itemPadding = PaddingValues(vertical = TangemTheme.dimens.spacing4), items = persistentListOf( stringReference("Fist item"), stringReference("Second item"), stringReference("Third item"), ), + itemPadding = PaddingValues(vertical = TangemTheme.dimens.spacing4), rootContent = { PreviewItem(stringReference("Root")) }, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt index 423100d2e9..a39c6b7783 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt @@ -16,11 +16,11 @@ import kotlinx.collections.immutable.toImmutableList @Composable inline fun InformationBlockContentScope.ListItems( items: ImmutableList, - itemContent: @Composable BoxScope.(T) -> Unit, modifier: Modifier = Modifier, itemPadding: PaddingValues = PaddingValues(all = TangemTheme.dimens.spacing0), horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, verticalArragement: Arrangement.Vertical = Arrangement.Top, + itemContent: @Composable BoxScope.(T) -> Unit, ) { Column( modifier = modifier.fillMaxWidth(), @@ -42,10 +42,10 @@ inline fun InformationBlockContentScope.ListItems( @Composable inline fun InformationBlockContentScope.GridItems( items: ImmutableList, - itemContent: @Composable BoxScope.(T) -> Unit, modifier: Modifier = Modifier, verticalAlignment: Alignment.Vertical = Alignment.Top, horizontalArragement: Arrangement.Horizontal = Arrangement.Start, + itemContent: @Composable BoxScope.(T) -> Unit, ) { val rowItems by remember(items) { derivedStateOf { @@ -81,10 +81,10 @@ inline fun InformationBlockContentScope.GridItems( @Composable inline fun InformationBlockContentScope.ArrowRowItems( items: ImmutableList, - rootContent: @Composable BoxScope.() -> Unit, - itemContent: @Composable BoxScope.(T) -> Unit, modifier: Modifier = Modifier, itemPadding: PaddingValues = PaddingValues(all = TangemTheme.dimens.spacing0), + rootContent: @Composable BoxScope.() -> Unit, + itemContent: @Composable BoxScope.(T) -> Unit, ) { Column( modifier = modifier.fillMaxWidth(), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheet.kt index 196a5f2012..b239dfc92b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheet.kt @@ -48,10 +48,10 @@ const val MODAL_SHEET_MAX_HEIGHT = 0.8f inline fun TangemModalBottomSheet( config: TangemBottomSheetConfig, containerColor: Color = TangemTheme.colors.background.primary, + noinline onBack: (() -> Unit)? = null, skipPartiallyExpanded: Boolean = true, dismissOnClickOutside: Boolean = true, scrollableContent: Boolean = true, - noinline onBack: (() -> Unit)? = null, crossinline title: @Composable BoxScope.(T) -> Unit = {}, crossinline content: @Composable ColumnScope.(T) -> Unit, ) { @@ -202,9 +202,9 @@ inline fun BsContent( inline fun BasicModalBottomSheet( config: TangemBottomSheetConfig, sheetState: SheetState, + modifier: Modifier = Modifier, noinline onBack: (() -> Unit)? = null, noinline bsContent: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, ) { if (onBack != null) { ModalBottomSheetWithBackHandling( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt index 3826cc503f..6506a16d14 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt @@ -143,11 +143,11 @@ inline fun BasicModalBottomSheetWit config: TangemBottomSheetConfig, sheetState: SheetState, containerColor: Color, + modifier: Modifier = Modifier, noinline onBack: (() -> Unit)? = null, crossinline title: @Composable BoxScope.(T) -> Unit, crossinline content: @Composable (T) -> Unit, noinline footer: @Composable (BoxScope.(T) -> Unit)?, - modifier: Modifier = Modifier, ) { val model = config.content as? T ?: return diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheet.kt index 1ecce16ab7..26ad428e58 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/sheet/TangemBottomSheet.kt @@ -152,10 +152,10 @@ inline fun BasicBottomSheet( sheetState: SheetState, containerColor: Color, addBottomInsets: Boolean, + modifier: Modifier = Modifier, noinline onBack: (() -> Unit)? = null, crossinline title: @Composable (BoxScope.(T) -> Unit), crossinline content: @Composable (ColumnScope.(T) -> Unit), - modifier: Modifier = Modifier, ) { val model = config.content as? T ?: return diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt index a7fa9386f9..090b82dc91 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt @@ -111,10 +111,10 @@ fun ActionButton( fun ActionBaseButton( config: ActionButtonConfig, shape: RoundedCornerShape, - content: @Composable (modifier: Modifier) -> Unit, modifier: Modifier = Modifier, color: Color = TangemTheme.colors.button.secondary, containerColor: Color = TangemTheme.colors.background.secondary, + content: @Composable (modifier: Modifier) -> Unit, ) { val context = LocalContext.current val backgroundColor by animateColorAsState( @@ -163,9 +163,9 @@ fun ActionBaseButton( @Composable fun ActionButtonContent( config: ActionButtonConfig, - text: @Composable (Color) -> Unit, modifier: Modifier = Modifier, paddingBetweenIconAndText: Dp = 8.dp, + text: @Composable (Color) -> Unit, ) { Row( modifier = modifier diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/DefaultCurrencyIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/DefaultCurrencyIcon.kt index bc9b5f2707..630bd4cc12 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/DefaultCurrencyIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/DefaultCurrencyIcon.kt @@ -24,8 +24,8 @@ internal inline fun DefaultCurrencyIcon( size: Dp, alpha: Float, colorFilter: ColorFilter?, - crossinline errorIcon: @Composable () -> Unit, modifier: Modifier = Modifier, + crossinline errorIcon: @Composable () -> Unit, ) { var iconBackgroundColor by remember { mutableStateOf(Color.Transparent) } var isBackgroundColorDefined by remember { mutableStateOf(false) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt index 02e42068d6..e8fa3055bf 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt @@ -113,8 +113,8 @@ private fun TokenIcon( url: String?, alpha: Float, colorFilter: ColorFilter?, - errorIcon: @Composable () -> Unit, modifier: Modifier = Modifier, + errorIcon: @Composable () -> Unit, ) { if (url == null) { errorIcon() diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt index 94f6b64e78..f7ec1628af 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SearchBar.kt @@ -103,11 +103,11 @@ fun SearchBar( @OptIn(ExperimentalMaterial3Api::class) private fun DecorationBox( state: SearchBarUM, - innerTextField: @Composable () -> Unit, interactionSource: MutableInteractionSource, colors: TextFieldColors, focusManager: FocusManager, keyboardController: SoftwareKeyboardController?, + innerTextField: @Composable () -> Unit, ) { TextFieldDefaults.DecorationBox( value = state.query, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt index 1616968df4..e5c732ea8c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt @@ -121,12 +121,12 @@ fun SimpleTextField( @Composable private fun SimpleTextPlaceholder( - placeholder: TextReference?, value: String, textStyle: TextStyle, centered: Boolean, - textValue: @Composable () -> Unit, + placeholder: TextReference?, color: Color = TangemTheme.colors.text.disabled, + textValue: @Composable () -> Unit, ) { Box(contentAlignment = if (centered) Alignment.Center else Alignment.TopStart) { if (value.isBlank() && placeholder != null) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/icons/IconTint.kt b/core/ui/src/main/java/com/tangem/core/ui/components/icons/IconTint.kt new file mode 100644 index 0000000000..db6a607f67 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/icons/IconTint.kt @@ -0,0 +1,10 @@ +package com.tangem.core.ui.components.icons + +import androidx.compose.runtime.Stable + +@Stable +enum class IconTint { + Accent, + Warning, + Inactive, +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/icons/identicon/Blockies.kt b/core/ui/src/main/java/com/tangem/core/ui/components/icons/identicon/Blockies.kt index f1ba4a748e..83d46d09e3 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/icons/identicon/Blockies.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/icons/identicon/Blockies.kt @@ -58,8 +58,8 @@ internal data class Blockies( } private fun dataFromSeed(seed: MutableList) = MutableList(SIZE * SIZE) { DEFAULT_VALUE_F }.apply { - (0 until SIZE).forEach { row -> - (0 until HALF_SIZE).forEach { column -> + for (row in 0 until SIZE) { + for (column in 0 until HALF_SIZE) { val value = floor(nextSeed(seed) * PROBABILITY_COLOR) this[row * SIZE + column] = value this[(row + 1) * SIZE - column - 1] = value diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ArrowRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ArrowRow.kt index 67ef1ac47d..36d9892d0d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ArrowRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ArrowRow.kt @@ -21,9 +21,9 @@ import com.tangem.core.ui.utils.* @Composable inline fun ArrowRow( isLastItem: Boolean, - content: @Composable() (BoxScope.() -> Unit), modifier: Modifier = Modifier, contentPadding: PaddingValues = PaddingValues(all = TangemTheme.dimens.spacing0), + content: @Composable() (BoxScope.() -> Unit), ) { val density = LocalDensity.current.density val defaultRowHeight = TangemTheme.dimens.size0 diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/BlockchainRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/BlockchainRow.kt index df2cdf37a9..d89d6710c1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/BlockchainRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/BlockchainRow.kt @@ -27,7 +27,7 @@ private const val DISABLED_ICON_ALPHA = 0.4f * [Figma Component](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2737-2800&t=ewlXfWwbDnRhjw4B-4) * */ @Composable -fun BlockchainRow(model: BlockchainRowUM, action: @Composable BoxScope.() -> Unit, modifier: Modifier = Modifier) { +fun BlockchainRow(model: BlockchainRowUM, modifier: Modifier = Modifier, action: @Composable BoxScope.() -> Unit) { RowContentContainer( modifier = modifier .heightIn(min = TangemTheme.dimens.size52) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt index a9020f35ae..ffe4aae9e2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt @@ -53,10 +53,10 @@ fun ChainRow(model: ChainRowUM, modifier: Modifier = Modifier, action: @Composab @Composable inline fun ChainRowContainer( + modifier: Modifier = Modifier, icon: @Composable BoxScope.() -> Unit, text: @Composable BoxScope.() -> Unit, action: @Composable BoxScope.() -> Unit, - modifier: Modifier = Modifier, ) { RowContentContainer( modifier = modifier diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt index 383854fd65..590e93ddb5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/NetworkTitle.kt @@ -29,8 +29,8 @@ import com.tangem.core.ui.res.TangemThemePreview */ @Composable fun NetworkTitle( - title: @Composable BoxScope.() -> Unit, modifier: Modifier = Modifier, + title: @Composable BoxScope.() -> Unit, action: (@Composable BoxScope.() -> Unit)? = null, ) { val minHeight = if (action == null) TangemTheme.dimens.size36 else TangemTheme.dimens.size40 diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt index f07edb1802..2c55637c6e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt @@ -13,11 +13,11 @@ import com.tangem.core.ui.res.TangemTheme @Composable inline fun RowContentContainer( + modifier: Modifier = Modifier, + horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(TangemTheme.dimens.spacing8), icon: @Composable BoxScope.() -> Unit, text: @Composable BoxScope.() -> Unit, action: @Composable BoxScope.() -> Unit, - modifier: Modifier = Modifier, - horizontalArrangement: Arrangement.Horizontal = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { Row( modifier = modifier.fillMaxWidth(), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt index 9a4585eb59..7bb297765a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt @@ -22,6 +22,7 @@ import com.tangem.core.ui.R import com.tangem.core.ui.components.audits.AuditLabelUM import com.tangem.core.ui.components.currency.icon.CurrencyIcon import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.token.internal.* import com.tangem.core.ui.components.token.state.TokenItemState @@ -488,7 +489,14 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider TangemTheme.colors.icon.accent + IconTint.Warning -> TangemTheme.colors.icon.attention + IconTint.Inactive -> TangemTheme.colors.icon.inactive }, contentDescription = null, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenTitle.kt index 7f379e09a6..3fadd11e81 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenTitle.kt @@ -2,7 +2,9 @@ package com.tangem.core.ui.components.token.internal import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -13,8 +15,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.components.token.state.TokenItemState.TitleState as TokenTitleState @@ -56,6 +60,11 @@ private fun ContentTitle(state: TokenTitleState.Content, modifier: Modifier = Mo hasPending = state.hasPending, modifier = Modifier.align(alignment = Alignment.CenterVertically), ) + + YieldSupplyApyLabel( + apy = state.earnApy, + modifier = Modifier.align(alignment = Alignment.CenterVertically), + ) } } @@ -71,6 +80,25 @@ private fun CurrencyNameText(name: String, isAvailable: Boolean, modifier: Modif ) } +@Composable +private fun YieldSupplyApyLabel(apy: TextReference?, modifier: Modifier = Modifier) { + AnimatedVisibility(visible = apy != null, modifier = modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.text.accent.copy(alpha = 0.1f), + shape = TangemTheme.shapes.roundedCornersSmall2, + ), + ) { + Text( + text = apy?.resolveReference().orEmpty(), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.accent, + modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp), + ) + } + } +} + @Composable private fun PendingTransactionImage(hasPending: Boolean, modifier: Modifier = Modifier) { AnimatedVisibility(visible = hasPending, modifier = modifier) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt index be357d5ebf..f86a303999 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt @@ -3,6 +3,7 @@ package com.tangem.core.ui.components.token.state import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.audits.AuditLabelUM import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.extensions.TextReference import kotlinx.collections.immutable.ImmutableList @@ -167,6 +168,7 @@ sealed class TokenItemState { val text: TextReference, val hasPending: Boolean = false, val isAvailable: Boolean = true, + val earnApy: TextReference? = null, ) : TitleState() data object Loading : TitleState() @@ -204,7 +206,7 @@ sealed class TokenItemState { data class IconUM( val iconRes: Int, - val useAccentColor: Boolean, + val tint: IconTint = IconTint.Inactive, ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt index 395b09aed0..7a54751d4b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tooltip/TangemTooltip.kt @@ -24,9 +24,9 @@ import kotlinx.coroutines.launch @Composable fun TangemTooltip( text: String, - content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + content: @Composable (Modifier) -> Unit, ) { InternalTangemTooltip( modifier = modifier, @@ -46,9 +46,9 @@ fun TangemTooltip( @Composable fun TangemTooltip( text: AnnotatedString, - content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + content: @Composable (Modifier) -> Unit, ) { InternalTangemTooltip( modifier = modifier, @@ -68,10 +68,10 @@ fun TangemTooltip( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun InternalTangemTooltip( - tooltipContent: @Composable () -> Unit, - content: @Composable (Modifier) -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, + tooltipContent: @Composable () -> Unit, + content: @Composable (Modifier) -> Unit, ) { val tooltipState = rememberTooltipState(isPersistent = true) val coroutineScope = rememberCoroutineScope() diff --git a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt index 6128a71c4a..d6f98918b8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableBottomSheetComponent.kt @@ -10,13 +10,16 @@ interface ComposableBottomSheetComponent { @Composable fun BottomSheet() -} -fun getEmptyComposableBottomSheetComponent() = EmptyComposableBottomSheetComponent + companion object { + val EMPTY = EmptyComposableBottomSheetComponent + } +} object EmptyComposableBottomSheetComponent : ComposableBottomSheetComponent { override fun dismiss() {} @Composable - override fun BottomSheet() {} + override fun BottomSheet() { + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableContentComponent.kt b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableContentComponent.kt index b242250e28..9e861ebc84 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableContentComponent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableContentComponent.kt @@ -9,9 +9,11 @@ fun interface ComposableContentComponent { @Composable fun Content(modifier: Modifier) -} -fun getEmptyComposableContentComponent() = EmptyComposableContentComponent + companion object { + val EMPTY = EmptyComposableContentComponent + } +} object EmptyComposableContentComponent : ComposableContentComponent { @Composable diff --git a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableModularContentComponent.kt b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableModularContentComponent.kt index 15a24d2001..f6531b2720 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableModularContentComponent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/decompose/ComposableModularContentComponent.kt @@ -15,9 +15,11 @@ interface ComposableModularContentComponent { @Composable fun Footer() -} -fun getEmptyComposableModularContentComponent() = EmptyComposableModularContentComponent + companion object { + val EMPTY = EmptyComposableModularContentComponent + } +} object EmptyComposableModularContentComponent : ComposableModularContentComponent { @Composable diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt index 696d1619ee..85baa41281 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt @@ -52,10 +52,10 @@ fun TangemTheme( @Composable fun TangemTheme( - isDark: Boolean = false, windowSize: WindowSize, typography: TangemTypography = TangemTheme.typography, dimens: TangemDimens = TangemTheme.dimens, + isDark: Boolean = false, vibratorHapticManager: VibratorHapticManager? = null, eventMessageHandler: EventMessageHandler = remember { EventMessageHandler() }, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, diff --git a/core/ui/src/main/java/com/tangem/core/ui/security/DisableScreenshots.kt b/core/ui/src/main/java/com/tangem/core/ui/security/DisableScreenshots.kt index 58466d1140..dcb5b64ee2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/security/DisableScreenshots.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/security/DisableScreenshots.kt @@ -1,11 +1,53 @@ package com.tangem.core.ui.security +import android.app.Activity import android.view.WindowManager import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.platform.LocalContext import com.tangem.core.ui.utils.findActivity -import timber.log.Timber + +private val LocalSecureFlagController = staticCompositionLocalOf { + error("No SecureFlagController provided") +} + +private class SecureFlagController(private val activity: Activity) { + private var count by mutableIntStateOf(0) + + fun enable() { + if (count == 0) { + activity.window.setFlags( + WindowManager.LayoutParams.FLAG_SECURE, + WindowManager.LayoutParams.FLAG_SECURE, + ) + } + count++ + } + + fun disable() { + count-- + if (count == 0) { + activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + } +} + +@Composable +fun ProvideSecureFlagController(content: @Composable () -> Unit) { + val activity = LocalContext.current.findActivity() + val controller = remember(activity) { SecureFlagController(activity) } + + CompositionLocalProvider( + LocalSecureFlagController provides controller, + content = content, + ) +} /** * Disables screenshots for the current composition. @@ -14,18 +56,10 @@ import timber.log.Timber */ @Composable fun DisableScreenshotsDisposableEffect() { - val activity = LocalContext.current.findActivity() + val secureFlagController = LocalSecureFlagController.current - DisposableEffect(activity) { - Timber.d("Security mode: enabled") - activity.window.setFlags( - WindowManager.LayoutParams.FLAG_SECURE, - WindowManager.LayoutParams.FLAG_SECURE, - ) - - onDispose { - Timber.d("Security mode: disabled") - activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) - } + DisposableEffect(secureFlagController) { + secureFlagController.enable() + onDispose { secureFlagController.disable() } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/test/StoriesScreenTestTags.kt b/core/ui/src/main/java/com/tangem/core/ui/test/StoriesScreenTestTags.kt index a1b7576f50..12d500ece7 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/test/StoriesScreenTestTags.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/test/StoriesScreenTestTags.kt @@ -6,4 +6,6 @@ object StoriesScreenTestTags { const val ORDER_BUTTON = "STORIES_SCREEN_ORDER_BUTTON" const val CREATE_NEW_WALLET_BUTTON = "STORIES_SCREEN_CREATE_NEW_WALLET_BUTTON" const val ADD_EXISTING_WALLET_BUTTON = "STORIES_SCREEN_ADD_EXISTING_WALLET_BUTTON" + const val TITLE = "STORIES_SCREEN_TITLE" + const val TEXT = "STORIES_SCREEN_TEXT" } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt index ebce7baf36..06d5fe6b63 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DecimalFormatterExt.kt @@ -64,9 +64,7 @@ fun DecimalFormat.getValidatedNumberWithFixedDecimals(text: String, decimals: In val beforeDecimal = filteredChars.substringBefore(decimalSeparator) val afterDecimal = filteredChars.substringAfter(decimalSeparator) decimals.getWithIntegerDecimals(beforeDecimal, decimalSeparator, afterDecimal) - } - // If there is no dot, just take all digits - else { + } else { // If there is no dot, just take all digits filteredChars } } @@ -87,9 +85,7 @@ fun DecimalFormat.formatWithThousands(text: String, decimals: Int): String { .reversed() val afterDecimal = localizedText.substringAfter(decimalSeparator) decimals.getWithIntegerDecimals(beforeDecimal, decimalSeparator, afterDecimal) - } - // If there is no dot, just take all digits - else { + } else { // If there is no dot, just take all digits localizedText.reversed() .chunked(TEXT_CHUNK_THOUSAND) .joinToString(thousandsSeparator.toString()) diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/InputNumberFormatter.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/InputNumberFormatter.kt index 7873012994..7d1c7e7c2c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/InputNumberFormatter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/InputNumberFormatter.kt @@ -44,9 +44,7 @@ class InputNumberFormatter( val beforeDecimal = filteredChars.substringBefore(decimalSeparator) val afterDecimal = filteredChars.substringAfter(decimalSeparator) beforeDecimal + decimalSeparator + afterDecimal.take(decimals) - } - // If there is no dot, just take all digits - else { + } else { // If there is no dot, just take all digits filteredChars } } @@ -62,9 +60,7 @@ class InputNumberFormatter( .reversed() val afterDecimal = text.substringAfter(decimalSeparator) beforeDecimal + decimalSeparator + afterDecimal.take(decimals) - } - // If there is no dot, just take all digits - else { + } else { // If there is no dot, just take all digits text.reversed() .chunked(TEXT_CHUNK_THOUSAND) .joinToString(thousandsSeparator.toString()) diff --git a/core/ui/src/main/res/drawable/ic_attention_12.xml b/core/ui/src/main/res/drawable/ic_attention_12.xml new file mode 100644 index 0000000000..960069e5dd --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_attention_12.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt b/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt index 6ab240ea41..2a9271dc93 100644 --- a/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt +++ b/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt @@ -20,7 +20,22 @@ fun Collection.copy(): Collection { return this.map { it } } -inline fun List.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? { - val index = indexOfFirst(predicate) - return if (index == -1) null else index +/** + * Adds an element to the mutable list if the specified condition is true. + * + * @param condition The condition to evaluate. + * @param create A lambda function that creates the element to be added. + */ +inline fun MutableCollection.addIf(condition: Boolean, create: () -> T) { + addIf(condition = condition, element = create()) +} + +/** + * Adds an element to the mutable list if the specified condition is true. + * + * @param condition The condition to evaluate. + * @param element The element to be added. + */ +fun MutableCollection.addIf(condition: Boolean, element: T) { + if (condition) this.add(element) } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/List.kt b/core/utils/src/main/java/com/tangem/utils/extensions/List.kt index cad9f1fada..a47b33dee3 100644 --- a/core/utils/src/main/java/com/tangem/utils/extensions/List.kt +++ b/core/utils/src/main/java/com/tangem/utils/extensions/List.kt @@ -72,4 +72,9 @@ fun List.filterIf(condition: Boolean, predicate: (T) -> Boolean): List } else { this } +} + +inline fun List.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? { + val index = indexOfFirst(predicate) + return if (index == -1) null else index } \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt index 430e9a0290..bae6820a73 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/cache/etag/ETagsStore.kt @@ -29,6 +29,5 @@ interface ETagsStore { enum class Key { WalletAccounts, UserTokens, - ; } } \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/quote/QuotesFetcher.kt b/data/common/src/main/kotlin/com/tangem/data/common/quote/QuotesFetcher.kt index 4f1e8b92ce..bc89150de8 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/quote/QuotesFetcher.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/quote/QuotesFetcher.kt @@ -47,7 +47,6 @@ interface QuotesFetcher { value = setOf(PRICE, PRICE_CHANGE_24H, PRICE_CHANGE_1W, PRICE_CHANGE_30D).combine(), ), LAST_UPDATED_AT(value = "lastUpdatedAt"), - ; } sealed interface Error { diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt index c2d3ece8ad..7519633126 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt @@ -1,9 +1,11 @@ package com.tangem.data.pay.di import com.tangem.data.pay.repository.DefaultKycRepository +import com.tangem.data.pay.repository.DefaultTangemPayTxHistoryRepository import com.tangem.data.pay.repository.DefaultOnboardingRepository import com.tangem.domain.pay.repository.KycRepository import com.tangem.domain.pay.repository.OnboardingRepository +import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -21,4 +23,8 @@ internal interface TangemPayDataModule { @Binds @Singleton fun bindOnboardingRepository(repository: DefaultOnboardingRepository): OnboardingRepository + + @Binds + @Singleton + fun bindTangemPayTxHistoryRepository(repository: DefaultTangemPayTxHistoryRepository): TangemPayTxHistoryRepository } \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultKycRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultKycRepository.kt index 0230e63d63..1775e41665 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultKycRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultKycRepository.kt @@ -1,6 +1,5 @@ package com.tangem.data.pay.repository -import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.pay.TangemPayApi import com.tangem.domain.pay.KycStartInfo import com.tangem.domain.pay.repository.KycRepository @@ -16,9 +15,9 @@ internal class DefaultKycRepository @Inject constructor( override suspend fun getKycStartInfo() = withContext(dispatchers.io) { requestHelper.request { authHeader -> - tangemPayApi.getKycAccess(authHeader = authHeader).getOrThrow().result + tangemPayApi.getKycAccess(authHeader = authHeader) }.map { - KycStartInfo(token = it.token, locale = it.locale) + KycStartInfo(token = it.result.token, locale = it.result.locale) } } } \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt index 24eb2cbaa8..cd8d7634f2 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt @@ -3,13 +3,11 @@ package com.tangem.data.pay.repository import arrow.core.Either import arrow.core.raise.either import com.tangem.core.error.UniversalError -import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.pay.TangemPayApi import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest import com.tangem.domain.pay.model.CustomerInfo import com.tangem.domain.pay.model.ProductInstance import com.tangem.domain.pay.repository.OnboardingRepository -import com.tangem.domain.visa.error.VisaApiError import javax.inject.Inject private const val VALID_STATUS = "valid" @@ -21,19 +19,17 @@ internal class DefaultOnboardingRepository @Inject constructor( override suspend fun validateDeeplink(link: String): Either = either { return requestHelper.request { - tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link)).getOrThrow().result - ?: raise(VisaApiError.UnknownWithoutCode) - }.map { result -> result.status == VALID_STATUS } + tangemPayApi.validateDeeplink(DeeplinkValidityRequest(link)) + }.map { it.result?.status == VALID_STATUS } } override suspend fun getCustomerInfo(): Either = either { return requestHelper.request { authHeader -> - val response = tangemPayApi.getCustomerMe(authHeader).getOrThrow() - response.result ?: raise(VisaApiError.UnknownWithoutCode) - }.map { result -> + tangemPayApi.getCustomerMe(authHeader) + }.map { CustomerInfo( - productInstance = result.productInstance?.let { ProductInstance(id = it.id, status = it.status) }, - kycStatus = result.kyc?.status, + productInstance = it.result?.productInstance?.let { ProductInstance(id = it.id, status = it.status) }, + kycStatus = it.result?.kyc?.status, ) } } diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt new file mode 100644 index 0000000000..f104ae6725 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultTangemPayTxHistoryRepository.kt @@ -0,0 +1,95 @@ +package com.tangem.data.pay.repository + +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.visa.utils.TangemPayTxHistoryItemConverter +import com.tangem.datasource.api.pay.TangemPayApi +import com.tangem.datasource.local.visa.TangemPayTxHistoryItemsStore +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow +import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext +import com.tangem.domain.tangempay.model.TangemPayTxHistoryListConfig +import com.tangem.domain.tangempay.repository.TangemPayTxHistoryRepository +import com.tangem.domain.visa.model.TangemPayTxHistoryItem +import com.tangem.pagination.BatchFetchResult +import com.tangem.pagination.BatchListSource +import com.tangem.pagination.fetcher.BatchFetcher +import com.tangem.pagination.fetcher.CursorBatchFetcher +import com.tangem.pagination.toBatchFlow +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import javax.inject.Inject + +private const val INITIAL_CURSOR = "initial_cursor_key" + +internal class DefaultTangemPayTxHistoryRepository @Inject constructor( + private val requestPerformer: TangemPayRequestPerformer, + private val visaApi: TangemPayApi, + private val cacheRegistry: CacheRegistry, + private val txHistoryItemsStore: TangemPayTxHistoryItemsStore, + private val dispatchers: CoroutineDispatcherProvider, +) : TangemPayTxHistoryRepository { + + override fun getTxHistoryBatchFlow( + batchSize: Int, + context: TangemPayTxHistoryListBatchingContext, + ): TangemPayTxHistoryListBatchFlow { + return BatchListSource( + fetchDispatcher = dispatchers.io, + context = context, + generateNewKey = { keys -> keys.lastOrNull()?.inc() ?: 0 }, + batchFetcher = createFetcher(batchSize), + ).toBatchFlow() + } + + private fun createFetcher( + batchSize: Int, + ): BatchFetcher> { + return CursorBatchFetcher( + prefetchDistance = batchSize, + batchSize = batchSize, + subFetcher = { request, _, _ -> + val items = loadItems(config = request.params, cursor = request.cursor, limit = request.limit) + BatchFetchResult.Success( + data = items, + last = items.size < request.limit, + empty = items.isEmpty(), + ) + }, + cursorFromItem = { item -> item.id }, // last item’s id becomes next cursor + ) + } + + private suspend fun loadItems( + config: TangemPayTxHistoryListConfig, + cursor: String?, + limit: Int, + ): List { + cacheRegistry.invokeOnExpire( + key = getCacheKey(userWalletId = config.userWalletId, cursor = cursor), + skipCache = config.refresh, + block = { fetch(userWalletId = config.userWalletId, cursor = cursor, pageSize = limit) }, + ) + + return txHistoryItemsStore.getSyncOrNull( + key = config.userWalletId, + cursor = cursor ?: INITIAL_CURSOR, + ).orEmpty() + } + + private fun getCacheKey(userWalletId: UserWalletId, cursor: String?): String { + return "tangem_pay_tx_history_${userWalletId}_${cursor ?: INITIAL_CURSOR}" + } + + private suspend fun fetch(userWalletId: UserWalletId, cursor: String?, pageSize: Int) { + val response = requestPerformer.request { authHeader -> + visaApi.getTangemPayTxHistory( + authHeader = authHeader, + limit = pageSize, + cursor = cursor, + ) + }.getOrNull() + response?.let { + val items = TangemPayTxHistoryItemConverter.convertList(response.result.transactions) + txHistoryItemsStore.store(key = userWalletId, cursor = cursor ?: INITIAL_CURSOR, value = items) + } + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt index 943480ef0e..dabdf56d8b 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt @@ -4,7 +4,9 @@ import arrow.core.Either import arrow.core.raise.either import com.squareup.moshi.Moshi import com.tangem.core.error.UniversalError +import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.ApiResponseError +import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.visa.TangemPayStorage @@ -44,18 +46,19 @@ internal class TangemPayRequestPerformer @Inject constructor( private val refreshTokensMutex = Mutex() private var refreshTokensJob: Deferred>? = null - suspend fun request(requestBlock: suspend (header: String) -> T): Either = either { - withContext(dispatchers.io) { - performRequest(requestBlock = requestBlock, refreshTokens = ::refreshAuthTokens).bind() + suspend fun request(requestBlock: suspend (header: String) -> ApiResponse): Either = + either { + withContext(dispatchers.io) { + performRequest(requestBlock = requestBlock, refreshTokens = ::refreshAuthTokens).bind() + } } - } private suspend fun performRequest( - requestBlock: suspend (header: String) -> T, + requestBlock: suspend (header: String) -> ApiResponse, refreshTokens: (suspend () -> Either)? = null, ): Either = either { runCatching { - requestBlock("Bearer ${getAccessTokens().bind().accessToken}") + requestBlock("Bearer ${getAccessTokens().bind().accessToken}").getOrThrow() }.getOrElse { error -> when (error) { is ApiResponseError.HttpException -> { diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/TangemPayTxHistoryItemConverter.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/TangemPayTxHistoryItemConverter.kt new file mode 100644 index 0000000000..9c4257091e --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/TangemPayTxHistoryItemConverter.kt @@ -0,0 +1,51 @@ +package com.tangem.data.visa.utils + +import com.tangem.datasource.api.pay.models.response.TangemPayTxHistoryResponse +import com.tangem.domain.visa.model.TangemPayTxHistoryItem +import com.tangem.utils.converter.Converter + +internal object TangemPayTxHistoryItemConverter : + Converter { + + @Suppress("CyclomaticComplexMethod") + override fun convert(value: TangemPayTxHistoryResponse.Transaction): TangemPayTxHistoryItem { + val spend = value.spend + val collateral = value.collateral + val payment = value.payment + val fee = value.fee + + return TangemPayTxHistoryItem( + id = value.id, + date = when { + spend != null -> spend.postedAt + collateral != null -> collateral.postedAt + payment != null -> payment.postedAt + fee != null -> fee.postedAt + else -> null + }, + amount = when { + spend != null -> spend.amount + collateral != null -> collateral.amount + payment != null -> payment.amount + fee != null -> fee.amount + else -> null + }, + merchantName = when { + spend != null -> spend.merchantName + else -> null + }, + status = when { + spend != null -> spend.status + payment != null -> payment.status + else -> null + }, + currency = when { + spend != null -> spend.currency + collateral != null -> collateral.currency + payment != null -> payment.currency + fee != null -> fee.currency + else -> null + }, + ) + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemConverter.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemConverter.kt new file mode 100644 index 0000000000..6311b23e70 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemConverter.kt @@ -0,0 +1,20 @@ +package com.tangem.data.visa.utils + +import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse +import com.tangem.domain.visa.model.VisaTxHistoryItem +import com.tangem.utils.converter.Converter + +internal object VisaTxHistoryItemConverter : Converter { + + override fun convert(value: VisaTxHistoryResponse.Transaction): VisaTxHistoryItem { + return VisaTxHistoryItem( + id = value.transactionId.toString(), + date = value.transactionDt, + amount = value.blockchainAmount, + fiatAmount = value.transactionAmount, + merchantName = value.merchantName, + status = value.transactionStatus, + fiatCurrency = findCurrencyByNumericCode(value.transactionCurrencyCode), + ) + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt deleted file mode 100644 index d7be726e36..0000000000 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.data.visa.utils - -import com.tangem.datasource.api.pay.models.response.VisaTxHistoryResponse -import com.tangem.domain.visa.model.VisaTxHistoryItem - -internal class VisaTxHistoryItemFactory { - - fun create(transaction: VisaTxHistoryResponse.Transaction): VisaTxHistoryItem { - return VisaTxHistoryItem( - id = transaction.transactionId.toString(), - date = transaction.transactionDt, - amount = transaction.blockchainAmount, - fiatAmount = transaction.transactionAmount, - merchantName = transaction.merchantName, - status = transaction.transactionStatus, - fiatCurrency = findCurrencyByNumericCode(transaction.transactionCurrencyCode), - ) - } -} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt index 44649d0bf8..01950d27b7 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt @@ -20,8 +20,6 @@ internal class VisaTxHistoryPagingSource( val requestTxHistory: suspend (offset: Int, pageSize: Int) -> VisaTxHistoryResponse, ) : PagingSource() { - private val itemsFactory = VisaTxHistoryItemFactory() - private val cardPublicKey = params.cardPublicKey private val pageSize = params.pageSize private val isRefresh = params.isRefresh @@ -82,7 +80,7 @@ internal class VisaTxHistoryPagingSource( pagedItems.update { it.toMutableMap().apply { - this[offset] = response.transactions.map(itemsFactory::create) + this[offset] = response.transactions.map(VisaTxHistoryItemConverter::convert) } } } diff --git a/data/wallet-manager/src/test/java/com/tangem/data/walletmanager/UpdateWalletManagerResultFactoryTest.kt b/data/wallet-manager/src/test/java/com/tangem/data/walletmanager/UpdateWalletManagerResultFactoryTest.kt index 4c5c929262..94d6776409 100644 --- a/data/wallet-manager/src/test/java/com/tangem/data/walletmanager/UpdateWalletManagerResultFactoryTest.kt +++ b/data/wallet-manager/src/test/java/com/tangem/data/walletmanager/UpdateWalletManagerResultFactoryTest.kt @@ -250,7 +250,9 @@ internal class UpdateWalletManagerResultFactoryTest { ), ), currenciesAmounts = setOf( - UpdateWalletManagerResult.CryptoCurrencyAmount.Coin(value = BigDecimal.ZERO), // default for demo + UpdateWalletManagerResult.CryptoCurrencyAmount.Coin( + value = BigDecimal.ZERO, + ), // default for demo ), currentTransactions = emptySet(), ), @@ -272,7 +274,9 @@ internal class UpdateWalletManagerResultFactoryTest { ), ), currenciesAmounts = setOf( - UpdateWalletManagerResult.CryptoCurrencyAmount.Coin(value = BigDecimal.ONE), // used demo amount + UpdateWalletManagerResult.CryptoCurrencyAmount.Coin( + value = BigDecimal.ONE, + ), // used demo amount ), currentTransactions = emptySet(), ), diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt index d8edb5dc18..56a5f027a2 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt @@ -415,6 +415,6 @@ internal class DefaultWalletsRepository( else -> ActivatePromoCodeError.ActivationFailed } return@fold error.left() - },) + }) } } \ No newline at end of file diff --git a/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt b/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt index f305beb722..d2e030bc54 100644 --- a/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt +++ b/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt @@ -114,62 +114,64 @@ class DefaultWalletsRepositoryTest { } @Test - fun `GIVEN API returns wallets WHEN getWalletsInfo THEN should return converted wallets and update cache if requested`() = runTest { - // GIVEN - val applicationId = "test_app_id" - val wallet1Id = "1234567890abcdef" - val wallet2Id = "fedcba0987654321" - val walletResponses = listOf( - WalletResponse( - id = wallet1Id, - notifyStatus = true, - ), - WalletResponse( - id = wallet2Id, - notifyStatus = false, - ), - ) - coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses) - coEvery { preferencesDataStore.updateData(any()) } returns mockk() + fun `GIVEN API returns wallets WHEN getWalletsInfo THEN should return converted wallets and update cache if requested`() = + runTest { + // GIVEN + val applicationId = "test_app_id" + val wallet1Id = "1234567890abcdef" + val wallet2Id = "fedcba0987654321" + val walletResponses = listOf( + WalletResponse( + id = wallet1Id, + notifyStatus = true, + ), + WalletResponse( + id = wallet2Id, + notifyStatus = false, + ), + ) + coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses) + coEvery { preferencesDataStore.updateData(any()) } returns mockk() - // WHEN - val result = repository.getWalletsInfo(applicationId, updateCache = true) + // WHEN + val result = repository.getWalletsInfo(applicationId, updateCache = true) - // THEN - assertThat(result).hasSize(2) - assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id) - assertThat(result[0].isNotificationsEnabled).isTrue() - assertThat(result[1].walletId.stringValue).isEqualTo(wallet2Id) - assertThat(result[1].isNotificationsEnabled).isFalse() + // THEN + assertThat(result).hasSize(2) + assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id) + assertThat(result[0].isNotificationsEnabled).isTrue() + assertThat(result[1].walletId.stringValue).isEqualTo(wallet2Id) + assertThat(result[1].isNotificationsEnabled).isFalse() - coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) } - coVerify(exactly = 2) { preferencesDataStore.updateData(any()) } - } + coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) } + coVerify(exactly = 2) { preferencesDataStore.updateData(any()) } + } @Test - fun `GIVEN API returns wallets WHEN getWalletsInfo with updateCache false THEN should return converted wallets without updating cache`() = runTest { - // GIVEN - val applicationId = "test_app_id" - val wallet1Id = "1234567890abcdef" - val walletResponses = listOf( - WalletResponse( - id = wallet1Id, - notifyStatus = true, - ), - ) - coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses) + fun `GIVEN API returns wallets WHEN getWalletsInfo with updateCache false THEN should return converted wallets without updating cache`() = + runTest { + // GIVEN + val applicationId = "test_app_id" + val wallet1Id = "1234567890abcdef" + val walletResponses = listOf( + WalletResponse( + id = wallet1Id, + notifyStatus = true, + ), + ) + coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses) - // WHEN - val result = repository.getWalletsInfo(applicationId, updateCache = false) + // WHEN + val result = repository.getWalletsInfo(applicationId, updateCache = false) - // THEN - assertThat(result).hasSize(1) - assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id) - assertThat(result[0].isNotificationsEnabled).isTrue() + // THEN + assertThat(result).hasSize(1) + assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id) + assertThat(result[0].isNotificationsEnabled).isTrue() - coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) } - coVerify(exactly = 0) { preferencesDataStore.updateData(any()) } - } + coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) } + coVerify(exactly = 0) { preferencesDataStore.updateData(any()) } + } @Test fun `GIVEN user wallets and application ID WHEN associateWallets THEN should convert and send to API`() = runTest { @@ -271,8 +273,8 @@ class DefaultWalletsRepositoryTest { // GIVEN coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Error( - HttpException(code = HttpException.Code.NOT_FOUND, message = null, errorBody = null), - ) as ApiResponse + HttpException(code = HttpException.Code.NOT_FOUND, message = null, errorBody = null), + ) as ApiResponse // WHEN val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr") @@ -288,8 +290,8 @@ class DefaultWalletsRepositoryTest { // GIVEN coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Error( - HttpException(code = HttpException.Code.CONFLICT, message = null, errorBody = null), - ) as ApiResponse + HttpException(code = HttpException.Code.CONFLICT, message = null, errorBody = null), + ) as ApiResponse // WHEN val result = repository.activatePromoCode(promoCode = "PROMO", bitcoinAddress = "addr") diff --git a/data/yield-supply/src/main/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepository.kt b/data/yield-supply/src/main/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepository.kt index 86c802d7db..d804ebb1c3 100644 --- a/data/yield-supply/src/main/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepository.kt +++ b/data/yield-supply/src/main/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepository.kt @@ -30,6 +30,7 @@ internal class DefaultYieldSupplyTransactionRepository( override suspend fun createEnterTransactions( userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, + maxNetworkFee: BigDecimal, ): List { val cryptoCurrency = cryptoCurrencyStatus.currency @@ -62,6 +63,7 @@ internal class DefaultYieldSupplyTransactionRepository( existingYieldContractAddress = existingYieldContractAddress, calculatedYieldContractAddress = calculatedYieldContractAddress, yieldTokenStatus = yieldTokenStatus, + maxNetworkFee = maxNetworkFee, ) } @@ -93,12 +95,14 @@ internal class DefaultYieldSupplyTransactionRepository( ) } + @Suppress("LongParameterList") private fun buildEnterTransactions( walletManager: WalletManager, cryptoCurrency: CryptoCurrency.Token, existingYieldContractAddress: String?, calculatedYieldContractAddress: String, yieldTokenStatus: YieldSupplyStatus?, + maxNetworkFee: BigDecimal, ): MutableList { val enterTransactions = mutableListOf() @@ -108,6 +112,7 @@ internal class DefaultYieldSupplyTransactionRepository( createDeployTransaction( walletManager = walletManager, cryptoCurrency = cryptoCurrency, + maxNetworkFee = maxNetworkFee, ), ) } @@ -118,6 +123,7 @@ internal class DefaultYieldSupplyTransactionRepository( cryptoCurrency = cryptoCurrency, yieldSupplyStatus = yieldTokenStatus, yieldContractAddress = calculatedYieldContractAddress, + maxNetworkFee = maxNetworkFee, ), ) !yieldTokenStatus.isActive -> enterTransactions.add( @@ -126,6 +132,7 @@ internal class DefaultYieldSupplyTransactionRepository( cryptoCurrency = cryptoCurrency, yieldSupplyStatus = yieldTokenStatus, yieldContractAddress = calculatedYieldContractAddress, + maxNetworkFee = maxNetworkFee, ), ) else -> Unit @@ -202,6 +209,7 @@ internal class DefaultYieldSupplyTransactionRepository( isActive = sdkSupplyStatus?.isActive == true, isInitialized = sdkSupplyStatus?.isInitialized == true, isAllowedToSpend = isAllowedToSpend, + // maxNetworkFee = sdkSupplyStatus?.maxNetworkFee, ) }.onFailure(Timber::e).getOrNull() } @@ -209,11 +217,12 @@ internal class DefaultYieldSupplyTransactionRepository( private fun createDeployTransaction( walletManager: WalletManager, cryptoCurrency: CryptoCurrency.Token, + maxNetworkFee: BigDecimal, ): TransactionData.Uncompiled { val callData = YieldSupplyContractCallDataProviderFactory.getDeployCallData( tokenContractAddress = cryptoCurrency.contractAddress, walletAddress = walletManager.wallet.address, - maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency), + maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrency), ) val factoryContractAddress = walletManager.getYieldSupplyContractAddresses()?.factoryContractAddress @@ -234,10 +243,11 @@ internal class DefaultYieldSupplyTransactionRepository( cryptoCurrency: CryptoCurrency.Token, yieldContractAddress: String, yieldSupplyStatus: YieldSupplyStatus, + maxNetworkFee: BigDecimal, ): TransactionData.Uncompiled { val callData = YieldSupplyContractCallDataProviderFactory.getInitTokenCallData( tokenContractAddress = cryptoCurrency.contractAddress, - maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency), + maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrency), ) return createTransaction( @@ -255,10 +265,11 @@ internal class DefaultYieldSupplyTransactionRepository( cryptoCurrency: CryptoCurrency.Token, yieldContractAddress: String, yieldSupplyStatus: YieldSupplyStatus, + maxNetworkFee: BigDecimal, ): TransactionData.Uncompiled { val callData = YieldSupplyContractCallDataProviderFactory.getReactivateTokenCallData( tokenContractAddress = cryptoCurrency.contractAddress, - maxNetworkFee = MAX_NETWORK_FEE.convertToSdkAmount(cryptoCurrency), + maxNetworkFee = maxNetworkFee.convertToSdkAmount(cryptoCurrency), ) return createTransaction( @@ -364,8 +375,4 @@ internal class DefaultYieldSupplyTransactionRepository( isAllowedToSpend = yieldSupplyStatus?.isAllowedToSpend ?: false, ), ) - - private companion object { - val MAX_NETWORK_FEE: BigDecimal = BigDecimal.TEN // TODO for TESTNET only - } } \ No newline at end of file diff --git a/data/yield-supply/src/test/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepositoryTest.kt b/data/yield-supply/src/test/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepositoryTest.kt index 069e3630f8..a31cac1f50 100644 --- a/data/yield-supply/src/test/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepositoryTest.kt +++ b/data/yield-supply/src/test/java/com/tangem/data/yield/supply/DefaultYieldSupplyTransactionRepositoryTest.kt @@ -16,7 +16,6 @@ import com.tangem.domain.models.yield.supply.YieldSupplyStatus import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import com.tangem.blockchain.yieldsupply.providers.YieldSupplyStatus as SDKYieldSupplyStatus import io.mockk.coEvery import io.mockk.every import io.mockk.mockk @@ -26,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import java.math.BigDecimal +import com.tangem.blockchain.yieldsupply.providers.YieldSupplyStatus as SDKYieldSupplyStatus @TestInstance(TestInstance.Lifecycle.PER_CLASS) class DefaultYieldSupplyTransactionRepositoryTest { @@ -74,7 +74,11 @@ class DefaultYieldSupplyTransactionRepositoryTest { coEvery { walletManager.isAllowedToSpend(any()) } returns false coEvery { walletManager.calculateYieldContract() } returns yieldContractAddress - val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus) + val result = repository.createEnterTransactions( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxNetworkFee = BigDecimal.TEN, + ) // Assert that 3 transactions are returned: deploy, approve, enter Truth.assertThat(result).isNotNull() @@ -117,13 +121,18 @@ class DefaultYieldSupplyTransactionRepositoryTest { fun `createEnterTransactions returns init-approve-enter transactions`() = runTest { coEvery { walletManager.getYieldContract() } returns yieldContractAddress coEvery { walletManager.calculateYieldContract() } returns yieldContractAddress + coEvery { walletManager.isAllowedToSpend(any()) } returns false coEvery { walletManager.getYieldSupplyStatus(any()) } returns SDKYieldSupplyStatus( isActive = false, isInitialized = false, maxNetworkFee = BigDecimal.TEN, ) - val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus) + val result = repository.createEnterTransactions( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxNetworkFee = BigDecimal.TEN, + ) // Assert that 3 transactions are returned: init token, approve, enter Truth.assertThat(result).isNotNull() @@ -165,13 +174,18 @@ class DefaultYieldSupplyTransactionRepositoryTest { fun `createEnterTransactions returns reactivate-approve-enter transactions`() = runTest { coEvery { walletManager.getYieldContract() } returns yieldContractAddress coEvery { walletManager.calculateYieldContract() } returns yieldContractAddress + coEvery { walletManager.isAllowedToSpend(any()) } returns false coEvery { walletManager.getYieldSupplyStatus(any()) } returns SDKYieldSupplyStatus( isActive = false, isInitialized = true, maxNetworkFee = BigDecimal.TEN, ) - val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus) + val result = repository.createEnterTransactions( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxNetworkFee = BigDecimal.TEN, + ) // Assert that 3 transactions are returned: reactivate token, approve, enter Truth.assertThat(result).isNotNull() @@ -220,7 +234,11 @@ class DefaultYieldSupplyTransactionRepositoryTest { ) coEvery { walletManager.isAllowedToSpend(any()) } returns true - val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus) + val result = repository.createEnterTransactions( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxNetworkFee = BigDecimal.TEN, + ) // Assert that 2 transactions are returned: approve, enter Truth.assertThat(result).isNotNull() @@ -257,7 +275,11 @@ class DefaultYieldSupplyTransactionRepositoryTest { ) coEvery { walletManager.isAllowedToSpend(any()) } returns true - val result = repository.createEnterTransactions(userWalletId, cryptoCurrencyStatus) + val result = repository.createEnterTransactions( + userWalletId = userWalletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxNetworkFee = BigDecimal.TEN, + ) // Assert that transaction is returned: enter Truth.assertThat(result).isNotNull() diff --git a/domain/account/src/main/java/com/tangem/domain/account/producer/SingleAccountListProducer.kt b/domain/account/src/main/java/com/tangem/domain/account/producer/SingleAccountListProducer.kt index 8d85130007..9acea3c434 100644 --- a/domain/account/src/main/java/com/tangem/domain/account/producer/SingleAccountListProducer.kt +++ b/domain/account/src/main/java/com/tangem/domain/account/producer/SingleAccountListProducer.kt @@ -5,7 +5,7 @@ import com.tangem.domain.core.flow.FlowProducer import com.tangem.domain.models.wallet.UserWalletId /** - * Produces a list of [AccountList] for a specific user wallet. + * Produces a [AccountList] for a specific user wallet. * [REDACTED_AUTHOR] */ diff --git a/domain/account/status/.gitignore b/domain/account/status/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/domain/account/status/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/account/status/build.gradle.kts b/domain/account/status/build.gradle.kts new file mode 100644 index 0000000000..104086e62c --- /dev/null +++ b/domain/account/status/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.domain.account.status" +} + +tasks.withType().configureEach { + useJUnitPlatform() +} + +dependencies { + implementation(projects.domain.account) + implementation(projects.domain.core) + implementation(projects.domain.quotes) + implementation(projects.domain.models) + implementation(projects.domain.networks) + implementation(projects.domain.staking) + implementation(projects.domain.tokens) + + implementation(projects.libs.crypto) + + implementation(deps.kotlin.datetime) + + // region DI + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) + // end + + testImplementation(deps.test.coroutine) + testImplementation(deps.test.junit5) + testRuntimeOnly(deps.test.junit5.engine) + testImplementation(deps.test.mockk) + testImplementation(deps.test.truth) + testImplementation(tangemDeps.blockchain) + testImplementation(projects.common.test) +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListProducerFactoryModule.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListProducerFactoryModule.kt new file mode 100644 index 0000000000..926780dc55 --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListProducerFactoryModule.kt @@ -0,0 +1,28 @@ +package com.tangem.domain.account.status.di + +import com.tangem.domain.account.status.producer.DefaultMultiAccountStatusListProducer +import com.tangem.domain.account.status.producer.DefaultSingleAccountStatusListProducer +import com.tangem.domain.account.status.producer.MultiAccountStatusListProducer +import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface AccountStatusListProducerFactoryModule { + + @Binds + @Singleton + fun bindSingleAccountStatusListProducerFactory( + factory: DefaultSingleAccountStatusListProducer.Factory, + ): SingleAccountStatusListProducer.Factory + + @Binds + @Singleton + fun bindMultiAccountStatusListProducerFactory( + factory: DefaultMultiAccountStatusListProducer.Factory, + ): MultiAccountStatusListProducer.Factory +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt new file mode 100644 index 0000000000..51b10dc806 --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt @@ -0,0 +1,38 @@ +package com.tangem.domain.account.status.di + +import com.tangem.domain.account.status.producer.MultiAccountStatusListProducer +import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer +import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier +import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AccountStatusListSupplierModule { + + @Provides + @Singleton + fun provideSingleAccountStatusListSupplier( + factory: SingleAccountStatusListProducer.Factory, + ): SingleAccountStatusListSupplier { + return object : SingleAccountStatusListSupplier( + factory = factory, + keyCreator = { "account_status_list_${it.userWalletId}" }, + ) {} + } + + @Provides + @Singleton + fun provideMultiAccountStatusListSupplier( + factory: MultiAccountStatusListProducer.Factory, + ): MultiAccountStatusListSupplier { + return object : MultiAccountStatusListSupplier( + factory = factory, + keyCreator = { "multi_account_status_list" }, + ) {} + } +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultMultiAccountStatusListProducer.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultMultiAccountStatusListProducer.kt new file mode 100644 index 0000000000..8f4335ee9d --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultMultiAccountStatusListProducer.kt @@ -0,0 +1,48 @@ +package com.tangem.domain.account.status.producer + +import arrow.core.Option +import arrow.core.some +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier +import com.tangem.domain.core.wallets.UserWalletsListRepository +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest + +/** +[REDACTED_AUTHOR] + */ +// TODO: Finalize [REDACTED_JIRA] +internal class DefaultMultiAccountStatusListProducer @AssistedInject constructor( + @Assisted val params: Unit, + private val userWalletsListRepository: UserWalletsListRepository, + private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier, +) : MultiAccountStatusListProducer { + + override val fallback: Option> = emptyList().some() + + @OptIn(ExperimentalCoroutinesApi::class) + override fun produce(): Flow> { + return userWalletsListRepository.userWallets + .filterNotNull() + .flatMapLatest { userWallets -> + val flows = userWallets.map { + singleAccountStatusListSupplier( + params = SingleAccountStatusListProducer.Params(it.walletId), + ) + } + + combine(flows) { it.toList() } + } + } + + @AssistedFactory + interface Factory : MultiAccountStatusListProducer.Factory { + override fun create(params: Unit): DefaultMultiAccountStatusListProducer + } +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt new file mode 100644 index 0000000000..ebb05f18cb --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt @@ -0,0 +1,50 @@ +package com.tangem.domain.account.status.producer + +import arrow.core.Option +import arrow.core.none +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.account.supplier.SingleAccountListSupplier +import com.tangem.domain.models.network.NetworkStatus +import com.tangem.domain.models.quote.QuoteStatus +import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.networks.single.SingleNetworkStatusSupplier +import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier +import com.tangem.domain.staking.StakingIdFactory +import com.tangem.domain.staking.single.SingleYieldBalanceSupplier +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +/** +[REDACTED_AUTHOR] + */ +// TODO: Implement [REDACTED_JIRA] +@Suppress("UnusedPrivateProperty", "UnusedPrivateClass") +@OptIn(ExperimentalCoroutinesApi::class) +internal class DefaultSingleAccountStatusListProducer @AssistedInject constructor( + @Assisted private val params: SingleAccountStatusListProducer.Params, + private val singleAccountListSupplier: SingleAccountListSupplier, + private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, + private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier, + private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, + private val stakingIdFactory: StakingIdFactory, +) : SingleAccountStatusListProducer { + + override val fallback: Option = none() + + override fun produce(): Flow = emptyFlow() + + private data class CryptoCurrencyStatusSources( + val networkStatus: NetworkStatus, + val yieldBalance: YieldBalance?, + val quoteStatus: QuoteStatus?, + ) + + @AssistedFactory + interface Factory : SingleAccountStatusListProducer.Factory { + override fun create(params: SingleAccountStatusListProducer.Params): DefaultSingleAccountStatusListProducer + } +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/MultiAccountStatusListProducer.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/MultiAccountStatusListProducer.kt new file mode 100644 index 0000000000..5ebd910a58 --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/MultiAccountStatusListProducer.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.account.status.producer + +import com.tangem.domain.account.models.AccountList +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.core.flow.FlowProducer + +/** + * Produces a list of [AccountList]s for all user wallets. + * +[REDACTED_AUTHOR] + */ +interface MultiAccountStatusListProducer : FlowProducer> { + + interface Factory : FlowProducer.Factory +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/SingleAccountStatusListProducer.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/SingleAccountStatusListProducer.kt new file mode 100644 index 0000000000..97935a14f3 --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/SingleAccountStatusListProducer.kt @@ -0,0 +1,17 @@ +package com.tangem.domain.account.status.producer + +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.core.flow.FlowProducer +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Produces a [AccountStatusList] for a specific user wallet. + * +[REDACTED_AUTHOR] + */ +interface SingleAccountStatusListProducer : FlowProducer { + + data class Params(val userWalletId: UserWalletId) + + interface Factory : FlowProducer.Factory +} \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/supplier/MultiAccountStatusListSupplier.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/supplier/MultiAccountStatusListSupplier.kt new file mode 100644 index 0000000000..60ed1d5971 --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/supplier/MultiAccountStatusListSupplier.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.account.status.supplier + +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.account.status.producer.MultiAccountStatusListProducer +import com.tangem.domain.core.flow.FlowCachingSupplier + +/** + * Supplier that provides a list of [AccountStatusList]s for all user wallets. + * +[REDACTED_AUTHOR] + */ +abstract class MultiAccountStatusListSupplier( + override val factory: MultiAccountStatusListProducer.Factory, + override val keyCreator: (Unit) -> String, +) : FlowCachingSupplier>() \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/supplier/SingleAccountStatusListSupplier.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/supplier/SingleAccountStatusListSupplier.kt new file mode 100644 index 0000000000..5e5ef8f506 --- /dev/null +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/supplier/SingleAccountStatusListSupplier.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.account.status.supplier + +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer +import com.tangem.domain.core.flow.FlowCachingSupplier + +/** + * Supplier that provides a single [AccountStatusList] for a specific user wallet. + * +[REDACTED_AUTHOR] + */ +abstract class SingleAccountStatusListSupplier( + override val factory: SingleAccountStatusListProducer.Factory, + override val keyCreator: (SingleAccountStatusListProducer.Params) -> String, +) : FlowCachingSupplier() \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/StateDialog.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/StateDialog.kt index 120604ae39..9d5084361d 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/StateDialog.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/StateDialog.kt @@ -7,6 +7,6 @@ interface StateDialog { data class ScanFailsDialog(val source: ScanFailsSource, val onTryAgain: (() -> Unit)? = null) : StateDialog enum class ScanFailsSource { - MAIN, SIGN_IN, SETTINGS, INTRO; + MAIN, SIGN_IN, SETTINGS, INTRO } } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TokensGroupType.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TokensGroupType.kt index 23982e6f41..735b112edf 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/TokensGroupType.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TokensGroupType.kt @@ -15,5 +15,4 @@ enum class TokensGroupType { /** Grouping by network */ NETWORK, - ; } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TokensSortType.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TokensSortType.kt index 0e85408187..3a68f0be35 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/TokensSortType.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TokensSortType.kt @@ -15,5 +15,4 @@ enum class TokensSortType { /** Sorted by their balance */ BALANCE, - ; } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt index 374956a548..88af8c6bbd 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TotalFiatBalance.kt @@ -26,13 +26,12 @@ sealed interface TotalFiatBalance { /** * Represents the successfully loaded state of the fiat balance. * - * @property amount the loaded fiat balance amount - * @property isAllAmountsSummarized indicates whether the amount includes a summary of all underlying amounts + * @property amount the loaded fiat balance amount + * @property source status source */ @Serializable data class Loaded( val amount: SerializedBigDecimal, - val isAllAmountsSummarized: Boolean, val source: StatusSource, ) : TotalFiatBalance } \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt index 15b906020b..681a6d940e 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/account/CryptoPortfolioIcon.kt @@ -44,7 +44,6 @@ data class CryptoPortfolioIcon private constructor( Clock, Package, Gift, - ; } /** @@ -64,7 +63,6 @@ data class CryptoPortfolioIcon private constructor( Pattypan, UFOGreen, VitalGreen, - ; } companion object { diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt index bec343c4df..776c46147f 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/tokenlist/TokenList.kt @@ -70,7 +70,6 @@ sealed interface TokenList { override val totalFiatBalance: TotalFiatBalance = TotalFiatBalance.Loaded( amount = SerializedBigDecimal.ZERO, - isAllAmountsSummarized = true, source = StatusSource.ACTUAL, ) diff --git a/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt b/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt index e3a191f205..83fae860be 100644 --- a/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt +++ b/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt @@ -114,39 +114,40 @@ class GetApplicationIdUseCaseTest { } @Test - fun `GIVEN no local application ID WHEN multiple concurrent invokes with delay THEN create only one application ID`() = runTest { - // GIVEN - val newApplicationId = ApplicationId("new-app-id") - var isIdCreated = false + fun `GIVEN no local application ID WHEN multiple concurrent invokes with delay THEN create only one application ID`() = + runTest { + // GIVEN + val newApplicationId = ApplicationId("new-app-id") + var isIdCreated = false - coEvery { pushNotificationsRepository.getApplicationId() } answers { - if (!isIdCreated) null else newApplicationId - } - coEvery { pushNotificationsRepository.createApplicationId() } answers { - isIdCreated = true - newApplicationId - } - coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit + coEvery { pushNotificationsRepository.getApplicationId() } answers { + if (!isIdCreated) null else newApplicationId + } + coEvery { pushNotificationsRepository.createApplicationId() } answers { + isIdCreated = true + newApplicationId + } + coEvery { pushNotificationsRepository.saveApplicationId(newApplicationId) } returns Unit - // WHEN - val results = coroutineScope { - List(PARALLEL_COUNT) { - async { - delay(100) - useCase() - } - }.awaitAll() - } + // WHEN + val results = coroutineScope { + List(PARALLEL_COUNT) { + async { + delay(100) + useCase() + } + }.awaitAll() + } - // THEN - results.forEach { result -> - assertThat(result).isInstanceOf(Either.Right::class.java) - assertThat((result as Either.Right).value).isEqualTo(newApplicationId) + // THEN + results.forEach { result -> + assertThat(result).isInstanceOf(Either.Right::class.java) + assertThat((result as Either.Right).value).isEqualTo(newApplicationId) + } + coVerify(exactly = PARALLEL_COUNT + 1) { pushNotificationsRepository.getApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.createApplicationId() } + coVerify(exactly = 1) { pushNotificationsRepository.saveApplicationId(newApplicationId) } } - coVerify(exactly = PARALLEL_COUNT + 1) { pushNotificationsRepository.getApplicationId() } - coVerify(exactly = 1) { pushNotificationsRepository.createApplicationId() } - coVerify(exactly = 1) { pushNotificationsRepository.saveApplicationId(newApplicationId) } - } companion object { private const val PARALLEL_COUNT = 100 diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt index 0fc94390be..18401f9621 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetWalletTotalBalanceUseCase.kt @@ -13,7 +13,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations -import com.tangem.domain.tokens.operations.TokenListFiatBalanceOperations +import com.tangem.domain.tokens.operations.TotalFiatBalanceCalculator import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import timber.log.Timber @@ -112,11 +112,8 @@ class GetWalletTotalBalanceUseCase( } } - val operations = TokenListFiatBalanceOperations( - currencies = ensureNotNull(statuses.toNonEmptyListOrNull()) { lceLoading() }, - isAnyTokenLoading = false, + TotalFiatBalanceCalculator.calculate( + statuses = ensureNotNull(statuses.toNonEmptyListOrNull()) { lceLoading() }, ) - - operations.calculateFiatBalance() } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt deleted file mode 100644 index 198890fd63..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.tangem.domain.tokens.operations - -import arrow.core.NonEmptyList -import com.tangem.domain.models.StatusSource -import com.tangem.domain.models.TotalFiatBalance -import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.domain.models.getResultStatusSource -import com.tangem.domain.models.staking.YieldBalance -import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance -import com.tangem.lib.crypto.BlockchainUtils -import com.tangem.utils.extensions.orZero -import java.math.BigDecimal - -internal class TokenListFiatBalanceOperations( - private val currencies: NonEmptyList, - private val isAnyTokenLoading: Boolean, -) { - - @Suppress("LoopWithTooManyJumpStatements") - fun calculateFiatBalance(): TotalFiatBalance { - var fiatBalance: TotalFiatBalance = TotalFiatBalance.Loading - if (isAnyTokenLoading) return fiatBalance - - for (token in currencies) { - val blockchainId = token.currency.network.rawId - when (val status = token.value) { - is CryptoCurrencyStatus.Loading -> { - fiatBalance = TotalFiatBalance.Loading - break - } - is CryptoCurrencyStatus.NoQuote, - is CryptoCurrencyStatus.MissedDerivation, - -> { - fiatBalance = TotalFiatBalance.Failed - break - } - is CryptoCurrencyStatus.Unreachable, - is CryptoCurrencyStatus.NoAmount, - -> { - if (BlockchainUtils.isIncludeToBalanceOnError(blockchainId)) { - fiatBalance = recalculateNoAccountBalance(status, fiatBalance) - } else { - fiatBalance = TotalFiatBalance.Failed - break - } - } - is CryptoCurrencyStatus.NoAccount -> { - fiatBalance = recalculateNoAccountBalance(status, fiatBalance) - } - is CryptoCurrencyStatus.Loaded -> { - fiatBalance = recalculateBalance(status, fiatBalance, blockchainId) - } - is CryptoCurrencyStatus.Custom -> { - fiatBalance = recalculateBalance(status, fiatBalance, blockchainId) - } - } - } - - return (fiatBalance as? TotalFiatBalance.Loaded)?.copy( - source = currencies.map { it.value.sources.total }.getResultStatusSource(), - ) ?: fiatBalance - } - - private fun recalculateNoAccountBalance( - status: CryptoCurrencyStatus.Value, - currentBalance: TotalFiatBalance, - ): TotalFiatBalance { - return (currentBalance as? TotalFiatBalance.Loaded)?.copy(isAllAmountsSummarized = false) - ?: TotalFiatBalance.Loaded( - amount = BigDecimal.ZERO, - isAllAmountsSummarized = false, - source = (status as? CryptoCurrencyStatus.NoAccount)?.sources?.total ?: StatusSource.ACTUAL, - ) - } - - private fun recalculateBalance( - status: CryptoCurrencyStatus.Loaded, - currentBalance: TotalFiatBalance, - blockchainId: String, - ): TotalFiatBalance { - return with(currentBalance) { - val yieldBalance = status.yieldBalance as? YieldBalance.Data - val stakingBalance = yieldBalance?.getTotalWithRewardsStakingBalance(blockchainId).orZero() - val fiatStakingBalance = status.fiatRate.times(stakingBalance) - (this as? TotalFiatBalance.Loaded)?.copy( - amount = this.amount + status.fiatAmount + fiatStakingBalance, - ) ?: TotalFiatBalance.Loaded( - amount = status.fiatAmount + fiatStakingBalance, - isAllAmountsSummarized = true, - source = status.sources.total, - ) - } - } - - private fun recalculateBalance( - status: CryptoCurrencyStatus.Custom, - currentBalance: TotalFiatBalance, - blockchainId: String, - ): TotalFiatBalance { - return with(currentBalance) { - val isTokenAmountCanBeSummarized = status.fiatAmount != null - val yieldBalance = (status.yieldBalance as? YieldBalance.Data) - ?.getTotalWithRewardsStakingBalance(blockchainId).orZero() - val fiatYieldBalance = status.fiatRate?.times(yieldBalance).orZero() - (this as? TotalFiatBalance.Loaded)?.copy( - amount = this.amount + status.fiatAmount.orZero() + fiatYieldBalance, - isAllAmountsSummarized = isTokenAmountCanBeSummarized, - ) ?: TotalFiatBalance.Loaded( - amount = status.fiatAmount.orZero() + fiatYieldBalance, - isAllAmountsSummarized = isTokenAmountCanBeSummarized, - source = StatusSource.ACTUAL, - ) - } - } -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index 01eac8c1bb..4ff3806cbc 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -33,13 +33,9 @@ internal class TokenListOperations( private fun Raise.createTokenList(isGrouped: Boolean, isSortedByBalance: Boolean): TokenList { val nonEmptyCurrencies = tokens.toNonEmptyListOrNull() ?: return TokenList.Empty - val isAnyTokenLoading = nonEmptyCurrencies.any { it.value is CryptoCurrencyStatus.Loading } - val fiatBalanceOperations = TokenListFiatBalanceOperations(nonEmptyCurrencies, isAnyTokenLoading) - return createTokenList( currencies = nonEmptyCurrencies, - fiatBalance = fiatBalanceOperations.calculateFiatBalance(), - isAnyTokenLoading = isAnyTokenLoading, + fiatBalance = TotalFiatBalanceCalculator.calculate(statuses = nonEmptyCurrencies), isGrouped = isGrouped, isSortedByBalance = isSortedByBalance, ) @@ -48,13 +44,12 @@ internal class TokenListOperations( private fun Raise.createTokenList( currencies: NonEmptyList, fiatBalance: TotalFiatBalance, - isAnyTokenLoading: Boolean, isGrouped: Boolean, isSortedByBalance: Boolean, ): TokenList { val sortingOperations = TokenListSortingOperations( currencies = currencies, - isAnyTokenLoading = isAnyTokenLoading, + isAnyTokenLoading = currencies.any { it.value is CryptoCurrencyStatus.Loading }, sortByBalance = isSortedByBalance, ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TotalFiatBalanceCalculator.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TotalFiatBalanceCalculator.kt new file mode 100644 index 0000000000..bef4a4e3dc --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TotalFiatBalanceCalculator.kt @@ -0,0 +1,194 @@ +package com.tangem.domain.tokens.operations + +import arrow.core.NonEmptyList +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.getResultStatusSource +import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance +import com.tangem.lib.crypto.BlockchainUtils +import com.tangem.utils.extensions.orZero +import java.math.BigDecimal + +/** + * Utility to calculate total fiat balance from a set of [CryptoCurrencyStatus]. + * + * The calculation considers various states of each cryptocurrency, including loading, + * unreachable, no amount, no account, loaded, and custom states. + * + * The result is a [TotalFiatBalance], which can be in one of the following states: + * - [TotalFiatBalance.Loading]: If any cryptocurrency is still loading. + * - [TotalFiatBalance.Failed]: If any cryptocurrency is in a non-computable state (e.g., no quote). + * - [TotalFiatBalance.Loaded]: If all cryptocurrencies are computable, containing the total fiat amount, + * a flag indicating if all amounts were summarized, and the source of the data. + * + * The calculation also takes into account staking balances when available. + */ +object TotalFiatBalanceCalculator { + + fun calculate(statuses: NonEmptyList): TotalFiatBalance { + val computationState = ComputationState.resolve(statuses) + + return when (computationState) { + ComputationState.LOADING -> TotalFiatBalance.Loading + ComputationState.NON_COMPUTABLE -> TotalFiatBalance.Failed + ComputationState.COMPUTABLE -> compute(statuses) + } + } + + private fun compute(statuses: NonEmptyList): TotalFiatBalance { + var mutableBalance: TotalFiatBalance = TotalFiatBalance.Loading + + for (token in statuses) { + val blockchainId = token.currency.network.rawId + + when (val status = token.value) { + is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, + is CryptoCurrencyStatus.NoAccount, + -> { + mutableBalance = mutableBalance.plusEmptyBalance(status) + } + is CryptoCurrencyStatus.Loaded -> { + mutableBalance = mutableBalance.plusLoaded(status, blockchainId) + } + is CryptoCurrencyStatus.Custom -> { + mutableBalance = mutableBalance.plusLoaded(status, blockchainId) + } + // Non computable states, should be handled before + CryptoCurrencyStatus.Loading, + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.NoQuote, + -> continue + } + } + + return mutableBalance.updateSource(statuses) + } + + private fun TotalFiatBalance.plusEmptyBalance(status: CryptoCurrencyStatus.Value): TotalFiatBalance { + return fold( + ifLoaded = { it }, + ifNot = { + TotalFiatBalance.Loaded( + amount = BigDecimal.ZERO, + source = status.sources.total, // never mind + ) + }, + ) + } + + private fun TotalFiatBalance.plusLoaded( + status: CryptoCurrencyStatus.Loaded, + blockchainId: String, + ): TotalFiatBalance { + val fiatStakingBalance = status.getFiatStakingBalance(blockchainId) + + return fold( + ifLoaded = { loaded -> + loaded.copy(amount = loaded.amount + status.fiatAmount + fiatStakingBalance) + }, + ifNot = { + TotalFiatBalance.Loaded( + amount = status.fiatAmount + fiatStakingBalance, + source = status.sources.total, // never mind + ) + }, + ) + } + + private fun TotalFiatBalance.plusLoaded( + status: CryptoCurrencyStatus.Custom, + blockchainId: String, + ): TotalFiatBalance { + val fiatStakingBalance = status.getFiatStakingBalance(blockchainId) + + return fold( + ifLoaded = { loaded -> + loaded.copy( + amount = loaded.amount + status.fiatAmount.orZero() + fiatStakingBalance, + ) + }, + ifNot = { + TotalFiatBalance.Loaded( + amount = status.fiatAmount.orZero() + fiatStakingBalance, + source = StatusSource.ACTUAL, + ) + }, + ) + } + + private fun TotalFiatBalance.updateSource(statuses: NonEmptyList): TotalFiatBalance { + return fold( + ifLoaded = { loaded -> + loaded.copy( + source = statuses.map { it.value.sources.total }.getResultStatusSource(), + ) + }, + ifNot = { this }, + ) + } + + private fun CryptoCurrencyStatus.Loaded.getFiatStakingBalance(blockchainId: String): BigDecimal { + val yieldBalance = yieldBalance as? YieldBalance.Data + val stakingBalance = yieldBalance?.getTotalWithRewardsStakingBalance(blockchainId).orZero() + + return fiatRate.times(stakingBalance) + } + + private fun CryptoCurrencyStatus.Custom.getFiatStakingBalance(blockchainId: String): BigDecimal { + val yieldBalance = yieldBalance as? YieldBalance.Data + val stakingBalance = yieldBalance?.getTotalWithRewardsStakingBalance(blockchainId).orZero() + + return fiatRate?.times(stakingBalance).orZero() + } + + private inline fun TotalFiatBalance.fold( + ifLoaded: (TotalFiatBalance.Loaded) -> TotalFiatBalance, + ifNot: () -> TotalFiatBalance, + ): TotalFiatBalance { + return if (this is TotalFiatBalance.Loaded) { + ifLoaded(this) + } else { + ifNot() + } + } + + private enum class ComputationState { + + LOADING, NON_COMPUTABLE, COMPUTABLE; + + companion object { + + fun resolve(statuses: NonEmptyList): ComputationState { + for (status in statuses) { + when (status.value) { + CryptoCurrencyStatus.Loading -> { + return LOADING + } + is CryptoCurrencyStatus.NoQuote, + is CryptoCurrencyStatus.MissedDerivation, + -> { + return NON_COMPUTABLE + } + is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, + -> { + val blockchainId = status.currency.network.rawId + if (!BlockchainUtils.isIncludeToBalanceOnError(blockchainId)) { + return NON_COMPUTABLE + } + } + is CryptoCurrencyStatus.Loaded, + is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.NoAccount, + -> continue + } + } + + return COMPUTABLE + } + } + } +} \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt index caa2c13c6f..af553ea246 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt @@ -80,7 +80,6 @@ internal object MockTokenLists { sortedBy = TokensSortType.NONE, totalFiatBalance = TotalFiatBalance.Loaded( amount = tokens.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }, - isAllAmountsSummarized = true, source = StatusSource.ACTUAL, ), ) @@ -97,7 +96,6 @@ internal object MockTokenLists { amount = groups .flatMap { it.currencies as NonEmptyList } .sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }, - isAllAmountsSummarized = true, source = StatusSource.ACTUAL, ), ) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/operations/TotalFiatBalanceCalculatorTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/operations/TotalFiatBalanceCalculatorTest.kt new file mode 100644 index 0000000000..12dba0e8bd --- /dev/null +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/operations/TotalFiatBalanceCalculatorTest.kt @@ -0,0 +1,506 @@ +package com.tangem.domain.tokens.operations + +import arrow.core.nonEmptyListOf +import com.google.common.truth.Truth +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.TotalFiatBalance +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.network.NetworkAddress +import com.tangem.domain.models.staking.BalanceItem +import com.tangem.domain.models.staking.BalanceType +import com.tangem.domain.models.staking.YieldBalance +import com.tangem.domain.models.staking.YieldBalanceItem +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class TotalFiatBalanceCalculatorTest { + + private val cryptoCurrencyFactory = MockCryptoCurrencyFactory() + private val binance = cryptoCurrencyFactory.createCoin(Blockchain.Binance) + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class LoadingOrNonComputable { + + @Test + fun `one token is Loading, total is Loading`() { + // Arrange + val statuses = nonEmptyListOf( + createLoading(currency = cryptoCurrencyFactory.ethereum), + createNoQuote(currency = cryptoCurrencyFactory.stellar), + createMissedDerivation(currency = cryptoCurrencyFactory.chia), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loading + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `one token is NoQuote, total is Failed`() { + // Arrange + val statuses = nonEmptyListOf( + createNoQuote(currency = cryptoCurrencyFactory.stellar), + createUnreachable(currency = cryptoCurrencyFactory.chia), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Failed + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `one token is MissedDerivation, total is Failed`() { + // Arrange + val statuses = nonEmptyListOf( + createNoQuote(currency = cryptoCurrencyFactory.stellar), + createUnreachable(currency = cryptoCurrencyFactory.chia), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Failed + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `one token is Unreachable and isIncludeToBalanceOnError is FALSE, total is Failed`() { + // Arrange + val statuses = nonEmptyListOf( + createUnreachable(currency = cryptoCurrencyFactory.stellar), + createNoAccount(currency = cryptoCurrencyFactory.chia), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Failed + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `one token is NoAmount and isIncludeToBalanceOnError is FALSE, total is Failed`() { + // Arrange + val statuses = nonEmptyListOf( + createNoAmount(currency = cryptoCurrencyFactory.stellar), + createNoAccount(currency = cryptoCurrencyFactory.chia), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Failed + Truth.assertThat(actual).isEqualTo(expected) + } + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class Computable { + + @Test + fun `Unreachable token isIncludeToBalanceOnError, total is Loaded`() { + // Arrange + val statuses = nonEmptyListOf(createUnreachable(currency = binance)) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loaded( + amount = BigDecimal.ZERO, + source = StatusSource.ACTUAL, + ) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `NoAmount token isIncludeToBalanceOnError, total is Loaded`() { + // Arrange + val statuses = nonEmptyListOf(createNoAmount(currency = binance)) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loaded( + amount = BigDecimal.ZERO, + source = StatusSource.ACTUAL, + ) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `all tokens are NoAccount, total is Loaded`() { + // Arrange + val statuses = nonEmptyListOf( + createNoAccount(currency = cryptoCurrencyFactory.ethereum), + createNoAccount(currency = cryptoCurrencyFactory.stellar), + createNoAccount(currency = cryptoCurrencyFactory.chia), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loaded( + amount = BigDecimal.ZERO, + source = StatusSource.ACTUAL, + ) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `all tokens are Custom, total is Loaded`() { + // Arrange + val statuses = nonEmptyListOf( + /** + * Balance: 10 + * - fiat: 10 + * - staking: 0 + */ + createCustom( + currency = cryptoCurrencyFactory.ethereum, + fiatAmount = BigDecimal.TEN, + ), + /** + * Balance: 20 + * - fiat: 0 + * - staking: 20 (REWARDS) + */ + createCustom( + currency = cryptoCurrencyFactory.cardano, + fiatAmount = BigDecimal.ZERO, + yieldBalance = createYieldBalance( + amount = BigDecimal(20), + // It is important to use `BalanceType.REWARDS` because Cardano should not include the full + // staking balance. See `getTotalWithRewardsStakingBalance`. + balanceType = BalanceType.REWARDS, + ), + ), + /** + * Balance: 10 + * - fiat: 1 + * - staking: 9 (STAKED) + */ + createCustom( + currency = cryptoCurrencyFactory.stellar, + fiatAmount = BigDecimal.ONE, + yieldBalance = createYieldBalance( + amount = BigDecimal(9), + balanceType = BalanceType.STAKED, + ), + ), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loaded( + amount = BigDecimal(40), + source = StatusSource.ACTUAL, + ) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `all tokens are Loaded, total is Loaded`() { + // Arrange + val statuses = nonEmptyListOf( + /** + * Balance: 10 + * - fiat: 10 + * - staking: 0 + */ + createLoaded( + currency = cryptoCurrencyFactory.ethereum, + fiatAmount = BigDecimal.TEN, + ), + /** + * Balance: 20 + * - fiat: 0 + * - staking: 20 (REWARDS) + */ + createLoaded( + currency = cryptoCurrencyFactory.cardano, + fiatAmount = BigDecimal.ZERO, + yieldBalance = createYieldBalance( + amount = BigDecimal(20), + // It is important to use `BalanceType.REWARDS` because Cardano should not include the full + // staking balance. See `getTotalWithRewardsStakingBalance`. + balanceType = BalanceType.REWARDS, + ), + ), + /** + * Balance: 10 + * - fiat: 1 + * - staking: 9 (STAKED) + */ + createLoaded( + currency = cryptoCurrencyFactory.stellar, + fiatAmount = BigDecimal.ONE, + yieldBalance = createYieldBalance( + amount = BigDecimal(9), + balanceType = BalanceType.STAKED, + ), + ), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loaded( + amount = BigDecimal(40), + source = StatusSource.ACTUAL, + ) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `tokens contain all types of computable statuses, total is Loaded`() { + // Arrange + val statuses = nonEmptyListOf( + createNoAccount(currency = cryptoCurrencyFactory.ethereum), // 0 + createNoAmount(currency = binance), // 0 + createUnreachable(currency = binance), // 0 + createCustom( + // 1 + currency = cryptoCurrencyFactory.cardano, + fiatAmount = BigDecimal.ONE, + ), + createLoaded( + // 10 + currency = cryptoCurrencyFactory.stellar, + fiatAmount = BigDecimal.TEN, + ), + ) + + // Act + val actual = TotalFiatBalanceCalculator.calculate(statuses) + + // Assert + val expected = TotalFiatBalance.Loaded( + amount = BigDecimal(11), + source = StatusSource.ACTUAL, + ) + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `StatusSource is Actual is all tokens are Actual`() { + // Arrange + val statuses = nonEmptyListOf( + createNoAccount(currency = cryptoCurrencyFactory.ethereum), + createNoAmount(currency = binance), + createUnreachable(currency = binance), + createCustom( + currency = cryptoCurrencyFactory.cardano, + fiatAmount = BigDecimal.ONE, + ), + createLoaded( + currency = cryptoCurrencyFactory.stellar, + fiatAmount = BigDecimal.TEN, + ), + ) + + // Act + val actual = (TotalFiatBalanceCalculator.calculate(statuses) as TotalFiatBalance.Loaded).source + + // Assert + val expected = StatusSource.ACTUAL + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `StatusSource is Cache is any token is Cache`() { + // Arrange + val statuses = nonEmptyListOf( + createNoAccount(currency = cryptoCurrencyFactory.ethereum), + createNoAmount(currency = binance), + createLoaded( + currency = cryptoCurrencyFactory.cardano, + fiatAmount = BigDecimal.ONE, + source = StatusSource.CACHE, + ), + ) + + // Act + val actual = (TotalFiatBalanceCalculator.calculate(statuses) as TotalFiatBalance.Loaded).source + + // Assert + val expected = StatusSource.CACHE + Truth.assertThat(actual).isEqualTo(expected) + } + + @Test + fun `StatusSource is ONLY_CACHE is any token is ONLY_CACHE`() { + // Arrange + val statuses = nonEmptyListOf( + createNoAccount(currency = cryptoCurrencyFactory.ethereum), + createNoAmount(currency = binance), + createLoaded( + currency = cryptoCurrencyFactory.cardano, + fiatAmount = BigDecimal.ONE, + source = StatusSource.ONLY_CACHE, + ), + ) + + // Act + val actual = (TotalFiatBalanceCalculator.calculate(statuses) as TotalFiatBalance.Loaded).source + + // Assert + val expected = StatusSource.ONLY_CACHE + Truth.assertThat(actual).isEqualTo(expected) + } + } + + private fun createLoading(currency: CryptoCurrency): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.Loading, + ) + } + + private fun createNoQuote(currency: CryptoCurrency): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.NoQuote( + amount = BigDecimal.ONE, + yieldBalance = null, + yieldSupplyStatus = null, + hasCurrentNetworkTransactions = false, + pendingTransactions = emptySet(), + networkAddress = createNetworkAddress(), + sources = CryptoCurrencyStatus.Sources(), + ), + ) + } + + private fun createMissedDerivation(currency: CryptoCurrency): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.MissedDerivation(priceChange = null, fiatRate = null), + ) + } + + private fun createUnreachable(currency: CryptoCurrency): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.Unreachable(priceChange = null, fiatRate = null, networkAddress = null), + ) + } + + private fun createNoAmount(currency: CryptoCurrency): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.NoAmount(priceChange = null, fiatRate = null), + ) + } + + private fun createNoAccount(currency: CryptoCurrency): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.NoAccount( + priceChange = null, + amountToCreateAccount = BigDecimal.ONE, + fiatAmount = null, + fiatRate = null, + networkAddress = createNetworkAddress(), + sources = CryptoCurrencyStatus.Sources(), + ), + ) + } + + private fun createCustom( + currency: CryptoCurrency, + fiatAmount: BigDecimal?, + yieldBalance: YieldBalance? = null, + ): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.Custom( + amount = BigDecimal.ONE, + fiatAmount = fiatAmount, + fiatRate = BigDecimal.ONE, + priceChange = BigDecimal.ZERO, + yieldBalance = yieldBalance, + yieldSupplyStatus = null, + hasCurrentNetworkTransactions = false, + pendingTransactions = emptySet(), + networkAddress = createNetworkAddress(), + sources = CryptoCurrencyStatus.Sources(), + ), + ) + } + + private fun createLoaded( + currency: CryptoCurrency, + fiatAmount: BigDecimal, + yieldBalance: YieldBalance? = null, + source: StatusSource = StatusSource.ACTUAL, + ): CryptoCurrencyStatus { + return CryptoCurrencyStatus( + currency = currency, + value = CryptoCurrencyStatus.Loaded( + amount = BigDecimal.ONE, + fiatAmount = fiatAmount, + fiatRate = BigDecimal.ONE, + priceChange = BigDecimal.ZERO, + yieldBalance = yieldBalance, + yieldSupplyStatus = null, + hasCurrentNetworkTransactions = false, + pendingTransactions = emptySet(), + networkAddress = createNetworkAddress(), + sources = CryptoCurrencyStatus.Sources(source, source, source), + ), + ) + } + + private fun createYieldBalance(amount: BigDecimal, balanceType: BalanceType): YieldBalance.Data { + return YieldBalance.Data( + stakingId = mockk(), + source = StatusSource.ACTUAL, + balance = YieldBalanceItem( + items = listOf( + mockk { + every { this@mockk.amount } returns amount + every { this@mockk.type } returns balanceType + }, + ), + integrationId = "", + ), + ) + } + + private fun createNetworkAddress(): NetworkAddress.Single { + return NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "0x1", + type = NetworkAddress.Address.Type.Primary, + ), + ) + } +} \ No newline at end of file diff --git a/domain/visa/build.gradle.kts b/domain/visa/build.gradle.kts index 921d77a54c..e3bc73d03b 100644 --- a/domain/visa/build.gradle.kts +++ b/domain/visa/build.gradle.kts @@ -10,17 +10,18 @@ android { } dependencies { - /** Domain models */ - api(projects.domain.visa.models) - - /** Project - Domain */ + /** Project - Core */ + api(projects.core.pagination) implementation(projects.core.utils) implementation(projects.core.error) + + /** Project - Domain */ api(projects.domain.models) - implementation(projects.domain.core) - implementation(projects.domain.wallets.models) - implementation(projects.domain.tokens.models) + api(projects.domain.visa.models) implementation(projects.domain.appCurrency.models) + implementation(projects.domain.core) + implementation(projects.domain.tokens.models) + implementation(projects.domain.wallets.models) /** Security */ implementation(deps.spongecastle.core) diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/TangemPayTxHistoryItem.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/TangemPayTxHistoryItem.kt new file mode 100644 index 0000000000..2e97128461 --- /dev/null +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/TangemPayTxHistoryItem.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.visa.model + +import org.joda.time.DateTime +import java.math.BigDecimal + +data class TangemPayTxHistoryItem( + val id: String, + val date: DateTime?, + val amount: BigDecimal?, + val merchantName: String?, + val status: String?, + val currency: String?, +) { + val timeStampInMillis: Long = date?.millis ?: 0 +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt similarity index 87% rename from domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt rename to domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt index 6d3bcc876a..154766e32c 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt @@ -12,4 +12,6 @@ data class VisaTxHistoryItem( val merchantName: String?, val status: String, val fiatCurrency: Currency, -) \ No newline at end of file +) { + val timeStampInMillis: Long = date.millis +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/model/TangemPayTxHistoryListConfig.kt b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/model/TangemPayTxHistoryListConfig.kt new file mode 100644 index 0000000000..e62a78f59a --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/model/TangemPayTxHistoryListConfig.kt @@ -0,0 +1,5 @@ +package com.tangem.domain.tangempay.model + +import com.tangem.domain.models.wallet.UserWalletId + +data class TangemPayTxHistoryListConfig(val userWalletId: UserWalletId, val refresh: Boolean) \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/model/TangemPayTxHistoryTypeAliases.kt b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/model/TangemPayTxHistoryTypeAliases.kt new file mode 100644 index 0000000000..5f3c7c4d50 --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/model/TangemPayTxHistoryTypeAliases.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.tangempay.model + +import com.tangem.domain.visa.model.TangemPayTxHistoryItem +import com.tangem.pagination.BatchFlow +import com.tangem.pagination.BatchingContext + +typealias TangemPayTxHistoryListBatchingContext = BatchingContext + +typealias TangemPayTxHistoryListBatchFlow = BatchFlow, Nothing> \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/repository/TangemPayTxHistoryRepository.kt b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/repository/TangemPayTxHistoryRepository.kt new file mode 100644 index 0000000000..7915f1b83d --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/tangempay/repository/TangemPayTxHistoryRepository.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.tangempay.repository + +import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchFlow +import com.tangem.domain.tangempay.model.TangemPayTxHistoryListBatchingContext + +interface TangemPayTxHistoryRepository { + fun getTxHistoryBatchFlow( + batchSize: Int, + context: TangemPayTxHistoryListBatchingContext, + ): TangemPayTxHistoryListBatchFlow +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt index 68491232aa..cccba8a293 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManager.kt @@ -102,21 +102,21 @@ interface UserWalletsListManager { /** * Indicates that all [UserWallet]s is locked * - * @see [isLockedSync] + * @see [isLocked] * @see [UserWallet.isLocked] */ - val isLocked: Flow + val lockedState: Flow /** * Indicates that all [UserWallet]s is locked. Sync version. * - * @see [isLocked] + * @see [lockedState] * @see [UserWallet.isLocked] */ - val isLockedSync: Boolean + val isLocked: Boolean /** - * Receive saved [UserWallet]s, populate [userWallets] flow with it and set [isLocked] as false. + * Receive saved [UserWallet]s, populate [userWallets] flow with it and set [lockedState] as false. * * @param type Defines the behavior of the operation. * @@ -125,7 +125,7 @@ interface UserWalletsListManager { */ suspend fun unlock(type: UnlockType): CompletionResult - /** Remove [UserWallet]s from [userWallets] and set [isLocked] as true */ + /** Remove [UserWallet]s from [userWallets] and set [lockedState] as true */ fun lock() /** diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt index 8436bea168..cb47f21996 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt @@ -12,20 +12,20 @@ import kotlinx.coroutines.flow.flowOf * @return If [UserWalletsListManager] not implements [UserWalletsListManager.Lockable] returns [Flow] which * produces only one false value * - * @see UserWalletsListManager.Lockable.isLockedSync + * @see UserWalletsListManager.Lockable.isLocked * */ val UserWalletsListManager.isLocked: Flow - get() = asLockable()?.isLocked ?: flowOf(false) + get() = asLockable()?.lockedState ?: flowOf(false) /** * Indicates that the [UserWalletsListManager] is locked * * @return If [UserWalletsListManager] not implements [UserWalletsListManager.Lockable] returns false * - * @see UserWalletsListManager.Lockable.isLockedSync + * @see UserWalletsListManager.Lockable.isLocked * */ val UserWalletsListManager.isLockedSync: Boolean - get() = asLockable()?.isLockedSync == true + get() = asLockable()?.isLocked == true /** * Call [UserWalletsListManager.Lockable.unlock] if [UserWalletsListManager] implements [UserWalletsListManager.Lockable] diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/YieldSupplyTransactionRepository.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/YieldSupplyTransactionRepository.kt index 34a17c1905..17b679579a 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/YieldSupplyTransactionRepository.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/YieldSupplyTransactionRepository.kt @@ -6,12 +6,14 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.yield.supply.YieldSupplyStatus +import java.math.BigDecimal interface YieldSupplyTransactionRepository { suspend fun createEnterTransactions( userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, + maxNetworkFee: BigDecimal, ): List suspend fun createExitTransaction( diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyStartEarningUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyStartEarningUseCase.kt index b5e73e680f..a9ee4d9c8f 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyStartEarningUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyStartEarningUseCase.kt @@ -5,6 +5,7 @@ import com.tangem.blockchain.common.TransactionData import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.yield.supply.YieldSupplyTransactionRepository +import java.math.BigDecimal class YieldSupplyStartEarningUseCase( private val yieldSupplyTransactionRepository: YieldSupplyTransactionRepository, @@ -13,10 +14,12 @@ class YieldSupplyStartEarningUseCase( suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, + maxNetworkFee: BigDecimal, ): Either> = Either.catch { yieldSupplyTransactionRepository.createEnterTransactions( userWalletId = userWalletId, cryptoCurrencyStatus = cryptoCurrencyStatus, + maxNetworkFee = maxNetworkFee, ) } } \ No newline at end of file diff --git a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt index a38b1d1f15..1cff840e6d 100644 --- a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt +++ b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt @@ -2,9 +2,14 @@ package com.tangem.domain.yield.supply import arrow.core.Either import com.google.common.truth.Truth +import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.common.TransactionExtras +import com.tangem.blockchain.common.smartcontract.SmartContractCallDataProviderFactory import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.blockchain.yieldsupply.YieldSupplyContractCallDataProviderFactory import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.transaction.FeeRepository @@ -50,123 +55,187 @@ class YieldSupplyEstimateEnterFeeUseCaseTest { amount = BigDecimal.ONE.convertToSdkAmount(cryptoCurrency), ) - private fun uncompiled(fee: Fee) = TransactionData.Uncompiled( + private fun uncompiled(fee: Fee, extras: TransactionExtras) = TransactionData.Uncompiled( fee = fee, amount = BigDecimal.ONE.convertToSdkAmount(cryptoCurrency), contractAddress = null, sourceAddress = "0x1234567890123456789012345678901234567890", destinationAddress = "0x1234567890123456789012345678901234567890", + extras = extras, ) @Test - fun `test 1 transaction uses constant gas limit Legacy`() = runTest { + fun `test enter transaction uses constant gas limit Legacy`() = runTest { val fee = TransactionFee.Single(ethLegacyFee()) - val tx = uncompiled(ethLegacyFee()) coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee - val result = useCase(userWallet, cryptoCurrency, listOf(tx)) + val result = useCase( + userWallet = userWallet, + cryptoCurrency = cryptoCurrency, + transactionDataList = listOf( + getDeployTx(), + getEnterTx(), + ), + ) Truth.assertThat(result.isRight()).isTrue() val txs = (result as Either.Right).value - Truth.assertThat(txs.size).isEqualTo(1) - val lastFee = txs.last().fee as Fee.Ethereum.Legacy - Truth.assertThat(lastFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) + val deployFee = txs.first().fee as Fee.Ethereum.Legacy + val enterFee = txs.last().fee as Fee.Ethereum.Legacy + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) + Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) } @Test - fun `test 2 transactions, only last uses constant gas limit Legacy`() = runTest { + fun `test not enter transaction uses gas limit Legacy`() = runTest { val fee = TransactionFee.Single(ethLegacyFee()) - val tx = uncompiled(ethLegacyFee()) coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee) - val result = useCase(userWallet, cryptoCurrency, listOf(tx, tx)) + val result = useCase( + userWallet = userWallet, + cryptoCurrency = cryptoCurrency, + transactionDataList = listOf( + getDeployTx(), + getApproveTx(), + ), + ) Truth.assertThat(result.isRight()).isTrue() val txs = (result as Either.Right).value - Truth.assertThat(txs.size).isEqualTo(2) - val firstFee = txs.first().fee as Fee.Ethereum.Legacy - val lastFee = txs.last().fee as Fee.Ethereum.Legacy - Truth.assertThat(firstFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) - Truth.assertThat(lastFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) + val deployFee = txs.first().fee as Fee.Ethereum.Legacy + val approveFee = txs.last().fee as Fee.Ethereum.Legacy + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) + Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) } @Test - fun `test 3 transactions, only last uses constant gas limit Legacy`() = runTest { + fun `test enter transaction with wrong order uses gas limit Legacy`() = runTest { val fee = TransactionFee.Single(ethLegacyFee()) - val tx = uncompiled(ethLegacyFee()) - coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee, fee) - val result = useCase(userWallet, cryptoCurrency, listOf(tx, tx, tx)) + coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee) + + val result = useCase( + userWallet = userWallet, + cryptoCurrency = cryptoCurrency, + transactionDataList = listOf( + getEnterTx(), + getDeployTx(), + ), + ) Truth.assertThat(result.isRight()).isTrue() val txs = (result as Either.Right).value - Truth.assertThat(txs.size).isEqualTo(3) - val firstFee = txs[0].fee as Fee.Ethereum.Legacy - val secondFee = txs[1].fee as Fee.Ethereum.Legacy - val lastFee = txs[2].fee as Fee.Ethereum.Legacy - Truth.assertThat(firstFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) - Truth.assertThat(secondFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) - Truth.assertThat(lastFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) + val deployFee = txs.first().fee as Fee.Ethereum.Legacy + val enterFee = txs.last().fee as Fee.Ethereum.Legacy + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) + Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) } @Test - fun `test 1 transaction uses constant gas limit Eip1559`() = runTest { + fun `test enter transaction uses constant gas limit Eip1559`() = runTest { val fee = TransactionFee.Single(ethEip1559Fee()) - val tx = uncompiled(ethEip1559Fee()) coEvery { feeRepository.calculateFee(any(), any(), any()) } returns fee - val result = useCase(userWallet, cryptoCurrency, listOf(tx)) + val result = useCase( + userWallet = userWallet, + cryptoCurrency = cryptoCurrency, + transactionDataList = listOf( + getDeployTx(), + getEnterTx(), + ), + ) Truth.assertThat(result.isRight()).isTrue() val txs = (result as Either.Right).value - Truth.assertThat(txs.size).isEqualTo(1) - val lastFee = txs.last().fee as Fee.Ethereum.EIP1559 - Truth.assertThat(lastFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) + val deployFee = txs.first().fee as Fee.Ethereum.EIP1559 + val enterFee = txs.last().fee as Fee.Ethereum.EIP1559 + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) + Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) } @Test - fun `test 2 transactions, only last uses constant gas limit Eip1559`() = runTest { + fun `test not enter transaction uses gas limit Eip1559`() = runTest { val fee = TransactionFee.Single(ethEip1559Fee()) - val tx = uncompiled(ethEip1559Fee()) coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee) - val result = useCase(userWallet, cryptoCurrency, listOf(tx, tx)) + val result = useCase( + userWallet = userWallet, + cryptoCurrency = cryptoCurrency, + transactionDataList = listOf( + getDeployTx(), + getApproveTx(), + ), + ) Truth.assertThat(result.isRight()).isTrue() val txs = (result as Either.Right).value - Truth.assertThat(txs.size).isEqualTo(2) - val firstFee = txs.first().fee as Fee.Ethereum.EIP1559 - val lastFee = txs.last().fee as Fee.Ethereum.EIP1559 - Truth.assertThat(firstFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) - Truth.assertThat(lastFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) + val deployFee = txs.first().fee as Fee.Ethereum.EIP1559 + val approveFee = txs.last().fee as Fee.Ethereum.EIP1559 + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) + Truth.assertThat(approveFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) } @Test - fun `test 3 transactions, only last uses constant gas limit Eip1559`() = runTest { + fun `test enter transaction with wrong order uses gas limit Eip1559`() = runTest { val fee = TransactionFee.Single(ethEip1559Fee()) - val tx = uncompiled(ethEip1559Fee()) - coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee, fee) - val result = useCase(userWallet, cryptoCurrency, listOf(tx, tx, tx)) + coEvery { feeRepository.calculateFee(any(), any(), any()) } returnsMany listOf(fee, fee) + + val result = useCase( + userWallet = userWallet, + cryptoCurrency = cryptoCurrency, + transactionDataList = listOf( + getEnterTx(), + getDeployTx(), + ), + ) Truth.assertThat(result.isRight()).isTrue() val txs = (result as Either.Right).value - Truth.assertThat(txs.size).isEqualTo(3) - val firstFee = txs[0].fee as Fee.Ethereum.EIP1559 - val secondFee = txs[1].fee as Fee.Ethereum.EIP1559 - val lastFee = txs[2].fee as Fee.Ethereum.EIP1559 - Truth.assertThat(firstFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) - Truth.assertThat(secondFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) - Truth.assertThat(lastFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) + val deployFee = txs.first().fee as Fee.Ethereum.EIP1559 + val enterFee = txs.last().fee as Fee.Ethereum.EIP1559 + Truth.assertThat(deployFee.gasLimit).isEqualTo(BigInteger.valueOf(21_000)) + Truth.assertThat(enterFee.gasLimit).isEqualTo(BigInteger.valueOf(350_000)) } + + private fun getDeployTx() = uncompiled( + fee = ethLegacyFee(), + extras = EthereumTransactionExtras( + YieldSupplyContractCallDataProviderFactory.getDeployCallData( + walletAddress = "0x1234567890123456789012345678901234567890", + tokenContractAddress = "0x1234567890123456789012345678901234567890", + maxNetworkFee = BigDecimal.ONE.convertToSdkAmount(cryptoCurrency), + ), + ), + ) + + private fun getApproveTx() = uncompiled( + fee = ethLegacyFee(), + extras = EthereumTransactionExtras( + SmartContractCallDataProviderFactory.getApprovalCallData( + spenderAddress = "0x1234567890123456789012345678901234567890", + amount = null, + blockchain = Blockchain.EthereumTestnet, + ), + ), + ) + + private fun getEnterTx() = uncompiled( + fee = ethLegacyFee(), + extras = EthereumTransactionExtras( + YieldSupplyContractCallDataProviderFactory.getEnterCallData( + tokenContractAddress = "0x1234567890123456789012345678901234567890", + ), + ), + ) } \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 871b050803..209f923828 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -19,9 +19,11 @@ platform :android do desc "Run detekt" lane :detekt do + FileUtils.cp("../app/src/main/assets/tangem-app-config/android/google-services/dev/google-services.json", "../app") FileUtils.cp("../tangem-android-tools/CI/gradle_properties/tests_ci_gradle.properties", "../gradle.properties") puts File.read("../gradle.properties") gradle(task: "detekt") + gradle(task: "detektGoogleDebug") end desc "Run tests" diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt index c7c15c062f..bf9e0762a5 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt @@ -60,7 +60,7 @@ internal class AccountCreateEditModel @Inject constructor( private val umBuilder = AccountCreateEditUMBuilder(params) val uiState: StateFlow - field = MutableStateFlow(value = getInitialState()) + field = MutableStateFlow(value = getInitialState()) init { if (params is AccountCreateEditComponent.Params.Create) { diff --git a/features/account/impl/src/main/java/com/tangem/features/account/selector/AccountSelectorModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/selector/AccountSelectorModel.kt index 5012e93d14..8f3998c375 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/selector/AccountSelectorModel.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/selector/AccountSelectorModel.kt @@ -36,7 +36,7 @@ internal class AccountSelectorModel @Inject constructor( private val selectorController get() = params.controller internal val state: StateFlow - field = MutableStateFlow(emptyState()) + field = MutableStateFlow(emptyState()) init { balanceFetcher.data diff --git a/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/CreateWalletSelectionModel.kt b/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/CreateWalletSelectionModel.kt index d5c27dd004..17357cc5c3 100644 --- a/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/CreateWalletSelectionModel.kt +++ b/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/CreateWalletSelectionModel.kt @@ -61,14 +61,14 @@ internal class CreateWalletSelectionModel @Inject constructor( ) : Model() { internal val uiState: StateFlow - field = MutableStateFlow( - CreateWalletSelectionUM( - onBackClick = { router.pop() }, - onMobileWalletClick = ::onMobileWalletClick, - onHardwareWalletClick = ::onHardwareWalletClick, - onScanClick = ::onScanClick, - ), - ) + field = MutableStateFlow( + CreateWalletSelectionUM( + onBackClick = { router.pop() }, + onMobileWalletClick = ::onMobileWalletClick, + onHardwareWalletClick = ::onHardwareWalletClick, + onScanClick = ::onScanClick, + ), + ) init { showAlreadyHaveWalletWithDelay() diff --git a/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt b/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt index cc4e15fb2a..1df0b5eff7 100644 --- a/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt +++ b/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt @@ -140,11 +140,11 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi @Composable private fun WalletBlock( - modifier: Modifier = Modifier, title: String, description: String, - badge: @Composable () -> Unit, onClick: () -> Unit, + modifier: Modifier = Modifier, + badge: @Composable () -> Unit, ) { Column( modifier = modifier diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt index 3bf071d96a..1056d363be 100644 --- a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/ui/compose/StoriesAnimation.kt @@ -95,11 +95,11 @@ fun StoriesTextAnimation( @Composable fun StoriesBottomImageAnimation( + firstStepDuration: Int, + totalDuration: Int, initialScale: Float = 2.5f, secondStageScale: Float = SCALE_SWITCH_BARRIER, targetScale: Float = 1.0f, - firstStepDuration: Int, - totalDuration: Int, content: @Composable (Modifier) -> Unit, ) { val secondStepDuration = totalDuration - firstStepDuration diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt index 62fdd324b1..b3f1b8afa4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt @@ -25,10 +25,7 @@ internal class AccessCodeComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() - if (!state.isConfirmMode) { - DisableScreenshotsDisposableEffect() - } - + DisableScreenshotsDisposableEffect() AccessCode( modifier = modifier, state = state, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt index 40ef51ebc0..6f078713cb 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt @@ -56,7 +56,7 @@ internal class AccessCodeModel @Inject constructor( private val params = paramsContainer.require() internal val uiState: StateFlow - field = MutableStateFlow(getInitialState()) + field = MutableStateFlow(getInitialState()) private fun getInitialState() = AccessCodeUM( accessCode = "", diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt index 4a75ad8438..2e24f436b9 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt @@ -43,7 +43,7 @@ internal class HotAccessCodeRequestModel @Inject constructor( ) val uiState: StateFlow - field = MutableStateFlow(getInitialState()) + field = MutableStateFlow(getInitialState()) suspend fun show(attemptRequest: HotWalletPasswordRequester.AttemptRequest) { if (userWalletExists(attemptRequest.hotWalletId).not()) { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt index 096bd395a0..0cc5ea65a8 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/AddExistingWalletImportComponent.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.hotwallet.addexistingwallet.im.port.model.AddExistingWalletImportModel import com.tangem.features.hotwallet.addexistingwallet.im.port.ui.AddExistingWalletImportContent @@ -22,6 +23,7 @@ internal class AddExistingWalletImportComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() + DisableScreenshotsDisposableEffect() AddExistingWalletImportContent( state = state, modifier = modifier, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt index 60f3305500..add57c93fa 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/AddExistingWalletImportModel.kt @@ -80,7 +80,7 @@ internal class AddExistingWalletImportModel @Inject constructor( } internal val uiState: StateFlow - field = MutableStateFlow(importSeedPhraseUiStateBuilder.getState()) + field = MutableStateFlow(importSeedPhraseUiStateBuilder.getState()) @Suppress("UnusedPrivateMember") private fun importWallet(mnemonic: Mnemonic, passphrase: String?) { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/ImportSeedPhraseUiStateBuilder.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/ImportSeedPhraseUiStateBuilder.kt index 7f89025442..95e7f9c2db 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/ImportSeedPhraseUiStateBuilder.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/im/port/model/ImportSeedPhraseUiStateBuilder.kt @@ -67,13 +67,17 @@ internal class ImportSeedPhraseUiStateBuilder( val text = st.words.text val wordsFromText = text.split(" ").filter { it.isNotBlank() }.map { it.trim() } val newWords = wordsFromText.dropLast(1) + word - val newWordsText = newWords.joinToString(" ") - st.copy( - words = TextFieldValue( - text = newWordsText, - selection = TextRange(newWordsText.length), - ), + val newWordsText = newWords.joinToString(" ").plus(" ") + val newWordsState = TextFieldValue( + text = newWordsText, + selection = TextRange(newWordsText.length), ) + st.copy( + words = newWordsState, + ).also { + launchInterceptWords(wordsField = newWordsState) + suggestNextWord(newWordsState) + } } } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt index e8d5c52060..854ebf49bd 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt @@ -63,16 +63,16 @@ internal class AddExistingWalletStartModel @Inject constructor( private val params: AddExistingWalletStartComponent.Params = paramsContainer.require() internal val uiState: StateFlow - field = MutableStateFlow( - AddExistingWalletStartUM( - showWantToPurchaseBlock = false, - isScanInProgress = false, - onBackClick = params.callbacks::onBackClick, - onImportPhraseClick = params.callbacks::onImportPhraseClick, - onScanCardClick = ::onScanClick, - onBuyCardClick = ::onShopClick, - ), - ) + field = MutableStateFlow( + AddExistingWalletStartUM( + showWantToPurchaseBlock = false, + isScanInProgress = false, + onBackClick = params.callbacks::onBackClick, + onImportPhraseClick = params.callbacks::onImportPhraseClick, + onScanCardClick = ::onScanClick, + onBuyCardClick = ::onShopClick, + ), + ) init { showWantToPurchaseBlockWithDelay() diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt index 0b3261b4f3..c254e87c69 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt @@ -28,13 +28,13 @@ internal class CreateMobileWalletModel @Inject constructor( ) : Model() { internal val uiState: StateFlow - field = MutableStateFlow( - CreateMobileWalletUM( - onBackClick = { router.pop() }, - onCreateClick = ::onCreateClick, - createButtonLoading = false, - ), - ) + field = MutableStateFlow( + CreateMobileWalletUM( + onBackClick = { router.pop() }, + onCreateClick = ::onCreateClick, + createButtonLoading = false, + ), + ) private fun onCreateClick() { modelScope.launch { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt index 7f25916f2d..10d919bad7 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/ManualBackupCheckComponent.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.hotwallet.manualbackup.check.model.ManualBackupCheckModel import com.tangem.features.hotwallet.manualbackup.check.ui.ManualBackupCheckContent @@ -22,6 +23,7 @@ internal class ManualBackupCheckComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() + DisableScreenshotsDisposableEffect() ManualBackupCheckContent( state = state, modifier = modifier, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt index e0778baa69..b68069c4fe 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/check/model/ManualBackupCheckModel.kt @@ -44,7 +44,7 @@ internal class ManualBackupCheckModel @Inject constructor( private val callbacks = params.callbacks internal val uiState: StateFlow - field = MutableStateFlow(getInitialUIState()) + field = MutableStateFlow(getInitialUIState()) init { modelScope.launch { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt index d0fcfe7333..9abbb6caff 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/completed/ManualBackupCompletedModel.kt @@ -18,9 +18,9 @@ internal class ManualBackupCompletedModel @Inject constructor( private val params: ManualBackupCompletedComponent.Params = paramsContainer.require() internal val uiState: StateFlow - field = MutableStateFlow( - ManualBackupCompletedUM( - onContinueClick = { params.callbacks.onContinueClick(params.userWalletId) }, - ), - ) + field = MutableStateFlow( + ManualBackupCompletedUM( + onContinueClick = { params.callbacks.onContinueClick(params.userWalletId) }, + ), + ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt index 0c99cd5606..abb3ea0654 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/model/ManualBackupPhraseModel.kt @@ -35,11 +35,11 @@ internal class ManualBackupPhraseModel @Inject constructor( private val callbacks = params.callbacks internal val uiState: StateFlow - field = MutableStateFlow( - ManualBackupPhraseUM( - onContinueClick = callbacks::onContinueClick, - ), - ) + field = MutableStateFlow( + ManualBackupPhraseUM( + onContinueClick = callbacks::onContinueClick, + ), + ) init { modelScope.launch { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/ManualBackupStartModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/ManualBackupStartModel.kt index ef4f85294b..c847e052a6 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/ManualBackupStartModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/start/ManualBackupStartModel.kt @@ -18,9 +18,9 @@ internal class ManualBackupStartModel @Inject constructor( private val params: ManualBackupStartComponent.Params = paramsContainer.require() internal val uiState: StateFlow - field = MutableStateFlow( - ManualBackupStartUM( - onContinueClick = params.callbacks::onContinueClick, - ), - ) + field = MutableStateFlow( + ManualBackupStartUM( + onContinueClick = params.callbacks::onContinueClick, + ), + ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setupfinished/MobileWalletSetupFinishedModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setupfinished/MobileWalletSetupFinishedModel.kt index 40ba81c5b3..d94e79c3ce 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setupfinished/MobileWalletSetupFinishedModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/setupfinished/MobileWalletSetupFinishedModel.kt @@ -18,9 +18,9 @@ internal class MobileWalletSetupFinishedModel @Inject constructor( private val params: MobileWalletSetupFinishedComponent.Params = paramsContainer.require() internal val uiState: StateFlow - field = MutableStateFlow( - MobileWalletSetupFinishedUM( - onContinueClick = params.callbacks::onContinueClick, - ), - ) + field = MutableStateFlow( + MobileWalletSetupFinishedUM( + onContinueClick = params.callbacks::onContinueClick, + ), + ) } \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt index ed64c2fb93..513718e705 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/stepper/impl/HotWalletStepperModel.kt @@ -20,7 +20,7 @@ internal class HotWalletStepperModel @Inject constructor( val params = paramsContainer.require() val uiState: StateFlow - field = MutableStateFlow(params.initState) + field = MutableStateFlow(params.initState) fun updateState(newState: HotWalletStepperComponent.StepperUM) { uiState.value = newState diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt index ede4241680..a0b991c0a4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/viewphrase/model/ViewPhraseModel.kt @@ -35,11 +35,11 @@ internal class ViewPhraseModel @Inject constructor( private val params = paramsContainer.require() internal val uiState: StateFlow - field = MutableStateFlow( - ViewPhraseUM( - onBackClick = { router.pop() }, - ), - ) + field = MutableStateFlow( + ViewPhraseUM( + onBackClick = { router.pop() }, + ), + ) init { loadSeedPhrase() diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt index e5cd76ac2f..3d889f2034 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt @@ -63,7 +63,7 @@ internal class WalletActivationModel @Inject constructor( is WalletActivationRoute.ManualBackupCheck -> stackNavigation.pop() is WalletActivationRoute.ManualBackupCompleted -> Unit is WalletActivationRoute.SetAccessCode -> Unit - is WalletActivationRoute.ConfirmAccessCode -> Unit + is WalletActivationRoute.ConfirmAccessCode -> stackNavigation.pop() is WalletActivationRoute.PushNotifications -> Unit is WalletActivationRoute.SetupFinished -> Unit } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletbackup/model/WalletBackupModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletbackup/model/WalletBackupModel.kt index b7357044d5..b399977f24 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletbackup/model/WalletBackupModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletbackup/model/WalletBackupModel.kt @@ -33,24 +33,24 @@ internal class WalletBackupModel @Inject constructor( private val params: WalletBackupComponent.Params = paramsContainer.require() val uiState: StateFlow - field = MutableStateFlow( - WalletBackupUM( - onBackClick = { router.pop() }, - recoveryPhraseOption = LabelUM( - text = resourceReference(R.string.hw_backup_no_backup), - style = LabelStyle.WARNING, + field = MutableStateFlow( + WalletBackupUM( + onBackClick = { router.pop() }, + recoveryPhraseOption = LabelUM( + text = resourceReference(R.string.hw_backup_no_backup), + style = LabelStyle.WARNING, + ), + googleDriveOption = LabelUM( + text = resourceReference(R.string.common_coming_soon), + style = LabelStyle.REGULAR, + ), + googleDriveStatus = BackupStatus.ComingSoon, + onRecoveryPhraseClick = ::onRecoveryPhraseClick, + onGoogleDriveClick = { }, + onHardwareWalletClick = ::onHardwareWalletClick, + backedUp = false, ), - googleDriveOption = LabelUM( - text = resourceReference(R.string.common_coming_soon), - style = LabelStyle.REGULAR, - ), - googleDriveStatus = BackupStatus.ComingSoon, - onRecoveryPhraseClick = ::onRecoveryPhraseClick, - onGoogleDriveClick = { }, - onHardwareWalletClick = ::onHardwareWalletClick, - backedUp = false, - ), - ) + ) init { getWalletUseCase.invoke(params.userWalletId) diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt index c7b9296386..03b4921a5d 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/choosetoken/model/ChooseManagedTokensModel.kt @@ -81,7 +81,7 @@ internal class ChooseManagedTokensModel @Inject constructor( val bottomSheetNavigation: SlotNavigation = SlotNavigation() val uiState: StateFlow - field = MutableStateFlow(createReadContentModel()) + field = MutableStateFlow(createReadContentModel()) init { manageTokensListManager.uiItems diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt index 23a63a6edd..9398d31472 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/list/ManageTokensListManager.kt @@ -83,6 +83,11 @@ internal class ManageTokensListManager @AssistedInject constructor( .distinctUntilChanged() val uiItems: Flow> = uiManager.items + /** + * Launch pagination flow to get currencies + * + * @param isCollapsed set initial display state of networks. !!! WARNING !!! Use `false` flag with cation + */ suspend fun launchPagination(isCollapsed: Boolean) = coroutineScope { val loadUserTokensFromRemote = when (mode) { is ManageTokensMode.Wallet -> source == ManageTokensSource.ONBOARDING diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt index 6b22d2f6e3..7ffb94a1db 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt @@ -58,8 +58,8 @@ internal fun MarketsTokenDetailsContent( onBackClick: () -> Unit, onHeaderSizeChange: (Dp) -> Unit, backButtonEnabled: Boolean, - portfolioBlock: @Composable ((Modifier) -> Unit)?, modifier: Modifier = Modifier, + portfolioBlock: @Composable ((Modifier) -> Unit)?, ) { Content( modifier = modifier, @@ -88,8 +88,8 @@ private fun Content( onBackClick: () -> Unit, onHeaderSizeChange: (Dp) -> Unit, backButtonEnabled: Boolean, - portfolioBlock: @Composable ((Modifier) -> Unit)?, modifier: Modifier = Modifier, + portfolioBlock: @Composable ((Modifier) -> Unit)?, ) { val density = LocalDensity.current val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt index f5c13e5165..ac68e930d6 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.token.TokenItem import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.haptic.TangemHapticEffect @@ -100,9 +101,9 @@ private class PortfolioTokenUMProvider : CollectionPreviewParameterProvider Unit, onClick: () -> Unit, modifier: Modifier = Modifier, + startIcon: @Composable RowScope.() -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/entity/DefaultNFTSendSuccessTrigger.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/entity/DefaultNFTSendSuccessTrigger.kt index fa1bb86d49..b3f0254a51 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/entity/DefaultNFTSendSuccessTrigger.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/entity/DefaultNFTSendSuccessTrigger.kt @@ -17,7 +17,7 @@ interface NFTSendSuccessListener { internal class DefaultNFTSendSuccessTrigger @Inject constructor() : NFTSendSuccessTrigger, NFTSendSuccessListener { override val nftSendSuccessFlow: SharedFlow - field = MutableSharedFlow() + field = MutableSharedFlow() override suspend fun triggerSuccessNFTSend() { nftSendSuccessFlow.emit(Unit) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt index ff0ace4c4a..94bff159f9 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/model/OnboardingEntryModel.kt @@ -219,7 +219,7 @@ internal class OnboardingEntryModel @Inject constructor( // legacy flow if (userWalletsListManager.hasUserWallets) { - val isLocked = runCatching { userWalletsListManager.asLockable()?.isLockedSync!! }.getOrElse { false } + val isLocked = runCatching { userWalletsListManager.asLockable()?.isLocked!! }.getOrElse { false } if (isLocked) { router.replaceAll(AppRoute.Welcome()) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt index c8a1c938fd..c7845cae52 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/entry/impl/ui/OnboardingEntry.kt @@ -14,8 +14,8 @@ import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingRoute @Composable internal inline fun OnboardingEntry( - modifier: Modifier = Modifier, childStack: ChildStack, + modifier: Modifier = Modifier, stepperContent: @Composable (Modifier) -> Unit, ) { Column( diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt index 0404814a1f..6d865157e7 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/ui/MultiWalletCreateWallet.kt @@ -7,6 +7,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -19,6 +20,7 @@ import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.test.StoriesScreenTestTags import com.tangem.features.onboarding.v2.impl.R import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM @@ -60,7 +62,9 @@ internal fun MultiWalletCreateWallet(state: MultiWalletCreateWalletUM, modifier: style = TangemTheme.typography.h2, color = TangemTheme.colors.text.primary1, textAlign = TextAlign.Center, - modifier = Modifier.padding(top = 16.dp), + modifier = Modifier + .padding(top = 16.dp) + .testTag(StoriesScreenTestTags.TITLE), ) Text( @@ -68,7 +72,9 @@ internal fun MultiWalletCreateWallet(state: MultiWalletCreateWalletUM, modifier: style = TangemTheme.typography.body1, color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, - modifier = Modifier.padding(top = 12.dp), + modifier = Modifier + .padding(top = 12.dp) + .testTag(StoriesScreenTestTags.TEXT), ) } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/builder/ImportSeedPhraseUiStateBuilder.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/builder/ImportSeedPhraseUiStateBuilder.kt index 0fd46b3494..883e831a85 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/builder/ImportSeedPhraseUiStateBuilder.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/seedphrase/model/builder/ImportSeedPhraseUiStateBuilder.kt @@ -58,13 +58,17 @@ internal class ImportSeedPhraseUiStateBuilder( val text = st.words.text val wordsFromText = text.split(" ").filter { it.isNotBlank() }.map { it.trim() } val newWords = wordsFromText.dropLast(1) + word - val newWordsText = newWords.joinToString(" ") - st.copy( - words = TextFieldValue( - text = newWordsText, - selection = TextRange(newWordsText.length), - ), + val newWordsText = newWords.joinToString(" ").plus(" ") + val newWordsState = TextFieldValue( + text = newWordsText, + selection = TextRange(newWordsText.length), ) + st.copy( + words = newWordsState, + ).also { + launchInterceptWords(wordsField = newWordsState) + suggestNextWord(newWordsState) + } } } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/OnrampOperation.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/OnrampOperation.kt index dea01060e5..fdeba229d0 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/OnrampOperation.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/OnrampOperation.kt @@ -9,5 +9,4 @@ internal enum class OnrampOperation { BUY, SELL, SWAP, - ; } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorReloadTrigger.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorReloadTrigger.kt index 71b251f889..6d082b2615 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorReloadTrigger.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorReloadTrigger.kt @@ -18,16 +18,16 @@ internal class DefaultFeeSelectorReloadTrigger @Inject constructor() : FeeSelectorCheckReloadListener { override val reloadTriggerFlow: SharedFlow - field = MutableSharedFlow() + field = MutableSharedFlow() override val loadingStateTriggerFlow: SharedFlow - field = MutableSharedFlow() + field = MutableSharedFlow() override val checkReloadTriggerFlow: SharedFlow - field = MutableSharedFlow() + field = MutableSharedFlow() override val checkReloadResultFlow: SharedFlow - field = MutableSharedFlow() + field = MutableSharedFlow() override suspend fun triggerUpdate(feeData: FeeSelectorData) { reloadTriggerFlow.emit(feeData) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt index 8b04dae993..958449221b 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt @@ -62,7 +62,7 @@ internal class FeeSelectorModel @Inject constructor( val feeSelectorBottomSheet = SlotNavigation() val uiState: StateFlow - field = MutableStateFlow(params.state) + field = MutableStateFlow(params.state) init { initAppCurrency() diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index 49efa0b9db..94e92ba3eb 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -127,7 +127,7 @@ internal class SendConfirmModel @Inject constructor( val uiState = _uiState.asStateFlow() val isBalanceHiddenFlow: StateFlow - field = MutableStateFlow(false) + field = MutableStateFlow(false) private val amountState get() = uiState.value.amountUM as? AmountState.Data diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index a6fec59187..5f82216ede 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -106,10 +106,10 @@ internal class SendModel @Inject constructor( val analyticCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY val uiState: StateFlow - field = MutableStateFlow(initialState()) + field = MutableStateFlow(initialState()) val isBalanceHiddenFlow: StateFlow - field = MutableStateFlow(false) + field = MutableStateFlow(false) val initialRoute = if (params.amount == null) { if (uiState.value.isRedesignEnabled) { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt index 9660b61db5..ce217d1184 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt @@ -88,10 +88,10 @@ internal class NFTSendModel @Inject constructor( private val nftAsset = params.nftAsset val uiState: StateFlow