diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c547b8b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,137 @@ +name: Rust-Flutter CI + +on: + pull_request: + merge_group: + +jobs: + rust-checks: + name: Rust Checks + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./rust + steps: + - uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --all -- --check + + - name: Run clippy + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Build check + run: cargo check --all-targets --all-features + + - name: Run tests + run: cargo test --all-features + + flutter-checks: + name: Flutter Checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.29.2' + channel: 'stable' + + - name: Install dependencies + run: flutter pub get + + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed . + + build-android: + name: Build Android APK + needs: [ rust-checks, flutter-checks ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Settup flutter rust bridge + run: cargo install flutter_rust_bridge_codegen + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.29.2' + channel: 'stable' + + - uses: kuhnroyal/flutter-fvm-config-action/setup@v3 + + - name: Make .env file for flutter build + uses: SpicyPizza/create-envfile@v2.0 + with: + envkey_ESPLORA_URL: "http://localhost:30000" + envkey_ARK_SERVER_URL: "http://localhost:7070" + envkey_ARK_NETWORK: "regtest" + + - name: Install dependencies + run: flutter pub get + + - name: Build rust library + run: flutter_rust_bridge_codegen generate + + - name: Build APK + run: flutter build apk --debug + + build-ios: + name: Build iOS + needs: [ rust-checks, flutter-checks ] + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Settup flutter rust bridge + run: cargo install flutter_rust_bridge_codegen + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.29.2' + channel: 'stable' + + - uses: kuhnroyal/flutter-fvm-config-action/setup@v3 + + - name: Make .env file for flutter build + uses: SpicyPizza/create-envfile@v2.0 + with: + envkey_ESPLORA_URL: "http://localhost:30000" + envkey_ARK_SERVER_URL: "http://localhost:7070" + envkey_ARK_NETWORK: "regtest" + + - name: Install dependencies + run: flutter pub get + + - name: Build rust library + run: flutter_rust_bridge_codegen generate + + - name: Build iOS + run: flutter build ios --debug --no-codesign diff --git a/justfile b/justfile index e0bb789..a8b2290 100644 --- a/justfile +++ b/justfile @@ -23,3 +23,6 @@ clippy: run: flutter run + +flutter-fmt: + dart format --output=write . diff --git a/lib/main.dart b/lib/main.dart index 2300943..1691694 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -51,7 +51,7 @@ Future determineStartScreen() async { final network = await _settingsService.getNetwork(); logger.i( - "Running on ${network} against ark server ${arkServerUrl} and esplora ${esploraUrl}"); + "Running on $network against ark server $arkServerUrl and esplora $esploraUrl"); if (exists) { logger.i("Wallet found, setting up client"); diff --git a/lib/src/ui/screens/dashboard_screen.dart b/lib/src/ui/screens/dashboard_screen.dart index e35f4a6..a8cc1d8 100644 --- a/lib/src/ui/screens/dashboard_screen.dart +++ b/lib/src/ui/screens/dashboard_screen.dart @@ -1,6 +1,5 @@ import 'package:ark_flutter/src/ui/screens/transaction_history_widget.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:ark_flutter/src/logger/logger.dart'; import 'package:ark_flutter/src/ui/screens/settings_screen.dart'; import 'package:ark_flutter/src/ui/screens/send_screen.dart'; @@ -13,15 +12,15 @@ class DashboardScreen extends StatefulWidget { final String aspId; const DashboardScreen({ - Key? key, + super.key, required this.aspId, - }) : super(key: key); + }); @override - _DashboardScreenState createState() => _DashboardScreenState(); + DashboardScreenState createState() => DashboardScreenState(); } -class _DashboardScreenState extends State { +class DashboardScreenState extends State { bool _isBalanceLoading = true; bool _isTransactionFetching = true; String? _balanceError; @@ -278,7 +277,7 @@ class _DashboardScreenState extends State { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: Colors.amber.withOpacity(0.3), + color: Colors.amber.withAlpha((0.3 * 255).round()), blurRadius: 8, offset: const Offset(0, 3), ), @@ -310,9 +309,9 @@ class _DashboardScreenState extends State { ), const SizedBox(height: 8), if (_balanceError != null) - Text( + const Text( 'Error loading balance', - style: const TextStyle( + style: TextStyle( color: Colors.red, fontSize: 16, fontWeight: FontWeight.bold, @@ -392,7 +391,7 @@ class _DashboardScreenState extends State { height: 32, width: 150, decoration: BoxDecoration( - color: Colors.white.withOpacity(0.2), + color: Colors.white.withAlpha((0.2 * 255).round()), borderRadius: BorderRadius.circular(4), ), ), @@ -401,7 +400,7 @@ class _DashboardScreenState extends State { height: 16, width: 100, decoration: BoxDecoration( - color: Colors.white.withOpacity(0.15), + color: Colors.white.withAlpha((0.15 * 255).round()), borderRadius: BorderRadius.circular(4), ), ), diff --git a/lib/src/ui/screens/onboarding_screen.dart b/lib/src/ui/screens/onboarding_screen.dart index c3cf303..e229f65 100644 --- a/lib/src/ui/screens/onboarding_screen.dart +++ b/lib/src/ui/screens/onboarding_screen.dart @@ -9,10 +9,10 @@ class OnboardingScreen extends StatefulWidget { const OnboardingScreen({super.key}); @override - _OnboardingScreenState createState() => _OnboardingScreenState(); + OnboardingScreenState createState() => OnboardingScreenState(); } -class _OnboardingScreenState extends State { +class OnboardingScreenState extends State { String? _selectedOption; final TextEditingController _secretKeyController = TextEditingController(); bool _isLoading = false; @@ -356,7 +356,7 @@ class _OnboardingScreenState extends State { style: TextStyle( fontSize: 14, color: isSelected - ? Colors.black.withOpacity(0.7) + ? Colors.white.withAlpha((0.7 * 255).round()) : Colors.white70, ), ), diff --git a/lib/src/ui/screens/receive_screen.dart b/lib/src/ui/screens/receive_screen.dart index 8428deb..da11767 100644 --- a/lib/src/ui/screens/receive_screen.dart +++ b/lib/src/ui/screens/receive_screen.dart @@ -1,13 +1,12 @@ import 'package:ark_flutter/src/rust/api/ark_api.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/rendering.dart'; import 'package:ark_flutter/src/logger/logger.dart'; +import 'package:flutter/services.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; import 'dart:async'; import 'dart:io'; -import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:path_provider/path_provider.dart'; @@ -15,16 +14,15 @@ class ReceiveScreen extends StatefulWidget { final String aspId; const ReceiveScreen({ - Key? key, + super.key, required this.aspId, - }) : super(key: key); + }); @override - _ReceiveScreenState createState() => _ReceiveScreenState(); + ReceiveScreenState createState() => ReceiveScreenState(); } -class _ReceiveScreenState extends State { - bool _isLoading = true; +class ReceiveScreenState extends State { String? _error; String _bip21Address = ""; @@ -34,14 +32,14 @@ class _ReceiveScreenState extends State { bool _showCopyMenu = false; // Track which addresses have been copied (for showing checkmarks) - Map _copiedAddresses = { + final Map _copiedAddresses = { 'BIP21': false, 'BTC': false, 'Ark': false, }; // Timers for resetting the checkmarks - Map _checkmarkTimers = { + final Map _checkmarkTimers = { 'BIP21': null, 'BTC': null, 'Ark': null, @@ -54,9 +52,6 @@ class _ReceiveScreenState extends State { } Future _fetchAddresses() async { - setState(() { - _isLoading = true; - }); try { final addresses = await address(); setState(() { @@ -69,11 +64,7 @@ class _ReceiveScreenState extends State { setState(() { _error = e.toString(); }); - } finally { - setState(() { - _isLoading = false; - }); - } + } finally {} } @override @@ -121,7 +112,7 @@ class _ReceiveScreenState extends State { logger.i("Copied $type address: $address"); } - GlobalKey _qrKey = GlobalKey(); + final GlobalKey _qrKey = GlobalKey(); Future _handleShare() async { try { diff --git a/lib/src/ui/screens/send_screen.dart b/lib/src/ui/screens/send_screen.dart index 8f46432..1a2a003 100644 --- a/lib/src/ui/screens/send_screen.dart +++ b/lib/src/ui/screens/send_screen.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:ark_flutter/src/logger/logger.dart'; import 'package:ark_flutter/src/ui/screens/sign_transaction_screen.dart'; @@ -8,16 +7,16 @@ class SendScreen extends StatefulWidget { final double availableSats; const SendScreen({ - Key? key, + super.key, required this.aspId, required this.availableSats, - }) : super(key: key); + }); @override - _SendScreenState createState() => _SendScreenState(); + SendScreenState createState() => SendScreenState(); } -class _SendScreenState extends State { +class SendScreenState extends State { final TextEditingController _addressController = TextEditingController(); final TextEditingController _amountController = TextEditingController(); double _usdAmount = 0.0; diff --git a/lib/src/ui/screens/settings_screen.dart b/lib/src/ui/screens/settings_screen.dart index a647c07..2bc89f6 100644 --- a/lib/src/ui/screens/settings_screen.dart +++ b/lib/src/ui/screens/settings_screen.dart @@ -1,5 +1,4 @@ import 'package:ark_flutter/src/rust/api/ark_api.dart'; -import 'package:ark_flutter/src/ui/screens/onboarding_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:ark_flutter/src/logger/logger.dart'; @@ -11,15 +10,15 @@ class SettingsScreen extends StatefulWidget { final String aspId; const SettingsScreen({ - Key? key, + super.key, required this.aspId, - }) : super(key: key); + }); @override - _SettingsScreenState createState() => _SettingsScreenState(); + SettingsScreenState createState() => SettingsScreenState(); } -class _SettingsScreenState extends State { +class SettingsScreenState extends State { String _nsec = 'Unknown'; Info? _info; String _selectedNetwork = 'Regtest'; @@ -496,7 +495,7 @@ class _SettingsScreenState extends State { color: Colors.red), onTap: _showResetWalletDialog, ), - ], borderColor: Colors.red.withOpacity(0.3)), + ], borderColor: Colors.red.withAlpha((0.3 * 255).round())), const SizedBox(height: 40), ], @@ -551,7 +550,7 @@ class _SettingsScreenState extends State { } else { return child; } - }).toList(), + }), ], ), ); diff --git a/lib/src/ui/screens/sign_transaction_screen.dart b/lib/src/ui/screens/sign_transaction_screen.dart index e20d522..493e458 100644 --- a/lib/src/ui/screens/sign_transaction_screen.dart +++ b/lib/src/ui/screens/sign_transaction_screen.dart @@ -9,17 +9,17 @@ class SignTransactionScreen extends StatefulWidget { final double amount; const SignTransactionScreen({ - Key? key, + super.key, required this.aspId, required this.address, required this.amount, - }) : super(key: key); + }); @override - _SignTransactionScreenState createState() => _SignTransactionScreenState(); + SignTransactionScreenState createState() => SignTransactionScreenState(); } -class _SignTransactionScreenState extends State { +class SignTransactionScreenState extends State { bool _isLoading = false; void _handleSign() async { diff --git a/lib/src/ui/screens/transaction_details_dialog.dart b/lib/src/ui/screens/transaction_details_dialog.dart index 1bb56b8..2654ac5 100644 --- a/lib/src/ui/screens/transaction_details_dialog.dart +++ b/lib/src/ui/screens/transaction_details_dialog.dart @@ -13,14 +13,14 @@ class TransactionDetailsDialog extends StatelessWidget { final String dialogTitle; const TransactionDetailsDialog({ - Key? key, + super.key, required this.dialogTitle, required this.txid, required this.createdAt, this.confirmedAt, required this.amountSats, required this.isSettled, - }) : super(key: key); + }); Future _handleSettlement(BuildContext context) async { try { @@ -173,7 +173,7 @@ class TransactionDetailsDialog extends StatelessWidget { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.amber.withOpacity(0.2), + color: Colors.amber..withAlpha((0.2 * 255).round()), borderRadius: BorderRadius.circular(8), ), child: const Text( diff --git a/lib/src/ui/screens/transaction_history_widget.dart b/lib/src/ui/screens/transaction_history_widget.dart index a5ae6d3..fe9e49c 100644 --- a/lib/src/ui/screens/transaction_history_widget.dart +++ b/lib/src/ui/screens/transaction_history_widget.dart @@ -9,18 +9,18 @@ class TransactionHistoryWidget extends StatefulWidget { final bool loading; const TransactionHistoryWidget({ - Key? key, + super.key, required this.aspId, required this.transactions, required this.loading, - }) : super(key: key); + }); @override - _TransactionHistoryWidgetState createState() => - _TransactionHistoryWidgetState(); + TransactionHistoryWidgetState createState() => + TransactionHistoryWidgetState(); } -class _TransactionHistoryWidgetState extends State { +class TransactionHistoryWidgetState extends State { String? _error; @override diff --git a/lib/src/ui/screens/transaction_success_screen.dart b/lib/src/ui/screens/transaction_success_screen.dart index 5bd366b..2859dac 100644 --- a/lib/src/ui/screens/transaction_success_screen.dart +++ b/lib/src/ui/screens/transaction_success_screen.dart @@ -6,10 +6,10 @@ class TransactionSuccessScreen extends StatelessWidget { final double amount; const TransactionSuccessScreen({ - Key? key, + super.key, required this.aspId, required this.amount, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -48,7 +48,8 @@ class TransactionSuccessScreen extends StatelessWidget { borderRadius: BorderRadius.circular(4), boxShadow: [ BoxShadow( - color: const Color(0xFF7E71F0).withOpacity(0.5), + color: const Color(0xFF7E71F0) + .withAlpha((0.5 * 255).round()), blurRadius: 10, offset: const Offset(0, 3), ),