From db54eb6236abdbd904a21de1facc1bc5ab9f6e6f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 23 Jul 2024 23:22:10 +0000 Subject: [PATCH] Publish source 2024-07-23 --- app/.gitignore | 1 - app/.idea/vcs.xml | 10 - app/android/app/build.gradle.kts | 4 +- .../core/form/FormScreen+FormScreenModel.kt | 2 +- .../ui/app/moneyhome/MoneyHomeScreen.kt | 203 +++++----- .../ui/app/nfc/FwupInstructionsScreen.kt | 2 +- .../wallet/ui/app/settings/SettingsScreen.kt | 9 +- ...tivationInstructionsFormScreenSnapshots.kt | 2 +- ...areFingerprintEnrollmentScreenSnapshots.kt | 6 +- .../device/DeviceSettingsScreenSnapshots.kt | 20 +- ..._device settings - no update available.png | 4 +- ...replacement pending and update pending.png | 4 +- ..._device settings - replacement pending.png | 4 +- ...ots_device settings - update available.png | 4 +- ...ce settings - when device is not found.png | 4 +- ...eyHome Screen Full with hidden balance.png | 4 +- ...Screen Lite without protecting wallets.png | 4 +- ...eScreenSnapshots_moneyhome_screen_full.png | 4 +- ...ettingsScreenSnapshots_settings screen.png | 4 +- .../ui/components/banner/BannerStyle.kt | 2 +- .../ui/components/button/ButtonStyle.kt | 8 +- .../ui/components/coachmark/Coachmark.kt | 197 ++++++++++ .../coachmark/CoachmarkPresenter.kt | 35 ++ .../ui/components/coachmark/NewCoachmark.kt | 74 ++++ .../wallet/ui/components/forms/Picker.kt | 2 +- .../wallet/ui/components/forms/TextField.kt | 2 +- .../wallet/ui/components/icon/IconImage.kt | 2 +- .../wallet/ui/components/icon/IconStyle.kt | 3 +- .../build/wallet/ui/components/label/Label.kt | 2 +- .../wallet/ui/components/list/ListItem.kt | 215 ++++++----- .../ui/components/list/ListItemPickerMenu.kt | 2 +- .../progress/CircularProgressIndicator.kt | 2 +- .../progress/LinearProgressIndicator.kt | 2 +- .../components/progress/StepperIndicator.kt | 2 +- .../wallet/ui/components/slider/Slider.kt | 4 +- .../wallet/ui/components/switch/Switch.kt | 2 +- .../wallet/ui/tokens/StyleDictionaryColors.kt | 6 +- .../wallet/ui/tokens/StyleDictionaryIcons.kt | 8 +- .../drawable/small_icon_settings_badged.xml | 25 ++ ...onents.icon_IconSnapshots_avatar icons.png | 4 +- ...ponents.icon_IconSnapshots_large icons.png | 4 +- ...nents.icon_IconSnapshots_regular icons.png | 4 +- ...ponents.icon_IconSnapshots_small icons.png | 4 +- ...onents.icon_IconSnapshots_tinted icons.png | 4 +- .../gradle/logic/extensions/AppExtension.kt | 3 +- .../wallet/gradle/logic/gradle/Snapshots.kt | 26 +- .../CalloutArrow.svg} | 0 .../Contents.json | 2 +- .../Contents.json | 15 + .../SmallIconSettingsBadged.svg | 5 + app/ios/Wallet/Sources/App/AppContext.swift | 1 - .../Sources/DebugMenu/AnalyticsView.swift | 4 +- .../Wallet/Sources/DebugMenu/LogsView.swift | 4 +- .../Secp256k1KeyGeneratorImpl.swift | 2 +- .../Sources/Logging/DatadogTracerImpl.swift | 4 +- .../Wallet/Sources/Logging/OSLogWriter.swift | 35 -- .../Wallet/Sources/NFC/NfcCommandsImpl.swift | 13 +- .../NotificationManagerImpl.swift | 4 +- .../FirmwareDeviceInfoExtensions.swift | 13 +- .../AppUiStateMachineManagerImpl.swift | 2 + ...BiometricPromptUiStateMachineManager.swift | 2 +- .../Sources/UI/Core/Model/TextModel.swift | 11 +- .../UI/Core/View+ConditionalModifier.swift | 8 +- .../UI/Core/View/AmountSliderView.swift | 2 +- .../UI/Core/View/CircularProgressView.swift | 2 +- .../Sources/UI/Core/View/CoachmarkView.swift | 149 ++++++++ .../Core/View/CollapsibleLabelContainer.swift | 7 +- .../Sources/UI/Core/View/IconView.swift | 2 +- .../Sources/UI/Core/View/KeypadView.swift | 1 + .../Sources/UI/Core/View/ListGroupView.swift | 16 +- .../UI/Core/View/ListItemAccessoryView.swift | 2 +- .../Sources/UI/Core/View/ListItemView.swift | 204 +++++----- .../Sources/UI/Core/View/NewCoachmark.swift | 68 ++++ .../Sources/UI/Core/View/ShowcaseView.swift | 24 +- .../Sources/UI/Core/View/SwitchCardView.swift | 2 +- .../Sources/UI/Core/View/VideoView.swift | 6 +- .../UI/DesignSystem/Button+DesignSystem.swift | 8 +- .../UI/DesignSystem/Color+DesignSystem.swift | 4 +- .../UI/DesignSystem/DesignSystemVideos.swift | 5 +- .../UI/DesignSystem/Text+DesignSystem.swift | 2 + .../Sources/UI/DesignSystem/gen/Color.swift | 4 +- .../Sources/UI/DesignSystem/gen/Icons.swift | 4 +- .../UI/Form/Base/FormMainContentView.swift | 37 +- app/ios/Wallet/Sources/UI/Form/FormView.swift | 2 +- .../Helpers/SharedIconModelExtension.swift | 2 +- .../Form/MainContent/FeeOptionListView.swift | 4 +- .../UI/Form/MainContent/StepperView.swift | 2 +- .../UI/Screens/Education/EducationView.swift | 2 +- .../Screens/MoneyHome/AddressQrCodeView.swift | 2 +- .../MoneyHome/Views/LiteMoneyHomeView.swift | 21 +- .../MoneyHome/Views/MoneyHomeView.swift | 103 ++++- .../UI/Screens/Settings/SettingsView.swift | 83 ++++- .../SpendingLimit/SpendingLimitCardView.swift | 2 +- .../CurrencyPreferenceSnapshotTests.swift | 12 +- .../DeviceSettingsSnapshotTests.swift | 20 +- .../SnapshotTests/ErrorSnapshotTests.swift | 6 +- .../MoneyHomeSnapshotTests.swift | 27 +- .../OnboardingSnapshotTests.swift | 8 +- .../SnapshotTests/SettingSnapshotTests.swift | 21 +- ...tings_replacement_pending.iPhoneProMax.png | Bin 272161 -> 279275 bytes ...cement_pending_and_update.iPhoneProMax.png | Bin 272161 -> 279275 bytes ..._when_device_is_not_found.iPhoneProMax.png | Bin 206999 -> 214250 bytes ...e_settings_with_no_update.iPhoneProMax.png | Bin 267272 -> 274547 bytes ...ngs_with_update_available.iPhoneProMax.png | Bin 278353 -> 285605 bytes ...e_with_coachmark_settings.iPhoneProMax.png | Bin 0 -> 1278414 bytes ..._home_with_coachmark_settings.iPhoneSe.png | Bin 0 -> 716606 bytes ...e_with_coachmark_settings.iPhoneProMax.png | Bin 0 -> 171193 bytes ..._home_with_coachmark_settings.iPhoneSe.png | Bin 0 -> 111450 bytes .../test_settings.iPhoneProMax.png | Bin 121001 -> 134526 bytes .../test_settings.iPhoneSe.png | Bin 83647 -> 95855 bytes ...ttings_with_status_banner.iPhoneProMax.png | Bin 136593 -> 149067 bytes ...t_settings_with_status_banner.iPhoneSe.png | Bin 97008 -> 108307 bytes ...st_sweep_funds_prompt_app.iPhoneProMax.png | Bin 141228 -> 142263 bytes .../test_sweep_funds_prompt_app.iPhoneSe.png | Bin 99660 -> 100638 bytes ...eep_funds_prompt_hardware.iPhoneProMax.png | Bin 139520 -> 140555 bytes ...t_sweep_funds_prompt_hardware.iPhoneSe.png | Bin 98192 -> 99169 bytes .../NotificationManagerImplTests.swift | 6 +- app/ios/project.yml | 8 +- app/justfile | 16 +- app/rust/firmware-ffi/src/firmware.udl | 11 + app/rust/firmware-ffi/src/lib.rs | 4 +- app/rust/wca/src/commands/device_id.rs | 39 ++ app/rust/wca/src/commands/mod.rs | 6 +- .../build/wallet/account/AccountDaoImpl.kt | 58 ++- .../AccountRepositoryImplComponentTests.kt | 24 +- .../analytics/events/EventTrackerMock.kt | 20 +- .../analytics/events/EventTrackerImpl.kt | 39 +- .../analytics/events/EventTrackerImplTests.kt | 6 +- .../wallet/analytics/events/EventTracker.kt | 11 +- .../analytics/events/EventTrackerContext.kt | 10 + .../events/screen/EventTrackerCountInfo.kt | 5 +- .../EventTrackerFingerprintScanStatsInfo.kt | 10 + .../events/screen/EventTrackerScreenInfo.kt | 10 +- ...hKeyRotationEventTrackerScreenIdContext.kt | 4 +- .../CloudEventTrackerScreenIdContext.kt | 4 +- .../context/EventTrackerScreenIdContext.kt | 5 - .../context/NfcEventTrackerScreenIdContext.kt | 4 +- ...PairHardwareEventTrackerScreenIdContext.kt | 4 +- ...NotificationEventTrackerScreenIdContext.kt | 4 +- ...InactiveWalletSweepEventTrackerScreenId.kt | 2 +- .../app-component/impl/build.gradle.kts | 1 + .../build/wallet/di/ActivityComponentImpl.kt | 124 +++--- .../build/wallet/di/AppComponentImpl.kt | 10 +- .../kotlin/build/wallet/di/AppComponent.kt | 2 +- .../auth/AppAuthPublicKeyProviderImpl.kt | 11 +- .../auth/AppAuthPublicKeyProviderImplTests.kt | 44 ++- .../build/wallet/bdk/bindings/BdkError.kt | 10 +- .../build/wallet/bitkey/account/Account.kt | 1 + .../account/OnboardingSoftwareAccount.kt | 6 +- app/shared/coachmark/fake/build.gradle.kts | 2 +- .../wallet/coachmark/CoachmarkDaoFake.kt | 6 +- .../wallet/coachmark/CoachmarkServiceMock.kt | 19 +- app/shared/coachmark/impl/build.gradle.kts | 4 +- .../wallet/coachmark/CoachmarkDaoImpl.kt | 12 +- .../CoachmarkFeatureFlagVisibilityDecider.kt | 30 -- .../wallet/coachmark/CoachmarkServiceImpl.kt | 28 +- .../coachmark/CoachmarkVisibilityDecider.kt | 28 ++ .../wallet/coachmark/CoachmarkDaoImplTests.kt | 6 +- .../wallet/coachmark/CoachmarkServiceTests.kt | 113 +++++- .../CoachmarkVisibilityDeciderTests.kt | 85 +++++ app/shared/coachmark/public/build.gradle.kts | 1 + .../build/wallet/coachmark/Coachmark.kt | 2 +- .../wallet/coachmark/CoachmarkIdentifier.kt | 15 +- app/shared/database/public/build.gradle.kts | 1 + .../database/BitkeyDatabaseProviderImpl.kt | 23 +- .../wallet/database/sqldelight/Coachmarks.sq | 12 +- .../sqldelight/PartnershipTransactions.sq | 11 +- .../database/sqldelight/SoftwareAccount.sq | 6 +- .../src/commonMain/sqldelight/databases/10.db | Bin 0 -> 425984 bytes .../commonMain/sqldelight/migrations/9.sqm | 6 + .../EmergencyAccessKitPayloadDecoderImpl.kt | 144 +++---- .../GetPurchaseQuoteListF8eClientMock.kt | 2 +- .../featureflags/FeatureFlagsF8eClientImpl.kt | 3 +- .../onboarding/CreateAccountF8eClientImpl.kt | 8 +- .../CreateAccountKeysetF8eClientImpl.kt | 6 +- .../onboarding/UpgradeAccountF8eClientImpl.kt | 34 +- .../partnerships/GetPartnerF8eClientImpl.kt | 2 +- .../build/wallet/f8e/partnerships/Quote.kt | 10 + .../flags/CoachmarksGlobalFeatureFlag.kt | 20 + ...ultipleFingerprintsIsEnabledFeatureFlag.kt | 21 -- .../firmware/FirmwareDeviceIdentifiersMock.kt | 3 +- .../firmware/FirmwareDeviceInfoDaoImpl.kt | 6 +- .../wallet/firmware/FirmwareDeviceInfoTest.kt | 3 +- .../firmware/FingerprintEnrollmentStatus.kt | 9 +- .../wallet/firmware/FirmwareDeviceInfo.kt | 37 +- .../MobilePayFiatConfigServiceImpl.kt | 1 - app/shared/nfc/impl/build.gradle.kts | 1 + .../build/wallet/nfc/NfcCommandsImpl.kt | 15 +- .../build/wallet/nfc/NfcCommandsFake.kt | 3 +- .../FirmwareTelemetryInterceptor.kt | 2 +- .../nfc/interceptors/MetricsInterceptors.kt | 32 +- .../CreateSoftwareWalletWorkflowImpl.kt | 16 +- .../onboarding/SoftwareAccountCreatorImpl.kt | 13 +- ...ateSoftwareWalletWorkflowComponentTests.kt | 8 +- .../CreateSoftwareWalletWorkflow.kt | 5 +- ...tnershipTransactionStatusRepositoryMock.kt | 28 ++ .../PartnershipTransactionEntity.kt | 4 +- .../PartnershipTransactionsDao.kt | 12 + .../PartnershipTransactionsDaoImpl.kt | 20 + .../PartnershipTransactionsRepositoryImpl.kt | 23 ++ .../PartnershipTransactionEntityTest.kt | 4 +- .../PartnershipTransactionsDaoMock.kt | 18 + .../PartnershipTransactionsRepositoryTest.kt | 128 ++++++- ...PartnershipTransactionsStatusRepository.kt | 22 ++ .../sweep/SweepPromptRequirementCheckImpl.kt | 1 - ...ActivateFullAccountDataStateMachineImpl.kt | 40 +- .../firmware/FirmwareDataStateMachineImpl.kt | 3 +- ...ateFullAccountDataStateMachineImplTests.kt | 203 ++++------ .../state-machine/ui/public/build.gradle.kts | 1 + .../cloud/CloudSignInUiStateMachineImpl.kt | 2 +- .../EnableNotificationsUiStateMachineImpl.kt | 2 +- .../ChooseAccountAccessUiStateMachineImpl.kt | 4 +- .../CreateSoftwareWalletUiStateMachineImpl.kt | 2 +- .../ActivationInstructionsBodyModel.kt | 6 +- ...ardwareFingerprintEnrollmentScreenModel.kt | 8 +- .../PairNewHardwareUiStateMachineImpl.kt | 8 +- ...gerprintEnrollmentInstructionsBodyModel.kt | 2 +- .../BiometricSettingUiStateMachineImpl.kt | 62 ++- .../statemachine/core/ErrorFormBodyModel.kt | 14 +- .../statemachine/core/LoadingBodyModel.kt | 6 +- .../core/LoadingSuccessBodyModel.kt | 6 +- .../core/NfcErrorFormBodyModel.kt | 2 +- .../core/NfcExceptionFormBodyModel.kt | 2 +- .../statemachine/core/form/FormBodyModel.kt | 6 +- .../dev/DebugMenuListStateMachineImpl.kt | 47 ++- .../moneyhome/MoneyHomeBodyModel.kt | 15 +- ...neyHomeViewingBalanceUiStateMachineImpl.kt | 23 +- .../moneyhome/lite/LiteMoneyHomeBodyModel.kt | 9 +- .../lite/LiteMoneyHomeUiStateMachineImpl.kt | 1 + .../nfc/FwupInstructionsBodyModel.kt | 6 +- .../nfc/NfcSessionUIStateMachine.kt | 6 +- .../PartnerEventTrackerScreenIdContext.kt | 4 +- .../ExpectedTransactionNoticeModel.kt | 2 + ...ctedTransactionNoticeUiStateMachineImpl.kt | 39 +- .../PartnershipsPurchaseUiStateMachineImpl.kt | 68 ++-- .../PartnershipsSelectPurchaseAmountModel.kt | 2 +- .../partnerships/purchase/QuoteDisplay.kt | 41 ++ .../purchase/SelectPartnerQuoteModel.kt | 69 ++-- .../PartnershipsTransferUiStateMachineImpl.kt | 21 +- .../recovery/cloud/RotateAuthKeyScreens.kt | 14 +- .../recovery/sweep/SweepBodyModels.kt | 14 +- .../TransferConfirmationUiStateMachineImpl.kt | 2 +- .../settings/SettingsBodyModel.kt | 1 + .../SettingsListUiStateMachineImpl.kt | 59 ++- .../device/DeviceSettingsFormBodyModel.kt | 24 +- .../DeviceSettingsUiStateMachineImpl.kt | 67 +++- .../EnrollingFingerprintUiStateMachineImpl.kt | 6 +- .../FingerprintNfcCommandsImpl.kt | 1 - .../TransactionDetailsUiStateMachineImpl.kt | 13 +- ...oseAccountAccessUiStateMachineImplTests.kt | 57 +-- ...iometricSettingsUiStateMachineImplTests.kt | 46 ++- .../MoneyHomeCardsStateMachineImplTests.kt | 2 +- .../AddBitcoinUiStateMachineImplTests.kt | 23 +- ...nershipsPurchaseUiStateMachineImplTests.kt | 238 +++++++++++- ...nershipsTransferUiStateMachineImplTests.kt | 57 ++- ...tedTransactionNoticeUiStateMachineTests.kt | 43 ++- .../purchase/QuoteDisplayTests.kt | 73 ++++ .../SettingsListUiStateMachineImplTests.kt | 16 +- .../DeviceSettingsUiStateMachineImplTests.kt | 19 +- ...ansactionDetailsUiStateMachineImplTests.kt | 37 +- .../EnableNotificationsUiStateMachineImpl.kt | 2 +- .../cloud/CloudSignInUiStateMachineImpl.kt | 2 +- .../create/CreateSoftwareWalletE2eTests.kt | 2 +- .../cloud/RotateAuthKeyFunctionalTests.kt | 40 +- .../socrec/SocRecE2eFunctionalTests.kt | 2 + .../cloud/CloudSignInModelFake.kt | 2 +- .../cloud/CloudSignInUiStateMachineFake.kt | 2 +- .../EnableNotificationsUiStateMachineImpl.kt | 2 +- .../kotlin/build/wallet/time/DurationMath.kt | 4 +- app/shared/ui/core/public/build.gradle.kts | 2 +- .../build/wallet/statemachine/core/Icon.kt | 1 + .../ui/model/coachmark/CoachmarkModel.kt | 41 ++ .../model/coachmark/NewCoachmarkTreatment.kt | 10 + .../wallet/ui/model/list/ListItemModel.kt | 9 +- app/style/tokens/color.json | 2 +- app/style/tokens/icons.json | 1 + bumpver.json | 2 +- cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- datadog/monitors/web/shop-api.ts | 127 +++++-- firmware/app/tasks/auth/src/auth_task.c | 31 +- firmware/app/tasks/sysinfo/meson.build | 1 + firmware/app/tasks/sysinfo/src/sysinfo_task.c | 44 +++ firmware/hal/biometrics/inc/bio.h | 18 + firmware/hal/biometrics/meson.build | 3 + firmware/hal/biometrics/src/fpc_biometrics.c | 57 +++ .../memfault_trace_reason_user_config.def | 3 + firmware/lib/filesystem/filesystem_util.c | 4 +- firmware/lib/kv/kv.c | 187 ++++++++++ firmware/lib/kv/kv.h | 51 +++ firmware/lib/kv/kv_fuzz.cc | 158 ++++++++ firmware/lib/kv/kv_test.c | 352 ++++++++++++++++++ firmware/lib/kv/meson.build | 35 ++ firmware/lib/metadata/inc/metadata.h | 1 + firmware/lib/metadata/src/metadata.c | 16 + firmware/lib/protobuf/protos/wallet.proto | 13 + firmware/meson.build | 1 + firmware/python/bitkey/firmware_signer.py | 1 - firmware/tasks/memfault.py | 2 +- server/Cargo.lock | 1 + server/src/api/account/src/entities.rs | 85 ++++- .../create_inactive_spending_keyset.rs | 48 ++- .../api/account/src/service/fetch_account.rs | 13 +- .../src/service/rotate_to_spending_keyset.rs | 47 ++- .../api/analytics/config/proto_config.yaml | 12 + .../src/destination/segment_tracker.rs | 1 + server/src/api/analytics/src/routes.rs | 1 + server/src/api/onboarding/Cargo.toml | 1 + server/src/api/onboarding/src/routes.rs | 307 ++++++++++++++- server/src/api/partnerships/src/routes.rs | 2 +- .../account_keysets_integration_tests.rs | 8 +- .../src/tests/analytics_integration_tests.rs | 1 + .../src/tests/onboarding_integration_tests.rs | 139 ++++++- .../src/api/server/src/tests/requests/axum.rs | 53 ++- .../build/wallet/analytics/v1/event.proto | 45 ++- .../wsm/wsm-enclave/src/psbt_verification.rs | 24 +- server/src/wsm/wsm-rust-client/src/lib.rs | 15 +- terraform/atlantis/Dockerfile | 13 +- .../shared/atlantis/service/terragrunt.hcl | 2 +- .../apps/fromagerie/service/db/main.tf | 29 ++ .../apps/fromagerie/service/db/variables.tf | 5 + .../modules/apps/fromagerie/service/main.tf | 3 + terraform/modules/named-stacks/api/main.tf | 1 + 323 files changed, 5543 insertions(+), 1615 deletions(-) delete mode 100644 app/.idea/vcs.xml create mode 100644 app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/Coachmark.kt create mode 100644 app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/CoachmarkPresenter.kt create mode 100644 app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/NewCoachmark.kt create mode 100644 app/android/ui/core/public/src/main/res/drawable/small_icon_settings_badged.xml rename app/ios/Wallet/Resources/Assets.xcassets/Other/{callout-arrow.imageset/callout-arrow.svg => CalloutArrow.imageset/CalloutArrow.svg} (100%) rename app/ios/Wallet/Resources/Assets.xcassets/Other/{callout-arrow.imageset => CalloutArrow.imageset}/Contents.json (82%) create mode 100644 app/ios/Wallet/Resources/Assets.xcassets/Small (24pt) Icons/SmallIconSettingsBadged.imageset/Contents.json create mode 100644 app/ios/Wallet/Resources/Assets.xcassets/Small (24pt) Icons/SmallIconSettingsBadged.imageset/SmallIconSettingsBadged.svg delete mode 100644 app/ios/Wallet/Sources/Logging/OSLogWriter.swift create mode 100644 app/ios/Wallet/Sources/UI/Core/View/CoachmarkView.swift create mode 100644 app/ios/Wallet/Sources/UI/Core/View/NewCoachmark.swift create mode 100644 app/ios/Wallet/Tests/SnapshotTests/__Snapshots__/SnapshotTestHelpers/test_lite_money_home_with_coachmark_settings.iPhoneProMax.png create mode 100644 app/ios/Wallet/Tests/SnapshotTests/__Snapshots__/SnapshotTestHelpers/test_lite_money_home_with_coachmark_settings.iPhoneSe.png create mode 100644 app/ios/Wallet/Tests/SnapshotTests/__Snapshots__/SnapshotTestHelpers/test_money_home_with_coachmark_settings.iPhoneProMax.png create mode 100644 app/ios/Wallet/Tests/SnapshotTests/__Snapshots__/SnapshotTestHelpers/test_money_home_with_coachmark_settings.iPhoneSe.png create mode 100644 app/shared/analytics/public/src/commonMain/kotlin/build/wallet/analytics/events/EventTrackerContext.kt create mode 100644 app/shared/analytics/public/src/commonMain/kotlin/build/wallet/analytics/events/screen/EventTrackerFingerprintScanStatsInfo.kt delete mode 100644 app/shared/analytics/public/src/commonMain/kotlin/build/wallet/analytics/events/screen/context/EventTrackerScreenIdContext.kt delete mode 100644 app/shared/coachmark/impl/src/commonMain/kotlin/build/wallet/coachmark/CoachmarkFeatureFlagVisibilityDecider.kt create mode 100644 app/shared/coachmark/impl/src/commonMain/kotlin/build/wallet/coachmark/CoachmarkVisibilityDecider.kt create mode 100644 app/shared/coachmark/impl/src/commonTest/kotlin/build/wallet/coachmark/CoachmarkVisibilityDeciderTests.kt create mode 100644 app/shared/database/public/src/commonMain/sqldelight/databases/10.db create mode 100644 app/shared/database/public/src/commonMain/sqldelight/migrations/9.sqm create mode 100644 app/shared/feature-flag/public/src/commonMain/kotlin/build/wallet/feature/flags/CoachmarksGlobalFeatureFlag.kt delete mode 100644 app/shared/feature-flag/public/src/commonMain/kotlin/build/wallet/feature/flags/MultipleFingerprintsIsEnabledFeatureFlag.kt create mode 100644 app/shared/state-machine/ui/public/src/commonMain/kotlin/build/wallet/statemachine/partnerships/purchase/QuoteDisplay.kt create mode 100644 app/shared/state-machine/ui/public/src/commonTest/kotlin/build/wallet/statemachine/partnerships/purchase/QuoteDisplayTests.kt create mode 100644 app/shared/ui/core/public/src/commonMain/kotlin/build/wallet/ui/model/coachmark/CoachmarkModel.kt create mode 100644 app/shared/ui/core/public/src/commonMain/kotlin/build/wallet/ui/model/coachmark/NewCoachmarkTreatment.kt create mode 100644 firmware/lib/kv/kv.c create mode 100644 firmware/lib/kv/kv.h create mode 100644 firmware/lib/kv/kv_fuzz.cc create mode 100644 firmware/lib/kv/kv_test.c create mode 100644 firmware/lib/kv/meson.build diff --git a/app/.gitignore b/app/.gitignore index 7eafa0fc1..36af38007 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -5,7 +5,6 @@ !/.idea/dictionaries/ !/.idea/externalDependencies.xml !/.idea/detekt.xml -!/.idea/vcs.xml # ktlint code style !/.idea/codeStyles/codeStyleConfig.xml !/.idea/codeStyles/Project.xml diff --git a/app/.idea/vcs.xml b/app/.idea/vcs.xml deleted file mode 100644 index 35012d555..000000000 --- a/app/.idea/vcs.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/android/app/build.gradle.kts b/app/android/app/build.gradle.kts index c437addd8..48360113b 100644 --- a/app/android/app/build.gradle.kts +++ b/app/android/app/build.gradle.kts @@ -15,9 +15,9 @@ buildLogic { app { version( yyyy = 2024, - version = 62, + version = 63, patch = 0, - build = 2 + build = 4 ) } compose { diff --git a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/core/form/FormScreen+FormScreenModel.kt b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/core/form/FormScreen+FormScreenModel.kt index 8fc2bc632..d2efce128 100644 --- a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/core/form/FormScreen+FormScreenModel.kt +++ b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/core/form/FormScreen+FormScreenModel.kt @@ -284,7 +284,7 @@ private fun Explainer(statements: ImmutableList) { append(body.string) body.linkedSubstrings.forEach { linkedSubstring -> addStyle( - style = SpanStyle(color = WalletTheme.colors.primary), + style = SpanStyle(color = WalletTheme.colors.bitkeyPrimary), start = linkedSubstring.range.first, end = linkedSubstring.range.last + 1 ) diff --git a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/moneyhome/MoneyHomeScreen.kt b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/moneyhome/MoneyHomeScreen.kt index 818c69d70..7ce638024 100644 --- a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/moneyhome/MoneyHomeScreen.kt +++ b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/moneyhome/MoneyHomeScreen.kt @@ -2,25 +2,22 @@ package build.wallet.ui.app.moneyhome import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.runtime.Composable +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.positionInParent import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import build.wallet.bitkey.socrec.ProtectedCustomer import build.wallet.bitkey.socrec.ProtectedCustomerAlias +import build.wallet.coachmark.CoachmarkIdentifier import build.wallet.compose.collections.emptyImmutableList import build.wallet.compose.collections.immutableListOf import build.wallet.statemachine.core.list.ListModel @@ -35,6 +32,7 @@ import build.wallet.ui.components.amount.HeroAmount import build.wallet.ui.components.button.Button import build.wallet.ui.components.button.ButtonContentsList import build.wallet.ui.components.button.RowOfButtons +import build.wallet.ui.components.coachmark.CoachmarkPresenter import build.wallet.ui.components.header.Header import build.wallet.ui.components.icon.IconButton import build.wallet.ui.components.layout.Divider @@ -52,95 +50,124 @@ import build.wallet.ui.tooling.PreviewWalletTheme @Composable fun MoneyHomeScreen(model: MoneyHomeBodyModel) { val listState = rememberLazyListState() - Column { - Box( - modifier = - Modifier - .pullRefresh( - refreshing = model.isRefreshing, - onRefresh = model.onRefresh - ) - ) { - LazyColumn( + var coachmarkOffset by remember { mutableStateOf(Offset(0f, 0f)) } + + Box { + // Display a coachmark if needed + model.coachmark?.let { coachmarkModel -> + CoachmarkPresenter( + yOffset = coachmarkOffset.y, + model = coachmarkModel + ) + } + Column { + Box( modifier = Modifier - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - state = listState - ) { - // Header - item { - Row( - modifier = Modifier.padding(horizontal = 20.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Header( - headline = "Home", - headlineTopSpacing = 8.dp, - fillsMaxWidth = false - ) - Spacer(Modifier.weight(1F)) - ToolbarAccessory(model.trailingToolbarAccessoryModel) - } - Spacer(Modifier.height(48.dp)) - } - - // Balance + buttons - item { - with(model.balanceModel) { - HeroAmount( - modifier = Modifier.clickable( - interactionSource = MutableInteractionSource(), - indication = null, - onClick = { model.onHideBalance() } - ), - primaryAmount = AnnotatedString(primaryAmount), - secondaryAmountWithCurrency = secondaryAmount, - hideBalance = model.hideBalance + .pullRefresh( + refreshing = model.isRefreshing, + onRefresh = model.onRefresh ) + ) { + LazyColumn( + modifier = + Modifier + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + state = listState + ) { + // Header + item { + Row( + modifier = Modifier.padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Header( + headline = "Home", + headlineTopSpacing = 8.dp, + fillsMaxWidth = false + ) + Spacer(Modifier.weight(1F)) + ToolbarAccessory(model.trailingToolbarAccessoryModel) + } + Spacer(Modifier.height(48.dp)) } - Spacer(Modifier.height(4.dp)) - MoneyHomeButtons(model = model.buttonsModel) - Spacer(Modifier.height(40.dp)) - } - // No UI between the action buttons and the tx list so show a divider - if (model.cardsModel.cards.isEmpty() && model.transactionsModel != null) { + // Balance + buttons item { - Divider( - modifier = - Modifier - .padding(horizontal = 20.dp) - .padding(top = 16.dp) - ) + with(model.balanceModel) { + HeroAmount( + modifier = Modifier + .clickable( + interactionSource = MutableInteractionSource(), + indication = null, + onClick = { + model.onHideBalance() + // dismiss the HiddenBalanceCoachmark coachmark if it's showing since you've interacted with the feature + if (model.coachmark?.identifier == + CoachmarkIdentifier.HiddenBalanceCoachmark + ) { + model.coachmark?.dismiss?.invoke() + } + } + ).onGloballyPositioned { layoutCoordinates -> + if (model.coachmark?.identifier == CoachmarkIdentifier.HiddenBalanceCoachmark) { + val positionInParent = layoutCoordinates.positionInParent() + val size = layoutCoordinates.size + coachmarkOffset = Offset( + 0f, + positionInParent.y + size.height + ) + } + }, + primaryAmount = AnnotatedString(primaryAmount), + secondaryAmountWithCurrency = secondaryAmount, + hideBalance = model.hideBalance + ) + } + Spacer(Modifier.height(4.dp)) + MoneyHomeButtons(model = model.buttonsModel) + Spacer(Modifier.height(40.dp)) } - } - // Cards - items(model.cardsModel.cards) { cardModel -> - MoneyHomeCard( - modifier = Modifier.padding(horizontal = 20.dp), - model = cardModel - ) - Spacer(modifier = Modifier.height(24.dp)) - } + // No UI between the action buttons and the tx list so show a divider + if (model.cardsModel.cards.isEmpty() && model.transactionsModel != null) { + item { + Divider( + modifier = + Modifier + .padding(horizontal = 20.dp) + .padding(top = 16.dp) + ) + } + } - model.transactionsModel?.let { transactionsModel -> - item { - Transactions( - model = transactionsModel, - seeAllButtonModel = model.seeAllButtonModel, - hideValue = model.hideBalance + // Cards + items(model.cardsModel.cards) { cardModel -> + MoneyHomeCard( + modifier = Modifier.padding(horizontal = 20.dp), + model = cardModel ) + Spacer(modifier = Modifier.height(24.dp)) + } + + model.transactionsModel?.let { transactionsModel -> + item { + Transactions( + model = transactionsModel, + seeAllButtonModel = model.seeAllButtonModel, + hideValue = model.hideBalance + ) + } } } - } - PullRefreshIndicator( - modifier = Modifier.align(Alignment.TopCenter), - refreshing = model.isRefreshing, - onRefresh = model.onRefresh - ) + PullRefreshIndicator( + modifier = Modifier.align(Alignment.TopCenter), + refreshing = model.isRefreshing, + onRefresh = model.onRefresh + ) + } } } } @@ -314,6 +341,7 @@ internal fun MoneyHomeScreenFull(hideBalance: Boolean = false) { size = Size.Footer, onClick = StandardClick {} ), + coachmark = null, buttonsModel = MoneyHomeButtonsModel.MoneyMovementButtonsModel( sendButton = @@ -334,7 +362,8 @@ internal fun MoneyHomeScreenFull(hideBalance: Boolean = false) { ), refresh = {}, onRefresh = {}, - isRefreshing = false + isRefreshing = false, + badgedSettingsIcon = true ) ) } @@ -352,6 +381,7 @@ internal fun MoneyHomeScreenLite() { protectedCustomers = immutableListOf( ProtectedCustomer("", ProtectedCustomerAlias("Alice")) ), + badgedSettingsIcon = false, onProtectedCustomerClick = {}, onBuyOwnBitkeyClick = {}, onAcceptInviteClick = {} @@ -370,6 +400,7 @@ internal fun MoneyHomeScreenLiteWithoutProtectedCustomers() { onSettings = {}, buttonModel = MoneyHomeButtonsModel.SingleButtonModel(onSetUpBitkeyDevice = { }), protectedCustomers = immutableListOf(), + badgedSettingsIcon = true, onProtectedCustomerClick = {}, onBuyOwnBitkeyClick = {}, onAcceptInviteClick = {} diff --git a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/nfc/FwupInstructionsScreen.kt b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/nfc/FwupInstructionsScreen.kt index f190c7e71..a6d8a0692 100644 --- a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/nfc/FwupInstructionsScreen.kt +++ b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/nfc/FwupInstructionsScreen.kt @@ -102,7 +102,7 @@ internal fun FwupInstructionsScreenPreview() { buttonText = "Click me", onButtonClick = {}, eventTrackerScreenId = null, - eventTrackerScreenIdContext = null + eventTrackerContext = null ) ) } diff --git a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/settings/SettingsScreen.kt b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/settings/SettingsScreen.kt index 38afb8bae..d4f5c93b6 100644 --- a/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/settings/SettingsScreen.kt +++ b/app/android/ui/app/public/src/main/kotlin/build/wallet/ui/app/settings/SettingsScreen.kt @@ -72,7 +72,8 @@ private fun SettingsSection(model: SettingsBodyModel.SectionModel) { specialTrailingAccessory = rowModel.specialTrailingIconModel?.let { model -> ListItemAccessory.IconAccessory(model = model) }, - onClick = rowModel.onClick + onClick = rowModel.onClick, + showNewCoachmark = rowModel.showNewCoachmark ) Divider() } @@ -116,6 +117,12 @@ internal fun SettingsScreenPreview() { iconSize = Small, iconTint = IconTint.Warning ) + ) {}, + SettingsBodyModel.RowModel( + icon = Icon.SmallIconLock, + title = "App Security", + isDisabled = false, + showNewCoachmark = true ) {} ) ) diff --git a/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/ActivationInstructionsFormScreenSnapshots.kt b/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/ActivationInstructionsFormScreenSnapshots.kt index 35fe20ad2..dcfebe151 100644 --- a/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/ActivationInstructionsFormScreenSnapshots.kt +++ b/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/ActivationInstructionsFormScreenSnapshots.kt @@ -17,7 +17,7 @@ class ActivationInstructionsFormScreenSnapshots : FunSpec({ onBack = {}, onContinue = {}, isNavigatingBack = false, - eventTrackerScreenIdContext = PairHardwareEventTrackerScreenIdContext.ACCOUNT_CREATION + eventTrackerContext = PairHardwareEventTrackerScreenIdContext.ACCOUNT_CREATION ) ) } diff --git a/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/HardwareFingerprintEnrollmentScreenSnapshots.kt b/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/HardwareFingerprintEnrollmentScreenSnapshots.kt index 9aaa104cc..c53aa9940 100644 --- a/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/HardwareFingerprintEnrollmentScreenSnapshots.kt +++ b/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/account/HardwareFingerprintEnrollmentScreenSnapshots.kt @@ -21,12 +21,12 @@ class HardwareFingerprintEnrollmentScreenSnapshots : FunSpec({ onSaveFingerprint = {}, onErrorOverlayClosed = {}, isNavigatingBack = false, - eventTrackerScreenIdContext = PairHardwareEventTrackerScreenIdContext.ACCOUNT_CREATION, + eventTrackerContext = PairHardwareEventTrackerScreenIdContext.ACCOUNT_CREATION, presentationStyle = ScreenPresentationStyle.Root, headline = "Set up your first fingerprint", instructions = "Place your finger on the sensor until you see a blue light. Lift your" + - " finger and repeat (15-20 times) adjusting your finger position slightly each time," + - " until the light turns green. Then save your fingerprint.", + " finger and repeat (15-20 times) adjusting your finger position slightly each time," + + " until the light turns green. Then save your fingerprint." ) PairNewHardwareScreen( model = diff --git a/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/settings/device/DeviceSettingsScreenSnapshots.kt b/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/settings/device/DeviceSettingsScreenSnapshots.kt index 2c58c9c80..36c9ced3b 100644 --- a/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/settings/device/DeviceSettingsScreenSnapshots.kt +++ b/app/android/ui/app/public/src/test/kotlin/build/wallet/ui/app/settings/device/DeviceSettingsScreenSnapshots.kt @@ -31,10 +31,10 @@ class DeviceSettingsScreenSnapshots : FunSpec({ onUpdateVersion = {}, onSyncDeviceInfo = {}, onManageReplacement = {}, - multipleFingerprintsEnabled = false, resetDeviceEnabled = false, onManageFingerprints = {}, - onResetDevice = {} + onResetDevice = {}, + coachmark = null ) ) } @@ -61,10 +61,10 @@ class DeviceSettingsScreenSnapshots : FunSpec({ onUpdateVersion = {}, onSyncDeviceInfo = {}, onManageReplacement = {}, - multipleFingerprintsEnabled = false, resetDeviceEnabled = false, onManageFingerprints = {}, - onResetDevice = {} + onResetDevice = {}, + coachmark = null ) ) } @@ -91,10 +91,10 @@ class DeviceSettingsScreenSnapshots : FunSpec({ onUpdateVersion = {}, onSyncDeviceInfo = {}, onManageReplacement = {}, - multipleFingerprintsEnabled = false, resetDeviceEnabled = false, onManageFingerprints = {}, - onResetDevice = {} + onResetDevice = {}, + coachmark = null ) ) } @@ -121,10 +121,10 @@ class DeviceSettingsScreenSnapshots : FunSpec({ onUpdateVersion = {}, onSyncDeviceInfo = {}, onManageReplacement = {}, - multipleFingerprintsEnabled = false, resetDeviceEnabled = false, onManageFingerprints = {}, - onResetDevice = {} + onResetDevice = {}, + coachmark = null ) ) } @@ -151,10 +151,10 @@ class DeviceSettingsScreenSnapshots : FunSpec({ onUpdateVersion = {}, onSyncDeviceInfo = {}, onManageReplacement = {}, - multipleFingerprintsEnabled = false, resetDeviceEnabled = false, onManageFingerprints = {}, - onResetDevice = {} + onResetDevice = {}, + coachmark = null ) ) } diff --git a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - no update available.png b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - no update available.png index 79d15483c..af61ee566 100644 --- a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - no update available.png +++ b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - no update available.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64382cc86e56650b6c51ce5b89a7057570e0b2843685afeedd96f29a2edb1e1d -size 49689 +oid sha256:4ba1ea0e3dacfb4b25641fc40bdf7575886d6eddf9b46fe299509b31626aa953 +size 52096 diff --git a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending and update pending.png b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending and update pending.png index 6c58d2a9d..85143f47e 100644 --- a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending and update pending.png +++ b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending and update pending.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8e28e3c13731b9102947e50de794a3ff0048bb97478acc80bada38272d44dc6 -size 51474 +oid sha256:fb78b97b53c57a0daf060c7a5e8d23222ba6e5ac70e9cc91c9f7bb69017ed71c +size 53733 diff --git a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending.png b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending.png index 6c58d2a9d..85143f47e 100644 --- a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending.png +++ b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - replacement pending.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8e28e3c13731b9102947e50de794a3ff0048bb97478acc80bada38272d44dc6 -size 51474 +oid sha256:fb78b97b53c57a0daf060c7a5e8d23222ba6e5ac70e9cc91c9f7bb69017ed71c +size 53733 diff --git a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - update available.png b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - update available.png index d38fcaafc..b8bb8e3e4 100644 --- a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - update available.png +++ b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - update available.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c26526f8f53d5ef1d9971e850e7a2b0cb6e80f2ae96c1ab8dfa09d12d58a419 -size 54643 +oid sha256:dc4083f445f7c4f3a5ecef1e2d1512888ce86232f847fcc5d1d871978100e708 +size 56880 diff --git a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - when device is not found.png b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - when device is not found.png index 2d9a8ffa9..ef2dba896 100644 --- a/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - when device is not found.png +++ b/app/android/ui/app/public/src/test/snapshots/DeviceSettingsScreen/images/build.wallet.ui.app.settings.device_DeviceSettingsScreenSnapshots_device settings - when device is not found.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b75fc372d7e62246f19ca000c3aa3308bd29c9c4d1197221de175a6cde39ac3f -size 35148 +oid sha256:34ae4893536acb28a6b84d19ef03dc9083c1a88073912c5cdd52e19feb15d81e +size 37298 diff --git a/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Full with hidden balance.png b/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Full with hidden balance.png index 2b9500265..b6168e159 100644 --- a/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Full with hidden balance.png +++ b/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Full with hidden balance.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d59edad99a672f6b22d323c77fa6d3c78f7e68081e8b28eddd95382d0d72b026 -size 29079 +oid sha256:d43b33825c6e1cd7b1ceed9bd96a578c5d61ea28b36519f8e4530cadb0cf2aac +size 29257 diff --git a/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Lite without protecting wallets.png b/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Lite without protecting wallets.png index 4656fbd7a..20bafad31 100644 --- a/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Lite without protecting wallets.png +++ b/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_MoneyHome Screen Lite without protecting wallets.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38f1f30dea38df2760127298020b50582432adaf956bebe46f8588ee2ed5c540 -size 174189 +oid sha256:1df6b0af573768d8e44865e8d10732a1f315b78f7faac34beff4958a1576c0e4 +size 174256 diff --git a/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_moneyhome_screen_full.png b/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_moneyhome_screen_full.png index a86ff4287..a0bc43260 100644 --- a/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_moneyhome_screen_full.png +++ b/app/android/ui/app/public/src/test/snapshots/MoneyHomeScreen/images/build.wallet.ui.app.moneyhome_MoneyHomeScreenSnapshots_moneyhome_screen_full.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41eef5f12b6b5d48236a4bd29f585e350925da8a74b2d1d1323d533cbd5e9431 -size 37322 +oid sha256:118b7a411c4c9c482048fa023715ac55e376ed336be5124e28b30e5425988afc +size 37471 diff --git a/app/android/ui/app/public/src/test/snapshots/SettingsScreen/images/build.wallet.ui.app.settings_SettingsScreenSnapshots_settings screen.png b/app/android/ui/app/public/src/test/snapshots/SettingsScreen/images/build.wallet.ui.app.settings_SettingsScreenSnapshots_settings screen.png index 9fce10f70..b6595742a 100644 --- a/app/android/ui/app/public/src/test/snapshots/SettingsScreen/images/build.wallet.ui.app.settings_SettingsScreenSnapshots_settings screen.png +++ b/app/android/ui/app/public/src/test/snapshots/SettingsScreen/images/build.wallet.ui.app.settings_SettingsScreenSnapshots_settings screen.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5505eb389d520a1413a242a0aed9b0e95d0e295495332ff375768ec16f975f94 -size 19084 +oid sha256:8b8a2a4021238e8d25d8916ae2e88ad3b5a8441702e0d532bc44152e23ea5a8f +size 23050 diff --git a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/banner/BannerStyle.kt b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/banner/BannerStyle.kt index 7b2954203..a1dd8e0fb 100644 --- a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/banner/BannerStyle.kt +++ b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/banner/BannerStyle.kt @@ -21,7 +21,7 @@ internal fun WalletTheme.bannerStyle(treatment: BannerTreatment): BannerStyle { when (treatment) { Default -> colors.foreground Warning -> colors.warningForeground - Ready -> colors.primary + Ready -> colors.bitkeyPrimary }, backgroundColor = when (treatment) { diff --git a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/button/ButtonStyle.kt b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/button/ButtonStyle.kt index f6e8e89b6..d05e71929 100644 --- a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/button/ButtonStyle.kt +++ b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/button/ButtonStyle.kt @@ -152,7 +152,7 @@ private fun textColor(treatment: ButtonModel.Treatment): Color { Translucent, Translucent10 -> colors.translucentForeground TertiaryPrimary, TertiaryPrimaryNoUnderline, - -> colors.primary + -> colors.bitkeyPrimary White -> Color.Black Warning -> colors.warning @@ -179,7 +179,7 @@ private fun iconColor(treatment: ButtonModel.Treatment): Color { Translucent, Translucent10 -> colors.translucentForeground TertiaryPrimary, TertiaryPrimaryNoUnderline, - -> colors.primary + -> colors.bitkeyPrimary White -> Color.Black @@ -191,7 +191,7 @@ private fun iconColor(treatment: ButtonModel.Treatment): Color { @ReadOnlyComposable private fun ButtonModel.Treatment.normalBackgroundColor() = when (this) { - Primary -> colors.primary + Primary -> colors.bitkeyPrimary PrimaryDanger -> colors.dangerBackground Secondary, SecondaryDestructive -> colors.secondary PrimaryDestructive -> colors.destructive @@ -213,7 +213,7 @@ private fun ButtonModel.Treatment.normalBackgroundColor() = private fun ButtonModel.Treatment.disabledBackgroundColor() = when (this) { Primary -> - colors.primary.copy(alpha = 0.4F) + colors.bitkeyPrimary.copy(alpha = 0.4F) PrimaryDestructive -> colors.destructive.copy(alpha = 0.4F) PrimaryDanger, Secondary, SecondaryDestructive -> diff --git a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/Coachmark.kt b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/Coachmark.kt new file mode 100644 index 000000000..6b373113d --- /dev/null +++ b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/Coachmark.kt @@ -0,0 +1,197 @@ +package build.wallet.ui.components.coachmark + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import build.wallet.coachmark.CoachmarkIdentifier +import build.wallet.statemachine.core.Icon +import build.wallet.ui.components.button.Button +import build.wallet.ui.components.icon.IconButton +import build.wallet.ui.components.icon.IconImage +import build.wallet.ui.components.label.Label +import build.wallet.ui.components.label.LabelTreatment +import build.wallet.ui.model.StandardClick +import build.wallet.ui.model.button.ButtonModel +import build.wallet.ui.model.coachmark.CoachmarkModel +import build.wallet.ui.model.coachmark.NewCoachmarkTreatment +import build.wallet.ui.model.icon.IconModel +import build.wallet.ui.model.icon.IconSize +import build.wallet.ui.theme.WalletTheme +import build.wallet.ui.tokens.LabelType +import build.wallet.ui.tokens.painter +import build.wallet.ui.tooling.PreviewWalletTheme + +/** + * Popover-style coachmark + * @param model The model to use to display the coachmark. + * @param offset The offset to apply to the coachmark. + */ +@Composable +fun Coachmark( + model: CoachmarkModel, + offset: Offset, +) { + val density = LocalDensity.current + + Column( + modifier = Modifier + .padding(start = 16.dp, end = 16.dp) + .fillMaxWidth() + .offset(with(density) { offset.x.toDp() }, with(density) { offset.y.toDp() }) + ) { + // Top arrow if needed + if (model.arrowPosition.vertical == CoachmarkModel.ArrowPosition.Vertical.Top) { + Row( + modifier = Modifier + .offset(y = 1.dp) + .height(12.dp) + .align( + when (model.arrowPosition.horizontal) { + CoachmarkModel.ArrowPosition.Horizontal.Leading -> Alignment.Start + CoachmarkModel.ArrowPosition.Horizontal.Centered -> Alignment.CenterHorizontally + CoachmarkModel.ArrowPosition.Horizontal.Trailing -> Alignment.End + } + ).padding(start = 16.dp, end = 16.dp) + ) { + IconImage( + model = IconModel( + icon = Icon.CalloutArrow, + iconSize = IconSize.Small + ), + color = WalletTheme.colors.coachmarkBackground + ) + } + } + + // Coachmark body + Row( + modifier = Modifier + .fillMaxWidth() + ) { + Card( + shape = RoundedCornerShape(20.dp), + backgroundColor = WalletTheme.colors.coachmarkBackground, + modifier = Modifier + .shadow(elevation = 8.dp, RoundedCornerShape(12.dp), spotColor = Color(0x0A000000), ambientColor = Color(0x0A000000)) + .fillMaxWidth() + ) { + Column( + horizontalAlignment = Alignment.Start, + modifier = Modifier.padding(16.dp) + ) { + // Image + model.image?.let { + Image( + painter = it.painter(), + contentDescription = null + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Row { + // New label + NewCoachmark(NewCoachmarkTreatment.Dark) + + Spacer(modifier = Modifier.weight(1f)) + + // Close button + IconButton( + iconModel = IconModel( + icon = Icon.SmallIconXFilled, + iconSize = IconSize.Small + ), + color = Color.Gray, + onClick = { + model.dismiss() + } + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + // Title + Label( + text = model.title, + type = LabelType.Title2, + treatment = LabelTreatment.Unspecified, + color = Color.White + ) + // Description + Label( + text = model.description, + type = LabelType.Body3Regular, + treatment = LabelTreatment.Unspecified, + color = Color.White + ) + // Button + model.button?.let { + Spacer(modifier = Modifier.height(8.dp)) + Button(model = it) + } + } + } + } + // Bottom arrow if needed + if (model.arrowPosition.vertical == CoachmarkModel.ArrowPosition.Vertical.Bottom) { + Row( + modifier = Modifier + .offset(y = (-1).dp) + .height(12.dp) + .align( + when (model.arrowPosition.horizontal) { + CoachmarkModel.ArrowPosition.Horizontal.Leading -> Alignment.Start + CoachmarkModel.ArrowPosition.Horizontal.Centered -> Alignment.CenterHorizontally + CoachmarkModel.ArrowPosition.Horizontal.Trailing -> Alignment.End + } + ).padding(start = 16.dp, end = 16.dp) + ) { + IconImage( + model = IconModel( + icon = Icon.CalloutArrow, + iconSize = IconSize.Small + ), + modifier = Modifier.rotate(degrees = 180f), + color = WalletTheme.colors.coachmarkBackground + ) + } + } + } +} + +@Preview +@Composable +internal fun CoachmarkPreviews() { + PreviewWalletTheme { + Coachmark( + model = CoachmarkModel( + identifier = CoachmarkIdentifier.MultipleFingerprintsCoachmark, + title = "Multiple fingerprints", + description = "Now you can add more fingerprints to your Bitkey device.", + arrowPosition = CoachmarkModel.ArrowPosition( + vertical = CoachmarkModel.ArrowPosition.Vertical.Top, + horizontal = CoachmarkModel.ArrowPosition.Horizontal.Trailing + ), + button = ButtonModel( + text = "Add fingerprints", + size = ButtonModel.Size.Footer, + onClick = StandardClick {} + ), + image = null, + dismiss = {} + ), + offset = Offset(0f, 0f) + ) + } +} diff --git a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/CoachmarkPresenter.kt b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/CoachmarkPresenter.kt new file mode 100644 index 000000000..7e1e118ae --- /dev/null +++ b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/CoachmarkPresenter.kt @@ -0,0 +1,35 @@ +package build.wallet.ui.components.coachmark + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.zIndex +import build.wallet.ui.model.coachmark.CoachmarkModel + +/** + * Presenter composable to help display a popover coachmark (Coachmark.kt) + * @param yOffset The y offset to apply to the coachmark. + * @param model The model to use to display the coachmark. + */ +@Composable +fun CoachmarkPresenter( + yOffset: Float, + model: CoachmarkModel, +) { + Box( + modifier = + Modifier + .fillMaxSize() + .zIndex(Float.MAX_VALUE) + .background(Color.Transparent) + ) { + Coachmark( + model = model, + offset = Offset(0f, yOffset) + ) + } +} diff --git a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/NewCoachmark.kt b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/NewCoachmark.kt new file mode 100644 index 000000000..6c684345d --- /dev/null +++ b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/coachmark/NewCoachmark.kt @@ -0,0 +1,74 @@ +package build.wallet.ui.components.coachmark + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import build.wallet.android.ui.core.R +import build.wallet.ui.model.coachmark.NewCoachmarkTreatment +import build.wallet.ui.theme.WalletTheme +import build.wallet.ui.tooling.PreviewWalletTheme + +/** + * A new label that can be used to indicate new features or content. + * + * @param treatment The treatment to apply to the new label. + */ +@Composable +fun NewCoachmark(treatment: NewCoachmarkTreatment) { + Box( + modifier = Modifier + .clip(CircleShape) + .background( + when (treatment) { + NewCoachmarkTreatment.Light -> WalletTheme.colors.bitkeyPrimary.copy(0.10f) + NewCoachmarkTreatment.Dark -> WalletTheme.colors.bitkeyPrimary + NewCoachmarkTreatment.Disabled -> WalletTheme.colors.foreground30 + } + ).padding(horizontal = 8.dp, vertical = 3.dp) + ) { + Text( + text = "New", + style = TextStyle( + fontSize = 12.sp, + lineHeight = 18.sp, + fontFamily = FontFamily(Font(R.font.inter_semibold)), + fontWeight = FontWeight(600), + color = when (treatment) { + NewCoachmarkTreatment.Light -> WalletTheme.colors.bitkeyPrimary + NewCoachmarkTreatment.Dark -> Color.White + NewCoachmarkTreatment.Disabled -> Color.Gray + }, + textAlign = TextAlign.Center + ) + ) + } +} + +@Preview +@Composable +internal fun NewLabelPreviews() { + PreviewWalletTheme { + Box(modifier = Modifier.padding(16.dp)) { + Column { + NewCoachmark(NewCoachmarkTreatment.Light) + Spacer(Modifier.height(8.dp)) + NewCoachmark(NewCoachmarkTreatment.Dark) + Spacer(Modifier.height(8.dp)) + NewCoachmark(NewCoachmarkTreatment.Disabled) + } + } + } +} diff --git a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/forms/Picker.kt b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/forms/Picker.kt index c563d5cd4..7dd398cd3 100644 --- a/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/forms/Picker.kt +++ b/app/android/ui/core/public/src/main/kotlin/build/wallet/ui/components/forms/Picker.kt @@ -120,7 +120,7 @@ private fun