diff --git a/android/app/src/main/play_store_512.png b/android/app/src/main/play_store_512.png new file mode 100644 index 0000000..7bd35ec Binary files /dev/null and b/android/app/src/main/play_store_512.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 5a227c9..3aab6f7 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png index c109174..df498de 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index c0472c4..21db880 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png index c0472c4..21db880 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index aac2ae3..dd64c6f 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png index a81514a..674ec69 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index f145a1f..a3c3e02 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png index f145a1f..a3c3e02 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 668070f..72436da 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png index 11525d0..8483aa4 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index 51979ce..4ef447d 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png index 51979ce..4ef447d 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index e9bb9be..e229dd8 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png index 72ffb32..1abdb1f 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 1aaa0b7..b48a0fa 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png index 1aaa0b7..b48a0fa 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 2382269..acb11cd 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png index a4fe917..6def718 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 4f0ca06..2e598ad 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png index 4f0ca06..2e598ad 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/android/app/src/main/res/play_store_512.png b/android/app/src/main/res/play_store_512.png deleted file mode 100644 index 36286f0..0000000 Binary files a/android/app/src/main/res/play_store_512.png and /dev/null differ diff --git a/assets/velocity-animation.gif b/assets/velocity-animation.gif index 8f7a203..3637984 100644 Binary files a/assets/velocity-animation.gif and b/assets/velocity-animation.gif differ diff --git a/assets/velocity-icon-black.png b/assets/velocity-icon-black.png index 88f6093..a2f5e39 100644 Binary files a/assets/velocity-icon-black.png and b/assets/velocity-icon-black.png differ diff --git a/assets/velocity.png b/assets/velocity.png index ffa9f78..a62521e 100644 Binary files a/assets/velocity.png and b/assets/velocity.png differ diff --git a/lib/navbar.dart b/lib/navbar.dart index a26bd4c..fe6feb4 100644 --- a/lib/navbar.dart +++ b/lib/navbar.dart @@ -51,13 +51,17 @@ class _ScaffoldWithNavBarState extends State { Expanded( child: NavigationRail( extended: true, - leading: Padding( - padding: const EdgeInsets.symmetric( - vertical: 12.0, - ), - child: Image( - image: AssetImage('assets/velocity.png'), - width: 150, + leading: Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 12.0, + horizontal: 8 + ), + child: Image( + image: AssetImage('assets/velocity.png'), + width: 150, + ), ), ), labelType: NavigationRailLabelType.none, diff --git a/lib/screens/accounts/account_provider.dart b/lib/screens/accounts/account_provider.dart index d75578b..955c02a 100644 --- a/lib/screens/accounts/account_provider.dart +++ b/lib/screens/accounts/account_provider.dart @@ -59,6 +59,7 @@ class AccountProvider extends ChangeNotifier { notifyListeners(); return ApiResponse.failure(_accountError!); } catch (e, s) { + logger.e(e); logger.e(s); _isLoadingAccount = false; _accountError = 'An unexpected error occurred. Please try again.'; diff --git a/lib/screens/accounts/widgets/account_balance_widget.dart b/lib/screens/accounts/widgets/account_balance_widget.dart index a8eda20..3fe1aa8 100644 --- a/lib/screens/accounts/widgets/account_balance_widget.dart +++ b/lib/screens/accounts/widgets/account_balance_widget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:qpay/screens/accounts/account_provider.dart'; import 'package:qpay/screens/accounts/models/account_model.dart'; @@ -21,19 +22,21 @@ class AccountBalanceWidget extends StatefulWidget { class _AccountBalanceWidgetState extends State { late AccountProvider _accountProvider; + bool _isLoggedIn = false; + String? _phoneNumber; + AccountModel? _usdAccount; AccountModel? _zwgAccount; bool _isLoadingUsd = false; bool _isLoadingZwG = false; String? _usdError; String? _zwgError; - String? _phoneNumber; @override void initState() { super.initState(); _accountProvider = AccountProvider(); - _loadBalances(); + _loadAuthStateAndBalances(); } @override @@ -42,11 +45,21 @@ class _AccountBalanceWidgetState extends State { super.dispose(); } - Future _loadBalances() async { + Future _loadAuthStateAndBalances() async { final prefs = await SharedPreferences.getInstance(); + + // Determine login state from persisted token + if (prefs.getString("token") != null) { + setState(() { + _isLoggedIn = true; + }); + } + _phoneNumber = prefs.getString('phone'); - if (_phoneNumber == null || _phoneNumber!.isEmpty) return; + if (_phoneNumber == null || _phoneNumber!.isEmpty) { + return; + } setState(() { _isLoadingUsd = true; @@ -84,6 +97,67 @@ class _AccountBalanceWidgetState extends State { } }); } + + // If both fetches failed, mark as not logged in + if (mounted && + !usdResult.isSuccess && + !zwgResult.isSuccess && + _isLoggedIn) { + setState(() { + _isLoggedIn = false; + }); + } + } + + Future _refreshBalances() async { + final prefs = await SharedPreferences.getInstance(); + _phoneNumber = prefs.getString('phone'); + + if (_phoneNumber == null || _phoneNumber!.isEmpty) return; + + setState(() { + _isLoadingUsd = true; + _isLoadingZwG = true; + }); + + final usdResult = await _accountProvider.fetchAccount( + 'USD$_phoneNumber', + ); + if (mounted) { + setState(() { + _isLoadingUsd = false; + if (usdResult.isSuccess && usdResult.data != null) { + _usdAccount = usdResult.data; + _usdError = null; + } else { + _usdError = usdResult.error; + } + }); + } + + final zwgResult = await _accountProvider.fetchAccount( + 'ZWG$_phoneNumber', + ); + if (mounted) { + setState(() { + _isLoadingZwG = false; + if (zwgResult.isSuccess && zwgResult.data != null) { + _zwgAccount = zwgResult.data; + _zwgError = null; + } else { + _zwgError = zwgResult.error; + } + }); + } + + if (mounted && + !usdResult.isSuccess && + !zwgResult.isSuccess && + _isLoggedIn) { + setState(() { + _isLoggedIn = false; + }); + } } @override @@ -91,6 +165,10 @@ class _AccountBalanceWidgetState extends State { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; + if (!_isLoggedIn) { + return _buildLoginPrompt(theme, isDark); + } + if (_phoneNumber == null || _phoneNumber!.isEmpty) { return const SizedBox.shrink(); } @@ -143,7 +221,7 @@ class _AccountBalanceWidgetState extends State { onPressed: _isLoadingUsd || _isLoadingZwG ? null : () { - _loadBalances(); + _refreshBalances(); }, icon: Icon( Icons.refresh_rounded, @@ -176,6 +254,70 @@ class _AccountBalanceWidgetState extends State { ); } + // ────────────────────────────────────────────────────────────── + // Login Prompt + // ────────────────────────────────────────────────────────────── + Widget _buildLoginPrompt(ThemeData theme, bool isDark) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + theme.colorScheme.primary.withValues(alpha: 0.12), + theme.colorScheme.primary.withValues(alpha: 0.04), + ], + ), + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: theme.colorScheme.primary.withValues(alpha: 0.15), + width: 1, + ), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Register or Login for more features', + style: TextStyle( + fontSize: 14, + color: isDark ? Colors.white : Colors.black87, + ), + ), + ], + ), + ), + ElevatedButton( + onPressed: () { + context.push('/onboarding/landing'); + }, + style: ElevatedButton.styleFrom( + backgroundColor: theme.colorScheme.primary, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 0, + ), + child: const Text( + 'Get Started', + style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), + ), + ), + ], + ), + ), + ); + } + Widget _buildCompactCard({ required ThemeData theme, required bool isDark, diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 88aedc4..7cd6614 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -393,15 +393,10 @@ class _HomeScreenState extends State with TickerProviderStateMixin { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (!_isLoggedIn) ...[ - _buildLoginPrompt(theme, isDark), - const SizedBox(height: 20), - ], - if (_isLoggedIn) - AccountBalanceWidget( - selectedCurrency: _selectedCurrency, - onCurrencySelected: _onCurrencySelected, - ), + AccountBalanceWidget( + selectedCurrency: _selectedCurrency, + onCurrencySelected: _onCurrencySelected, + ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -669,67 +664,6 @@ class _HomeScreenState extends State with TickerProviderStateMixin { ); } - // ────────────────────────────────────────────────────────────── - // Login Prompt - // ────────────────────────────────────────────────────────────── - Widget _buildLoginPrompt(ThemeData theme, bool isDark) { - return Container( - width: double.infinity, - padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 18), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - theme.colorScheme.primary.withValues(alpha: 0.12), - theme.colorScheme.primary.withValues(alpha: 0.04), - ], - ), - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: theme.colorScheme.primary.withValues(alpha: 0.15), - width: 1, - ), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Register or Login for more features', - style: TextStyle( - fontSize: 14, - color: isDark ? Colors.white : Colors.black87, - ), - ), - ], - ), - ), - ElevatedButton( - onPressed: () { - context.push('/onboarding/landing'); - }, - style: ElevatedButton.styleFrom( - backgroundColor: theme.colorScheme.primary, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 0, - ), - child: const Text( - 'Get Started', - style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), - ), - ), - ], - ), - ); - } - // ────────────────────────────────────────────────────────────── // Logout Dialog // ────────────────────────────────────────────────────────────── diff --git a/web/apple-touch-icon.png b/web/apple-touch-icon.png new file mode 100644 index 0000000..3d3903e Binary files /dev/null and b/web/apple-touch-icon.png differ diff --git a/web/favicon.ico b/web/favicon.ico new file mode 100644 index 0000000..e25acc7 Binary files /dev/null and b/web/favicon.ico differ diff --git a/web/icon-192-maskable.png b/web/icon-192-maskable.png new file mode 100644 index 0000000..712e278 Binary files /dev/null and b/web/icon-192-maskable.png differ diff --git a/web/icon-192.png b/web/icon-192.png new file mode 100644 index 0000000..7f3ce9e Binary files /dev/null and b/web/icon-192.png differ diff --git a/web/icon-512-maskable.png b/web/icon-512-maskable.png new file mode 100644 index 0000000..7bd35ec Binary files /dev/null and b/web/icon-512-maskable.png differ diff --git a/web/icon-512.png b/web/icon-512.png new file mode 100644 index 0000000..b58141d Binary files /dev/null and b/web/icon-512.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png deleted file mode 100644 index a806105..0000000 Binary files a/web/icons/Icon-192.png and /dev/null differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png deleted file mode 100644 index 3a93ad2..0000000 Binary files a/web/icons/Icon-512.png and /dev/null differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png deleted file mode 100644 index a806105..0000000 Binary files a/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png deleted file mode 100644 index 3a93ad2..0000000 Binary files a/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/web/index.html b/web/index.html index 3d83ff6..ec3ea3c 100644 --- a/web/index.html +++ b/web/index.html @@ -14,6 +14,14 @@ This is a placeholder for base href that will be replaced by the value of the `--base-href` argument provided to `flutter build`. --> + + + + + @@ -26,12 +34,11 @@ - - - + + - Velocity + Velocity Pay