From 88828e6a3802617e9c91626fc8efd35051eff78e Mon Sep 17 00:00:00 2001 From: Andrzej Chmielewski Date: Mon, 24 Jan 2022 19:03:12 +0100 Subject: [PATCH] feat: Starport template - quality improvements (#198) --- .../cosmos_bottom_sheet_container.dart | 28 ++-- packages/cosmos_utils/lib/extensions.dart | 11 ++ starport_template/README.md | 2 +- .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 19 +-- .../app/src/profile/AndroidManifest.xml | 7 - starport_template/lib/main.dart | 36 +++-- .../lib/pages/assets_portfolio_page.dart | 2 +- .../lib/pages/create_wallet_page.dart | 2 +- .../lib/pages/onboarding_page.dart | 4 +- .../lib/pages/passcode_prompt_page.dart | 36 ++--- .../password_setup_sheet.dart | 0 .../lib/pages/receive_money_sheet.dart | 97 +++++++++++++ .../lib/pages/security_sheet.dart | 11 +- .../lib/pages/transaction_history_page.dart | 2 +- .../lib/pages/wallet_details_page.dart | 136 ------------------ starport_template/lib/starport_app.dart | 2 + .../lib/stores/settings_store.dart | 23 ++- .../lib/widgets/balance_card_list.dart | 7 +- .../lib/widgets/receive_money_sheet.dart | 91 ------------ 20 files changed, 223 insertions(+), 300 deletions(-) delete mode 100644 starport_template/android/app/src/debug/AndroidManifest.xml delete mode 100644 starport_template/android/app/src/profile/AndroidManifest.xml rename starport_template/lib/{widgets => pages}/password_setup_sheet.dart (100%) create mode 100644 starport_template/lib/pages/receive_money_sheet.dart delete mode 100644 starport_template/lib/pages/wallet_details_page.dart delete mode 100644 starport_template/lib/widgets/receive_money_sheet.dart diff --git a/packages/cosmos_ui_components/lib/components/cosmos_bottom_sheet_container.dart b/packages/cosmos_ui_components/lib/components/cosmos_bottom_sheet_container.dart index c981f94a..c9204298 100644 --- a/packages/cosmos_ui_components/lib/components/cosmos_bottom_sheet_container.dart +++ b/packages/cosmos_ui_components/lib/components/cosmos_bottom_sheet_container.dart @@ -14,21 +14,25 @@ class CosmosBottomSheetContainer extends StatelessWidget { final screenHeight = MediaQuery.of(context).size.height; final maxHeight = screenHeight - (screenHeight / 10); final theme = CosmosTheme.of(context); - return ConstrainedBox( - constraints: BoxConstraints( - maxHeight: maxHeight, - ), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.vertical( - top: Radius.elliptical( - theme.radiusXL, - theme.radiusXL * 1.1, + return MediaQuery.removePadding( + context: context, + removeTop: true, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: maxHeight, + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical( + top: Radius.elliptical( + theme.radiusXL, + theme.radiusXL * 1.1, + ), ), + color: theme.colors.background, ), - color: theme.colors.background, + child: child, ), - child: child, ), ); } diff --git a/packages/cosmos_utils/lib/extensions.dart b/packages/cosmos_utils/lib/extensions.dart index c699bb07..2c9b1d33 100644 --- a/packages/cosmos_utils/lib/extensions.dart +++ b/packages/cosmos_utils/lib/extensions.dart @@ -12,6 +12,17 @@ extension IterableExtensions on Iterable { .map((key, value) => MapEntry(key, mapper(key, value))) .values; } + + T? firstOrNull({required bool Function(T) where}) { + try { + return firstWhere( + (it) => where(it), + ); + // ignore: avoid_catching_errors + } on StateError { + return null; + } + } } /// Extensions for the Either, Future and MobX combinations diff --git a/starport_template/README.md b/starport_template/README.md index cea7817d..4ed7f1f4 100644 --- a/starport_template/README.md +++ b/starport_template/README.md @@ -11,6 +11,6 @@ - If you're running the app on real device instead of emulator/simulator or want to taget a specific blockchain run on a remote machine, make sure to specify proper urls and ports when running the app. Here is an example for running the app on cosmos hub testnet: ``` -flutter run --dart-define=BASE_LCD_URL=api.testnet.cosmos.network --dart-define=PORT=443 +flutter run --dart-define=LCD_URL=https://api.testnet.cosmos.network --dart-define=LCD_PORT=443 --dart-define=GRPC_URL=https://grpc.testnet.cosmos.network --dart-define=GRPC_PORT=443 ``` diff --git a/starport_template/android/app/src/debug/AndroidManifest.xml b/starport_template/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 132bccf3..00000000 --- a/starport_template/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/starport_template/android/app/src/main/AndroidManifest.xml b/starport_template/android/app/src/main/AndroidManifest.xml index a98b9d82..fd8c475d 100644 --- a/starport_template/android/app/src/main/AndroidManifest.xml +++ b/starport_template/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ - + + + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" + /> + android:name="io.flutter.embedding.android.SplashScreenDrawable" + android:resource="@drawable/launch_background" + /> @@ -36,6 +37,6 @@ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> + android:value="2"/> diff --git a/starport_template/android/app/src/profile/AndroidManifest.xml b/starport_template/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 132bccf3..00000000 --- a/starport_template/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/starport_template/lib/main.dart b/starport_template/lib/main.dart index 7723a274..4906e0d3 100644 --- a/starport_template/lib/main.dart +++ b/starport_template/lib/main.dart @@ -1,6 +1,6 @@ -import 'dart:io'; - import 'package:alan/alan.dart'; +import 'package:cosmos_auth/cosmos_auth.dart'; +import 'package:cosmos_utils/cosmos_utils.dart'; import 'package:flutter/material.dart'; import 'package:starport_template/starport_app.dart'; import 'package:starport_template/stores/settings_store.dart'; @@ -16,11 +16,9 @@ void main() { } void _buildDependencies() { - StarportApp.networkInfo = NetworkInfo( - bech32Hrp: 'cosmos', - lcdInfo: LCDInfo(host: Platform.isAndroid ? 'http://10.0.2.2' : 'http://localhost'), - grpcInfo: GRPCInfo(host: Platform.isAndroid ? 'http://10.0.2.2' : 'http://localhost'), - ); + StarportApp.baseEnv = BaseEnv(); + StarportApp.networkInfo = StarportApp.baseEnv.networkInfo; + _logBackendInfo(StarportApp.networkInfo); StarportApp.secureDataStore = FlutterSecureStorageDataStore(); StarportApp.signingGateway = TransactionSigningGateway( @@ -37,8 +35,28 @@ void _buildDependencies() { plainDataStore: SharedPrefsPlainDataStore(), ), ); - StarportApp.baseEnv = BaseEnv(); + + StarportApp.cosmosAuth = CosmosAuth(); StarportApp.walletsStore = WalletsStore(StarportApp.signingGateway, StarportApp.baseEnv); - StarportApp.settingsStore = SettingsStore(); + StarportApp.settingsStore = SettingsStore(StarportApp.cosmosAuth, StarportApp.secureDataStore); StarportApp.transactionsStore = TransactionsStore(StarportApp.baseEnv); } + +void _logBackendInfo(NetworkInfo networkInfo) => debugLog( + ''' + Starting app with following info: + + bech32Hrp:\t${networkInfo.bech32Hrp} + + LCD : { + host:\t\t${networkInfo.lcdInfo.host} + port:\t\t${networkInfo.lcdInfo.port} + fullUrl:\t${networkInfo.lcdInfo.fullUrl} + } + + GRPC: { + host:\t\t${networkInfo.grpcInfo.host} + port:\t\t${networkInfo.grpcInfo.port} + } +''', + ); diff --git a/starport_template/lib/pages/assets_portfolio_page.dart b/starport_template/lib/pages/assets_portfolio_page.dart index d55011c2..2ab1cc46 100644 --- a/starport_template/lib/pages/assets_portfolio_page.dart +++ b/starport_template/lib/pages/assets_portfolio_page.dart @@ -7,13 +7,13 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:starport_template/entities/balance.dart'; +import 'package:starport_template/pages/receive_money_sheet.dart'; import 'package:starport_template/pages/select_asset_page.dart'; import 'package:starport_template/pages/transaction_history_page.dart'; import 'package:starport_template/pages/wallets_list_sheet.dart'; import 'package:starport_template/starport_app.dart'; import 'package:starport_template/widgets/asset_portfolio_heading.dart'; import 'package:starport_template/widgets/balance_card_list.dart'; -import 'package:starport_template/widgets/receive_money_sheet.dart'; import 'package:starport_template/widgets/starport_button_bar.dart'; import 'package:transaction_signing_gateway/model/wallet_public_info.dart'; diff --git a/starport_template/lib/pages/create_wallet_page.dart b/starport_template/lib/pages/create_wallet_page.dart index 3c9c1668..17bd0a2c 100644 --- a/starport_template/lib/pages/create_wallet_page.dart +++ b/starport_template/lib/pages/create_wallet_page.dart @@ -127,7 +127,7 @@ class _CreateWalletPageState extends State { } Future _authenticateUser() async { - final result = await CosmosAuth().biometricAuthenticate(); + final result = await StarportApp.cosmosAuth.biometricAuthenticate(); setState( () => _isAuthenticated = result.fold( (fail) => fail.type == LocalAuthFailureType.noBiometrics, diff --git a/starport_template/lib/pages/onboarding_page.dart b/starport_template/lib/pages/onboarding_page.dart index 4e3c6202..4e414312 100644 --- a/starport_template/lib/pages/onboarding_page.dart +++ b/starport_template/lib/pages/onboarding_page.dart @@ -42,11 +42,9 @@ class _OnboardingPageState extends State { right: theme.spacingL, ), child: Column( + mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - SizedBox( - height: MediaQuery.of(context).size.width + 24, - ), Text( 'Welcome to your first blockchain-powered mobile app.', style: Theme.of(context).textTheme.headline2, diff --git a/starport_template/lib/pages/passcode_prompt_page.dart b/starport_template/lib/pages/passcode_prompt_page.dart index 300bfa20..3f1538b3 100644 --- a/starport_template/lib/pages/passcode_prompt_page.dart +++ b/starport_template/lib/pages/passcode_prompt_page.dart @@ -1,4 +1,3 @@ -import 'package:cosmos_auth/auth/cosmos_auth.dart'; import 'package:cosmos_ui_components/cosmos_ui_components.dart'; import 'package:cosmos_utils/extensions.dart'; import 'package:flutter/foundation.dart'; @@ -17,17 +16,12 @@ class PasswordPromptPage extends StatefulWidget { final String? message; static Future promptPassword(BuildContext context, {String? message}) async { - return (await CosmosAuth().hasPassword( - secureDataStore: StarportApp.secureDataStore, - id: PasswordPromptPage.starportPassId, - )) - .fold( - (l) => null, - (hasPassword) => _showPage( - context, - setUpPasscode: !hasPassword, - message: hasPassword ? message : 'Provide passcode that we will use to secure your wallet data', - ), + final hasPassword = await StarportApp.settingsStore.hasPasswordSet(); + // ignore: use_build_context_synchronously + return _showPage( + context, + setUpPasscode: !hasPassword, + message: hasPassword ? message : 'Provide passcode that we will use to secure your wallet data', ); } @@ -64,6 +58,10 @@ class _PasswordPromptPageState extends State { bool get _arePasscodesValid => _passcodes.length == 2 && _passcodes[0] == _passcodes[1]; + int _attempts = 0; + + ValueKey get _promptKey => ValueKey(_attempts); + @override void initState() { super.initState(); @@ -89,7 +87,7 @@ class _PasswordPromptPageState extends State { return Scaffold( body: SafeArea( child: CosmosPasscodePrompt( - key: ValueKey(_title), + key: _promptKey, title: _title, message: widget.message, onSubmit: widget.setUpPasscode ? _onSetUpPasscodeSubmit : _onPasscodeSubmit, @@ -99,6 +97,7 @@ class _PasswordPromptPageState extends State { } void _onSetUpPasscodeSubmit(String value) { + _attempts++; setState(() => _passcodes.add(value)); if (_passcodes.length == 2) { if (_arePasscodesValid) { @@ -110,17 +109,20 @@ class _PasswordPromptPageState extends State { } Future _onPasscodeSubmit(String value) async { - final password = await CosmosAuth().readPassword( + final password = await StarportApp.cosmosAuth.readPassword( secureDataStore: StarportApp.secureDataStore, id: PasswordPromptPage.starportPassId, useBiometrics: false, ); - if (password.getOrElse(() => '') == value) { + _attempts++; + if (password.getOrElse(() => value) == value) { if (mounted) { Navigator.of(context).pop(value); } } else { + _passcodes.clear(); _showPasscodeError(); + setState(() {}); } } @@ -134,7 +136,7 @@ class _PasswordPromptPageState extends State { } void _savePasscode() { - CosmosAuth().savePassword( + StarportApp.cosmosAuth.savePassword( secureDataStore: StarportApp.secureDataStore, id: PasswordPromptPage.starportPassId, password: _passcodes[0], @@ -143,7 +145,7 @@ class _PasswordPromptPageState extends State { } Future _runBiometricsCheck() async { - final cosmosAuth = CosmosAuth(); + final cosmosAuth = StarportApp.cosmosAuth; final availableBiometrics = await cosmosAuth.getAvailableBiometrics().asyncFold( (fail) => [], (types) => types, diff --git a/starport_template/lib/widgets/password_setup_sheet.dart b/starport_template/lib/pages/password_setup_sheet.dart similarity index 100% rename from starport_template/lib/widgets/password_setup_sheet.dart rename to starport_template/lib/pages/password_setup_sheet.dart diff --git a/starport_template/lib/pages/receive_money_sheet.dart b/starport_template/lib/pages/receive_money_sheet.dart new file mode 100644 index 00000000..a44fa45b --- /dev/null +++ b/starport_template/lib/pages/receive_money_sheet.dart @@ -0,0 +1,97 @@ +import 'package:clipboard/clipboard.dart'; +import 'package:cosmos_ui_components/components/cosmos_bottom_sheet_container.dart'; +import 'package:cosmos_ui_components/components/cosmos_bottom_sheet_header.dart'; +import 'package:cosmos_ui_components/components/cosmos_text_button.dart'; +import 'package:cosmos_ui_components/components/gradient_avatar.dart'; +import 'package:cosmos_ui_components/cosmos_text_theme.dart'; +import 'package:cosmos_ui_components/cosmos_theme.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:transaction_signing_gateway/model/wallet_public_info.dart'; + +class ReceiveMoneySheet extends StatelessWidget { + const ReceiveMoneySheet({ + required this.walletInfo, + Key? key, + }) : super(key: key); + + final WalletPublicInfo walletInfo; + + String get walletAddress => walletInfo.publicAddress; + + @override + Widget build(BuildContext context) { + final theme = CosmosTheme.of(context); + return CosmosBottomSheetContainer( + child: SafeArea( + child: Column( + children: [ + CosmosBottomSheetHeader( + title: '', + titleTextStyle: CosmosTextTheme.title2Bold, + leading: const Icon(Icons.ten_k, color: Colors.transparent), + actions: [CosmosTextButton(text: 'Close', onTap: () => Navigator.of(context).pop())], + ), + QrImage( + data: walletInfo.publicAddress, + size: MediaQuery.of(context).size.height / 3.515, + ), + SizedBox(height: theme.spacingXXL), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 35, + child: GradientAvatar(stringKey: walletInfo.publicAddress), + ), + SizedBox(width: theme.spacingL), + Text( + walletInfo.name, + style: CosmosTextTheme.title1Medium, + ) + ], + ), + SizedBox(height: theme.spacingL), + Text(maskAddress(walletAddress), style: CosmosTextTheme.title1Medium), + const Spacer(), + CosmosTextButton( + text: 'Share', + onTap: _onTapShare, + textStyle: CosmosTextTheme.elevatedButton.copyWith(color: theme.colors.link), + suffixIcon: Image.asset('assets/images/share.png'), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: theme.spacingL), + child: Row( + children: [ + Expanded( + child: ElevatedButton( + onPressed: _onTapCopyAddress, + child: const Text('Copy address'), + ), + ) + ], + ), + ), + ], + ), + ), + ); + } + + void _onTapCopyAddress() => FlutterClipboard.copy(walletAddress); + + void _onTapShare() => Share.share(walletAddress); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('walletInfo', walletInfo)) + ..add(StringProperty('walletAddress', walletAddress)); + } +} + +String maskAddress(String address) => '${address.substring(0, 9)}...${address.substring(address.length - 4)}'; diff --git a/starport_template/lib/pages/security_sheet.dart b/starport_template/lib/pages/security_sheet.dart index 78e18048..f31e5efb 100644 --- a/starport_template/lib/pages/security_sheet.dart +++ b/starport_template/lib/pages/security_sheet.dart @@ -6,6 +6,7 @@ import 'package:cosmos_ui_components/cosmos_text_theme.dart'; import 'package:cosmos_ui_components/cosmos_ui_components.dart'; import 'package:cosmos_utils/extensions.dart'; import 'package:flutter/material.dart'; +import 'package:starport_template/pages/passcode_prompt_page.dart'; import 'package:starport_template/starport_app.dart'; import 'package:starport_template/stores/settings_store.dart'; @@ -74,7 +75,13 @@ class _SecuritySheetState extends State { void _setBiometricsEnabled(bool checked) => setState(() => _settingsStore.biometricsEnabled = checked); - void _setAppLockEnabled(bool checked) => setState(() => _settingsStore.appLockEnabled = checked); + Future _setAppLockEnabled(bool checked) async { + if (checked && !await _settingsStore.hasPasswordSet()) { + final result = await PasswordPromptPage.promptPassword(context); + setState(() => _settingsStore.appLockEnabled = result != null); + } + setState(() => _settingsStore.appLockEnabled = checked); + } String biometricsTitle() { if (Platform.isIOS) { @@ -96,7 +103,7 @@ class _SecuritySheetState extends State { } Future _getAvailableBiometrics() async { - _biometricTypes = await CosmosAuth().getAvailableBiometrics().asyncFold( + _biometricTypes = await StarportApp.cosmosAuth.getAvailableBiometrics().asyncFold( (fail) => [], (success) => success, ); diff --git a/starport_template/lib/pages/transaction_history_page.dart b/starport_template/lib/pages/transaction_history_page.dart index 3ec0fd97..a01349c9 100644 --- a/starport_template/lib/pages/transaction_history_page.dart +++ b/starport_template/lib/pages/transaction_history_page.dart @@ -7,11 +7,11 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:starport_template/entities/transaction_history_item.dart'; import 'package:starport_template/entities/wallet_additional_data.dart'; +import 'package:starport_template/pages/receive_money_sheet.dart'; import 'package:starport_template/pages/settings_sheet.dart'; import 'package:starport_template/pages/wallets_list_sheet.dart'; import 'package:starport_template/starport_app.dart'; import 'package:starport_template/widgets/asset_portfolio_heading.dart'; -import 'package:starport_template/widgets/receive_money_sheet.dart'; import 'package:starport_template/widgets/transaction_history_list.dart'; import 'package:transaction_signing_gateway/transaction_signing_gateway.dart'; diff --git a/starport_template/lib/pages/wallet_details_page.dart b/starport_template/lib/pages/wallet_details_page.dart deleted file mode 100644 index 15270aa6..00000000 --- a/starport_template/lib/pages/wallet_details_page.dart +++ /dev/null @@ -1,136 +0,0 @@ -import 'package:cosmos_ui_components/components/content_state_switcher.dart'; -import 'package:cosmos_ui_components/components/template/cosmos_balance_card.dart'; -import 'package:cosmos_ui_components/components/template/cosmos_balance_heading.dart'; -import 'package:cosmos_ui_components/components/template/cosmos_wallets_list_view.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:mobx/mobx.dart'; -import 'package:starport_template/entities/balance.dart'; -import 'package:starport_template/starport_app.dart'; - -// TODO: Remove this aftert [AssetsPortfolioPage] is finalized - -class WalletDetailsPage extends StatefulWidget { - const WalletDetailsPage({ - required this.walletInfo, - Key? key, - }) : super(key: key); - - final WalletInfo walletInfo; - - @override - State createState() => _WalletDetailsPageState(); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('walletInfo', walletInfo)); - } -} - -class _WalletDetailsPageState extends State { - ObservableList get balancesList => StarportApp.walletsStore.balancesList; - - bool get isBalancesLoading => StarportApp.walletsStore.isBalancesLoading; - - bool get isSendMoneyLoading => StarportApp.walletsStore.isSendMoneyLoading; - - bool get isError => StarportApp.walletsStore.isBalancesLoadingError; - - @override - void initState() { - super.initState(); - _fetchWalletBalances(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: Center( - child: Observer( - builder: (context) => ContentStateSwitcher( - contentChild: Column( - children: [ - ListTile( - title: const Text('Wallet address'), - subtitle: Text(widget.walletInfo.address), - ), - const Divider(), - const Padding(padding: EdgeInsets.only(top: 16)), - const BalanceHeading(), - Padding( - padding: const EdgeInsets.all(8), - child: Column( - children: balancesList - .map( - (balance) => CosmosBalanceCard( - denomText: balance.denom.text, - amountDisplayText: balance.amount.value.toString(), - secondaryText: 'available ${balance.denom.text.toUpperCase()}', - ), - ) - .toList(), - ), - ), - if (isSendMoneyLoading) - const Padding( - padding: EdgeInsets.only(top: 8), - child: Center( - child: Text( - 'Sending money', - textAlign: TextAlign.center, - ), - ), - ), - ], - ), - isLoading: isBalancesLoading, - isError: isError, - errorChild: const Center( - child: Text('An unexpected error occurred'), - ), - ), - ), - ), - ), - ); - } - - Future _fetchWalletBalances() async { - await StarportApp.walletsStore.getBalances(widget.walletInfo.address); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('isBalancesLoading', isBalancesLoading)) - ..add(IterableProperty('balancesList', balancesList)) - ..add(DiagnosticsProperty('isSendMoneyLoading', isSendMoneyLoading)) - ..add(DiagnosticsProperty('isError', isError)); - } - -// TODO: Remove these functions after [AssetsPortfolioPage] is finalized - -// void _transferPressed(Balance balance) { -// final denom = Denom(balance.denom.text); -// _openSendMoneySheet(denom); -// } - -// Future _openSendMoneySheet(Denom denom) async { -// final result = await showModalBottomSheet( -// context: context, -// builder: (context) => SafeArea( -// child: SendMoneySheet( -// denom: denom, -// walletInfo: widget.walletInfo, -// ), -// ), -// ); -// if (result == true) { -// StarportApp.walletsStore.getBalances(widget.walletInfo.address); -// } -// } -} diff --git a/starport_template/lib/starport_app.dart b/starport_template/lib/starport_app.dart index 91c9488f..7b66e428 100644 --- a/starport_template/lib/starport_app.dart +++ b/starport_template/lib/starport_app.dart @@ -1,4 +1,5 @@ import 'package:alan/alan.dart'; +import 'package:cosmos_auth/auth/cosmos_auth.dart'; import 'package:cosmos_ui_components/cosmos_theme.dart'; import 'package:cosmos_utils/cosmos_utils.dart'; import 'package:flutter/material.dart'; @@ -21,6 +22,7 @@ class StarportApp extends StatelessWidget { static late NetworkInfo networkInfo; static late SecureDataStore secureDataStore; static late SettingsStore settingsStore; + static late CosmosAuth cosmosAuth; @override Widget build(BuildContext context) { diff --git a/starport_template/lib/stores/settings_store.dart b/starport_template/lib/stores/settings_store.dart index fbc5e158..69ee2f0c 100644 --- a/starport_template/lib/stores/settings_store.dart +++ b/starport_template/lib/stores/settings_store.dart @@ -1,12 +1,20 @@ +import 'package:cosmos_auth/auth/cosmos_auth.dart'; +import 'package:cosmos_utils/cosmos_utils.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:starport_template/pages/passcode_prompt_page.dart'; class SettingsStore { - SettingsStore(); + SettingsStore( + this._cosmosAuth, + this._secureDataStore, + ); static const _appLockKey = 'settings_app_lock'; static const _biometricsKey = 'settings_biometrics'; + final CosmosAuth _cosmosAuth; + final SecureDataStore _secureDataStore; final Observable _appLockEnabled = Observable(false); final Observable _biometricsEnabled = Observable(false); @@ -31,9 +39,22 @@ class SettingsStore { Future init() async { final prefs = await SharedPreferences.getInstance(); appLockEnabled = prefs.getBool(_appLockKey) ?? false; + if (!(await hasPasswordSet())) { + appLockEnabled = false; + } biometricsEnabled = prefs.getBool(_biometricsKey) ?? false; _isInitialized = true; } + + Future hasPasswordSet() => _cosmosAuth + .hasPassword( + secureDataStore: _secureDataStore, + id: PasswordPromptPage.starportPassId, + ) + .asyncFold( + (fail) => false, + (success) => success, + ); } Future _saveBoolInPrefs(String key, bool value) async { diff --git a/starport_template/lib/widgets/balance_card_list.dart b/starport_template/lib/widgets/balance_card_list.dart index ca057297..e28629f7 100644 --- a/starport_template/lib/widgets/balance_card_list.dart +++ b/starport_template/lib/widgets/balance_card_list.dart @@ -24,8 +24,11 @@ class BalanceCardList extends StatelessWidget { children: [ CosmosBalanceCard( denomText: balance.denom.text.toUpperCase(), - amountDisplayText: formatAmount(balance.amount.value.toDouble()), - secondaryText: 'available ${balance.denom.text.toUpperCase()}', + amountDisplayText: formatAmount( + balance.amount.value.toDouble(), + symbol: '', + ), + secondaryText: balance.denom.text.toUpperCase(), onTap: onTapItem == null ? null : () => onTapItem!(balance), ), SizedBox(height: CosmosTheme.of(context).spacingL), diff --git a/starport_template/lib/widgets/receive_money_sheet.dart b/starport_template/lib/widgets/receive_money_sheet.dart deleted file mode 100644 index 4e72f140..00000000 --- a/starport_template/lib/widgets/receive_money_sheet.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:clipboard/clipboard.dart'; -import 'package:cosmos_ui_components/components/cosmos_bottom_sheet_container.dart'; -import 'package:cosmos_ui_components/components/cosmos_bottom_sheet_header.dart'; -import 'package:cosmos_ui_components/components/cosmos_text_button.dart'; -import 'package:cosmos_ui_components/components/gradient_avatar.dart'; -import 'package:cosmos_ui_components/components/minimal_bottom_spacer.dart'; -import 'package:cosmos_ui_components/cosmos_text_theme.dart'; -import 'package:cosmos_ui_components/cosmos_theme.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:qr_flutter/qr_flutter.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:transaction_signing_gateway/model/wallet_public_info.dart'; - -class ReceiveMoneySheet extends StatelessWidget { - const ReceiveMoneySheet({ - required this.walletInfo, - Key? key, - }) : super(key: key); - - final WalletPublicInfo walletInfo; - - String get walletAddress => walletInfo.publicAddress; - - @override - Widget build(BuildContext context) { - final theme = CosmosTheme.of(context); - return CosmosBottomSheetContainer( - child: Column( - children: [ - CosmosBottomSheetHeader( - title: '', - titleTextStyle: CosmosTextTheme.title2Bold, - leading: const Icon(Icons.ten_k, color: Colors.transparent), - actions: [CosmosTextButton(text: 'Close', onTap: () => Navigator.of(context).pop())], - ), - QrImage( - data: walletInfo.publicAddress, - size: MediaQuery.of(context).size.height / 3.515, - ), - SizedBox(height: theme.spacingXXL), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(height: 35, child: GradientAvatar(stringKey: walletInfo.publicAddress)), - SizedBox(width: theme.spacingL), - Text(walletInfo.name, style: CosmosTextTheme.title1Medium) - ], - ), - SizedBox(height: theme.spacingL), - Text(maskAddress(walletAddress), style: CosmosTextTheme.title1Medium), - const Spacer(), - CosmosTextButton( - text: 'Share', - onTap: _onTapShare, - textStyle: CosmosTextTheme.elevatedButton.copyWith(color: theme.colors.link), - suffixIcon: Image.asset('assets/images/share.png'), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: theme.spacingL), - child: Row( - children: [ - Expanded( - child: ElevatedButton( - onPressed: _onTapCopyAddress, - child: const Text('Copy address'), - ), - ) - ], - ), - ), - MinimalBottomSpacer(padding: theme.spacingXXXL) - ], - ), - ); - } - - void _onTapCopyAddress() => FlutterClipboard.copy(walletAddress); - - void _onTapShare() => Share.share(walletAddress); - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('walletInfo', walletInfo)) - ..add(StringProperty('walletAddress', walletAddress)); - } -} - -String maskAddress(String address) => '${address.substring(0, 9)}...${address.substring(address.length - 4)}';