diff --git a/lib/features/ark/ui/receive_page.dart b/lib/features/ark/ui/receive_page.dart index ae00e6f38..2fe03939a 100644 --- a/lib/features/ark/ui/receive_page.dart +++ b/lib/features/ark/ui/receive_page.dart @@ -56,10 +56,9 @@ class _ReceivePageState extends State { ), const Gap(16), ReceiveQR( - qrData: - _selectedOption! == context.loc.arkReceiveSegmentBoarding - ? btcAddress - : arkAddress, + qrData: _selectedOption! == context.loc.arkReceiveSegmentBoarding + ? btcAddress + : arkAddress, ), const Gap(16), ArkCopyAddressSection( @@ -87,13 +86,16 @@ class ReceiveQR extends StatelessWidget { padding: const EdgeInsets.all(16), constraints: const BoxConstraints(maxHeight: 300, maxWidth: 300), decoration: BoxDecoration( - color: context.appColors.onPrimary, + color: context.appColors.background, borderRadius: BorderRadius.circular(12), ), - child: - qrData.isNotEmpty - ? QrImageView(data: qrData) - : const LoadingBoxContent(height: 200), + child: qrData.isNotEmpty + ? QrImageView( + data: qrData, + // ignore: deprecated_member_use + foregroundColor: context.appColors.secondary, + ) + : const LoadingBoxContent(height: 200), ), ); } @@ -120,12 +122,12 @@ class _ArkCopyAddressSectionState extends State { Widget build(BuildContext context) { final currentAddress = widget.selectedOption == context.loc.arkReceiveSegmentBoarding - ? widget.btcAddress - : widget.arkAddress; + ? widget.btcAddress + : widget.arkAddress; final addressLabel = widget.selectedOption == context.loc.arkReceiveSegmentBoarding - ? context.loc.arkReceiveBoardingAddress - : context.loc.arkArkAddress; + ? context.loc.arkReceiveBoardingAddress + : context.loc.arkArkAddress; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), @@ -140,13 +142,12 @@ class _ArkCopyAddressSectionState extends State { overflow: .ellipsis, canShowValueModal: true, modalTitle: addressLabel, - modalContent: - currentAddress - .replaceAllMapped( - RegExp('.{1,4}'), - (match) => '${match.group(0)} ', - ) - .trim(), + modalContent: currentAddress + .replaceAllMapped( + RegExp('.{1,4}'), + (match) => '${match.group(0)} ', + ) + .trim(), ), ], ), diff --git a/lib/features/fund_exchange/ui/screens/fund_exchange_canada_post_screen.dart b/lib/features/fund_exchange/ui/screens/fund_exchange_canada_post_screen.dart index 321c30b82..0c2a8beaf 100644 --- a/lib/features/fund_exchange/ui/screens/fund_exchange_canada_post_screen.dart +++ b/lib/features/fund_exchange/ui/screens/fund_exchange_canada_post_screen.dart @@ -1,4 +1,5 @@ import 'package:bb_mobile/core/exchange/domain/entity/funding_details.dart'; +import 'package:bb_mobile/core/themes/app_theme.dart'; import 'package:bb_mobile/core/utils/build_context_x.dart'; import 'package:bb_mobile/core/widgets/loading/loading_box_content.dart'; import 'package:bb_mobile/core/widgets/text/text.dart'; @@ -75,7 +76,19 @@ class FundExchangeCanadaPostScreen extends StatelessWidget { if (details?.code == null) const LoadingBoxContent(height: 250.0, width: 250.0) else - QrImageView(size: 250, data: details!.code), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.appColors.background, + borderRadius: BorderRadius.circular(12), + ), + child: QrImageView( + size: 250, + data: details!.code, + // ignore: deprecated_member_use + foregroundColor: context.appColors.secondary, + ), + ), const Gap(24.0), ], ), diff --git a/lib/features/import_coldcard_q/import_coldcard_q_page.dart b/lib/features/import_coldcard_q/import_coldcard_q_page.dart index ddf852e63..c8f160bea 100644 --- a/lib/features/import_coldcard_q/import_coldcard_q_page.dart +++ b/lib/features/import_coldcard_q/import_coldcard_q_page.dart @@ -36,18 +36,16 @@ class ImportColdcardQPage extends StatelessWidget { Assets.misc.qRPlaceholder.path, height: 200, width: 200, - color: context.appColors.text, ), Gap(Device.screen.height * 0.05), Column( children: [ BBButton.small( label: context.loc.importColdcardButtonOpenCamera, - onPressed: - () => context.pushNamed( - ImportWatchOnlyWalletRoutes.scan.name, - extra: SignerDeviceEntity.coldcardQ, - ), + onPressed: () => context.pushNamed( + ImportWatchOnlyWalletRoutes.scan.name, + extra: SignerDeviceEntity.coldcardQ, + ), bgColor: context.appColors.surface, textColor: context.appColors.text, outlined: true, @@ -56,8 +54,8 @@ class ImportColdcardQPage extends StatelessWidget { Gap(Device.screen.height * 0.02), BBButton.small( label: context.loc.importColdcardButtonInstructions, - onPressed: - () => ColdcardQInstructionsBottomSheet.show(context), + onPressed: () => + ColdcardQInstructionsBottomSheet.show(context), bgColor: context.appColors.surface, textColor: context.appColors.text, outlined: true, @@ -65,12 +63,9 @@ class ImportColdcardQPage extends StatelessWidget { Gap(Device.screen.height * 0.02), BBButton.small( label: context.loc.importColdcardButtonPurchase, - onPressed: - () => launchUrl( - Uri.parse( - 'https://store.coinkite.com/promo/BULLBITCOIN', - ), - ), + onPressed: () => launchUrl( + Uri.parse('https://store.coinkite.com/promo/BULLBITCOIN'), + ), bgColor: context.appColors.surface, textColor: context.appColors.text, outlined: true, diff --git a/lib/features/import_qr_device/import_qr_device_page.dart b/lib/features/import_qr_device/import_qr_device_page.dart index 13a8450ea..d0463451d 100644 --- a/lib/features/import_qr_device/import_qr_device_page.dart +++ b/lib/features/import_qr_device/import_qr_device_page.dart @@ -56,7 +56,6 @@ class ImportQrDevicePage extends StatelessWidget { Assets.misc.qRPlaceholder.path, height: 200, width: 200, - color: context.appColors.text, ), Gap(Device.screen.height * 0.05), Column( diff --git a/lib/features/import_wallet/import_wallet_page.dart b/lib/features/import_wallet/import_wallet_page.dart index 7a86ce704..4bd2522c2 100644 --- a/lib/features/import_wallet/import_wallet_page.dart +++ b/lib/features/import_wallet/import_wallet_page.dart @@ -94,19 +94,18 @@ class ImportWalletPage extends StatelessWidget { context.pushNamed(ImportQrDeviceRoute.importPassport.name), ), const Gap(16), + TabMenuVerticalButton( + title: context.loc.importWalletKeystone, + onTap: () => + context.pushNamed(ImportQrDeviceRoute.importKeystone.name), + ), + const Gap(16), if (context.read().state.isSuperuser ?? false) ...[ TabMenuVerticalButton( title: context.loc.importWalletLedger, onTap: () => context.pushNamed(LedgerRoute.importLedger.name), ), const Gap(16), - TabMenuVerticalButton( - title: context.loc.importWalletKeystone, - onTap: () => context.pushNamed( - ImportQrDeviceRoute.importKeystone.name, - ), - ), - const Gap(16), TabMenuVerticalButton( title: 'BitBox', onTap: () => Platform.isAndroid diff --git a/lib/features/pay/ui/widgets/pay_qr_bottom_sheet.dart b/lib/features/pay/ui/widgets/pay_qr_bottom_sheet.dart index c74b04583..fd24a154c 100644 --- a/lib/features/pay/ui/widgets/pay_qr_bottom_sheet.dart +++ b/lib/features/pay/ui/widgets/pay_qr_bottom_sheet.dart @@ -60,7 +60,7 @@ class PayQrBottomSheet extends StatelessWidget { child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( - color: context.appColors.surfaceFixed, + color: context.appColors.background, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( @@ -73,7 +73,12 @@ class PayQrBottomSheet extends StatelessWidget { child: SizedBox( width: 280, height: 280, - child: QrImageView(data: bip21InvoiceData, size: 280), + child: QrImageView( + data: bip21InvoiceData, + size: 280, + // ignore: deprecated_member_use + foregroundColor: context.appColors.secondary, + ), ), ), ), diff --git a/lib/features/psbt_flow/show_animated_qr/show_animated_qr_widget.dart b/lib/features/psbt_flow/show_animated_qr/show_animated_qr_widget.dart index 4aa3fe0fb..82f5eb89e 100644 --- a/lib/features/psbt_flow/show_animated_qr/show_animated_qr_widget.dart +++ b/lib/features/psbt_flow/show_animated_qr/show_animated_qr_widget.dart @@ -113,13 +113,17 @@ class _ShowAnimatedQrViewState extends State<_ShowAnimatedQrView> { return Container( decoration: BoxDecoration( - color: context.appColors.surfaceFixed, + color: context.appColors.background, borderRadius: BorderRadius.circular(16), ), child: Column( mainAxisAlignment: .start, children: [ - QrImageView(data: state.parts[state.currentIndex]), + QrImageView( + data: state.parts[state.currentIndex], + // ignore: deprecated_member_use + foregroundColor: context.appColors.secondary, + ), const Gap(16), if (widget.showSlider) ...[ Padding( diff --git a/lib/features/receive/ui/screens/receive_qr_screen.dart b/lib/features/receive/ui/screens/receive_qr_screen.dart index d6fc915c7..ea39d58a3 100644 --- a/lib/features/receive/ui/screens/receive_qr_screen.dart +++ b/lib/features/receive/ui/screens/receive_qr_screen.dart @@ -145,11 +145,15 @@ class ReceiveQRDetails extends StatelessWidget { padding: const EdgeInsets.all(16), constraints: const BoxConstraints(maxHeight: 300, maxWidth: 300), decoration: BoxDecoration( - color: context.appColors.onPrimary, + color: context.appColors.background, borderRadius: BorderRadius.circular(12), ), child: qrData.isNotEmpty - ? QrImageView(data: qrData) + ? QrImageView( + data: qrData, + // ignore: deprecated_member_use + foregroundColor: context.appColors.secondary, + ) : const LoadingBoxContent(height: 200), ), ), @@ -371,7 +375,7 @@ class ReceiveLnInfoDetails extends StatelessWidget { margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( - color: context.appColors.onPrimary, + color: context.appColors.cardBackground, borderRadius: BorderRadius.circular(2), border: Border.all(color: context.appColors.surface), boxShadow: [ @@ -407,6 +411,7 @@ class ReceiveLnInfoDetails extends StatelessWidget { amountSat ?? 0, showFiat: false, style: context.font.bodyMedium, + color: context.appColors.secondary, ), BBText( '~$amountEquivalent', @@ -435,6 +440,7 @@ class ReceiveLnInfoDetails extends StatelessWidget { swap!.receieveAmount!, showFiat: false, style: context.font.bodyMedium, + color: context.appColors.secondary, ), ], ), @@ -456,6 +462,7 @@ class ReceiveLnInfoDetails extends StatelessWidget { child: BBText( note.isNotEmpty ? note : '', style: context.font.bodyMedium, + color: context.appColors.secondary, maxLines: 5, textAlign: .end, ), @@ -488,7 +495,12 @@ class ReceiveLnSwapID extends StatelessWidget { color: context.appColors.onSurfaceVariant, ), const Spacer(), - BBText(swap.id, style: context.font.bodyLarge, textAlign: .end), + BBText( + swap.id, + style: context.font.bodyLarge, + color: context.appColors.secondary, + textAlign: .end, + ), const Gap(4), InkWell( child: Icon(Icons.copy, color: context.appColors.primary, size: 16), @@ -527,7 +539,7 @@ class _ReceiveLnFeesDetailsState extends State { amt, showFiat: false, style: context.font.bodySmall, - color: context.appColors.onSurfaceVariant, + color: context.appColors.secondary, ), ], ), @@ -564,7 +576,7 @@ class _ReceiveLnFeesDetailsState extends State { swap.fees?.totalFees(null) ?? 0, showFiat: false, style: context.font.bodyLarge, - color: context.appColors.onSurfaceVariant, + color: context.appColors.secondary, ), const Gap(4), Icon( diff --git a/lib/features/sell/ui/widgets/sell_qr_bottom_sheet.dart b/lib/features/sell/ui/widgets/sell_qr_bottom_sheet.dart index ea4d99647..136ad1cfa 100644 --- a/lib/features/sell/ui/widgets/sell_qr_bottom_sheet.dart +++ b/lib/features/sell/ui/widgets/sell_qr_bottom_sheet.dart @@ -60,7 +60,7 @@ class SellQrBottomSheet extends StatelessWidget { child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( - color: context.appColors.surfaceFixed, + color: context.appColors.background, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( @@ -73,7 +73,12 @@ class SellQrBottomSheet extends StatelessWidget { child: SizedBox( width: 280, height: 280, - child: QrImageView(data: bip21InvoiceData, size: 280), + child: QrImageView( + data: bip21InvoiceData, + size: 280, + // ignore: deprecated_member_use + foregroundColor: context.appColors.secondary, + ), ), ), ), diff --git a/test/migrations_test/bull_database/schema_v10_to_v11_test.dart b/test/migrations_test/bull_database/schema_v10_to_v11_test.dart new file mode 100644 index 000000000..d2be64e5c --- /dev/null +++ b/test/migrations_test/bull_database/schema_v10_to_v11_test.dart @@ -0,0 +1,185 @@ +import 'package:bb_mobile/core/storage/sqlite_database.dart'; +import 'package:drift/drift.dart'; +import 'package:drift_dev/api/migrations_native.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'generated/schema.dart'; +import 'generated/schema_v10.dart' as v10; +import 'generated/schema_v11.dart' as v11; + +void main() { + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + late SchemaVerifier verifier; + + setUpAll(() => verifier = SchemaVerifier(GeneratedHelper())); + + group('v10 to v11: auto_swap triggerBalanceSats', () { + test('with existing auto_swap data', () async { + final schema = await verifier.schemaAt(10); + + final oldDb = v10.DatabaseAtV10(schema.newConnection()); + + await oldDb + .into(oldDb.autoSwap) + .insert( + v10.AutoSwapCompanion.insert( + enabled: const Value(true), + balanceThresholdSats: 1000000, + feeThresholdPercent: 0.5, + ), + ); + + await oldDb.close(); + + final db = SqliteDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 11); + await db.close(); + + final migratedDb = v11.DatabaseAtV11(schema.newConnection()); + + final allAutoSwaps = await migratedDb.select(migratedDb.autoSwap).get(); + expect(allAutoSwaps.length, 1); + + final autoSwap = allAutoSwaps.first; + expect(autoSwap.enabled, 1); + expect(autoSwap.balanceThresholdSats, 1000000); + expect(autoSwap.triggerBalanceSats, 2000000); + expect(autoSwap.feeThresholdPercent, 0.5); + expect(autoSwap.showWarning, 1); + + await migratedDb.close(); + }); + + test('with multiple auto_swap records', () async { + final schema = await verifier.schemaAt(10); + + final oldDb = v10.DatabaseAtV10(schema.newConnection()); + + await oldDb + .into(oldDb.autoSwap) + .insert( + v10.AutoSwapCompanion.insert( + enabled: const Value(true), + balanceThresholdSats: 500000, + feeThresholdPercent: 0.3, + ), + ); + + await oldDb + .into(oldDb.autoSwap) + .insert( + v10.AutoSwapCompanion.insert( + enabled: const Value(false), + balanceThresholdSats: 2000000, + feeThresholdPercent: 1.0, + ), + ); + + await oldDb.close(); + + final db = SqliteDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 11); + await db.close(); + + final migratedDb = v11.DatabaseAtV11(schema.newConnection()); + + final allAutoSwaps = await migratedDb.select(migratedDb.autoSwap).get(); + expect(allAutoSwaps.length, 2); + + final autoSwap1 = allAutoSwaps[0]; + expect(autoSwap1.balanceThresholdSats, 500000); + expect(autoSwap1.triggerBalanceSats, 1000000); + + final autoSwap2 = allAutoSwaps[1]; + expect(autoSwap2.balanceThresholdSats, 2000000); + expect(autoSwap2.triggerBalanceSats, 4000000); + + await migratedDb.close(); + }); + + test('with empty auto_swap table', () async { + final schema = await verifier.schemaAt(10); + + final oldDb = v10.DatabaseAtV10(schema.newConnection()); + await oldDb.close(); + + final db = SqliteDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 11); + await db.close(); + + final migratedDb = v11.DatabaseAtV11(schema.newConnection()); + final allAutoSwaps = await migratedDb.select(migratedDb.autoSwap).get(); + expect(allAutoSwaps.length, 0); + await migratedDb.close(); + }); + }); + + group('v10 to v11: prices table', () { + test('prices table created', () async { + final schema = await verifier.schemaAt(10); + + final db = SqliteDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 11); + await db.close(); + + final migratedDb = v11.DatabaseAtV11(schema.newConnection()); + + expect(await migratedDb.select(migratedDb.prices).get(), []); + await migratedDb.close(); + }); + }); + + group('v10 to v11: settings themeMode', () { + test('themeMode column added with default', () async { + final schema = await verifier.schemaAt(10); + + final oldDb = v10.DatabaseAtV10(schema.newConnection()); + await oldDb + .into(oldDb.settings) + .insert( + v10.SettingsCompanion.insert( + environment: 'mainnet', + bitcoinUnit: 'sats', + language: 'en', + currency: 'USD', + hideAmounts: false, + isSuperuser: false, + ), + ); + await oldDb.close(); + + final db = SqliteDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 11); + await db.close(); + + final migratedDb = v11.DatabaseAtV11(schema.newConnection()); + final settings = await migratedDb.select(migratedDb.settings).getSingle(); + expect(settings.themeMode, 'system'); + await migratedDb.close(); + }); + }); + + group('v10 to v11: mempool tables', () { + test('mempool tables created and seeded', () async { + final schema = await verifier.schemaAt(10); + + final db = SqliteDatabase(schema.newConnection()); + await verifier.migrateAndValidate(db, 11); + await db.close(); + + final migratedDb = v11.DatabaseAtV11(schema.newConnection()); + + final mempoolServers = await migratedDb + .select(migratedDb.mempoolServers) + .get(); + expect(mempoolServers.length, 4); + + final mempoolSettings = await migratedDb + .select(migratedDb.mempoolSettings) + .get(); + expect(mempoolSettings.length, 4); + + await migratedDb.close(); + }); + }); +}