diff --git a/lib/router.dart b/lib/router.dart index bb8fc224..ce0e4425 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -99,7 +99,7 @@ class Router { return MaterialPageRoute(builder: (_) => WelcomePage()); case Routes.newWalletFromWelcome: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => Provider( create: (_) => UserStore( accountService: UserService( @@ -110,7 +110,7 @@ class Router { Navigator.pushNamed(context, Routes.newWallet)))); case Routes.newWallet: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => ProxyProvider( @@ -130,7 +130,7 @@ class Router { callback = settings.arguments as Function(BuildContext, String); } - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => Provider( create: (_) => UserStore( accountService: UserService( @@ -138,18 +138,17 @@ class Router { sharedPreferences: sharedPreferences)), child: SetupPinCodePage( onPinCodeSetup: (context, pin) => - callback == null ? null : callback(context, pin))), - fullscreenDialog: true); + callback == null ? null : callback(context, pin)))); case Routes.restoreOptions: - return CupertinoPageRoute(builder: (_) => RestoreOptionsPage()); + return MaterialPageRoute(builder: (_) => RestoreOptionsPage()); case Routes.restoreWalletOptions: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => RestoreWalletOptionsPage()); case Routes.restoreWalletOptionsFromWelcome: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => Provider( create: (_) => UserStore( accountService: UserService( @@ -167,7 +166,7 @@ class Router { callback: settings.arguments as void Function())); case Routes.restoreWalletFromSeed: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => ProxyProvider( update: (_, authStore, __) => WalletRestorationStore( @@ -180,7 +179,7 @@ class Router { sharedPreferences: sharedPreferences))); case Routes.restoreWalletFromKeys: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => ProxyProvider( update: (_, authStore, __) => WalletRestorationStore( @@ -193,7 +192,7 @@ class Router { sharedPreferences: sharedPreferences))); case Routes.dashboard: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => createDashboardPage( walletService: walletService, priceStore: priceStore, @@ -202,7 +201,7 @@ class Router { walletStore: walletStore)); case Routes.send: - return CupertinoPageRoute( + return MaterialPageRoute( fullscreenDialog: true, builder: (_) => MultiProvider(providers: [ ProxyProvider( @@ -222,7 +221,7 @@ class Router { ], child: SendPage())); case Routes.receive: - return CupertinoPageRoute( + return MaterialPageRoute( fullscreenDialog: true, builder: (_) => MultiProvider(providers: [ Provider( @@ -231,31 +230,29 @@ class Router { ], child: ReceivePage())); case Routes.transactionDetails: - return CupertinoPageRoute( - fullscreenDialog: true, + return MaterialPageRoute( builder: (_) => TransactionDetailsPage( transactionInfo: settings.arguments as TransactionInfo)); case Routes.newSubaddress: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => Provider( create: (_) => SubadrressCreationStore(walletService: walletService), child: NewSubaddressPage())); case Routes.disclaimer: - return CupertinoPageRoute(builder: (_) => DisclaimerPage()); + return MaterialPageRoute(builder: (_) => DisclaimerPage()); case Routes.readDisclaimer: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => DisclaimerPage(isReadOnly: true)); case Routes.seedLanguage: - return CupertinoPageRoute(builder: (_) => SeedLanguage()); + return MaterialPageRoute(builder: (_) => SeedLanguage()); case Routes.walletList: return MaterialPageRoute( - fullscreenDialog: true, builder: (_) => Provider( create: (_) => WalletListStore( walletListService: walletListService, @@ -263,9 +260,10 @@ class Router { child: WalletListPage())); case Routes.auth: - return MaterialPageRoute( + return PageRouteBuilder( fullscreenDialog: true, - builder: (_) => Provider( + transitionDuration: Duration(milliseconds: 0), + pageBuilder: (_, __, ___) => Provider( create: (_) => AuthStore( sharedPreferences: sharedPreferences, userService: userService, @@ -276,9 +274,10 @@ class Router { )); case Routes.unlock: - return MaterialPageRoute( + return PageRouteBuilder( fullscreenDialog: true, - builder: (_) => createUnlockPage( + transitionDuration: Duration(milliseconds: 0), + pageBuilder: (_, __, ___) => createUnlockPage( sharedPreferences: sharedPreferences, userService: userService, walletService: walletService, @@ -286,20 +285,20 @@ class Router { settings.arguments as OnAuthenticationFinished)); case Routes.nodeList: - return CupertinoPageRoute(builder: (context) { + return MaterialPageRoute(builder: (context) { return Provider( create: (_) => NodeListStore(nodesSource: nodes), child: NodeListPage()); }); case Routes.newNode: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => Provider( create: (_) => NodeListStore(nodesSource: nodes), child: NewNodePage())); case Routes.login: - return CupertinoPageRoute(builder: (context) { + return MaterialPageRoute(builder: (context) { final authenticationStore = Provider.of(context); return createLoginPage( @@ -318,11 +317,10 @@ class Router { create: (_) => AccountListStore(walletService: walletService)), ], child: AccountListPage()); - }, - fullscreenDialog: true); + }); case Routes.accountCreation: - return CupertinoPageRoute(builder: (context) { + return MaterialPageRoute(builder: (context) { return Provider( create: (_) => AccountListStore(walletService: walletService), child: AccountPage(account: settings.arguments as Account)); @@ -355,7 +353,7 @@ class Router { }); case Routes.addressBookAddContact: - return CupertinoPageRoute(builder: (context) { + return MaterialPageRoute(builder: (context) { return MultiProvider( providers: [ Provider( @@ -400,7 +398,7 @@ class Router { ], child: SubaddressListPage())); case Routes.restoreWalletFromSeedDetails: - return CupertinoPageRoute( + return MaterialPageRoute( builder: (_) => ProxyProvider( update: (_, authStore, __) => WalletRestorationStore( @@ -432,13 +430,14 @@ class Router { return MaterialPageRoute(builder: (_) => ChangeLanguage()); case Routes.profile: - return CupertinoPageRoute(builder: (_) => ProfilePage()); + return MaterialPageRoute(builder: (_) => ProfilePage()); case Routes.stake: - return CupertinoPageRoute(builder: (_) => StakePage()); + return MaterialPageRoute(builder: (_) => StakePage()); case Routes.newStake: return MaterialPageRoute( + fullscreenDialog: true, builder: (_) => MultiProvider(providers: [ ProxyProvider( update: (_, settingsStore, __) => BalanceStore( diff --git a/lib/src/domain/services/wallet_service.dart b/lib/src/domain/services/wallet_service.dart index 3b067900..653fcb44 100644 --- a/lib/src/domain/services/wallet_service.dart +++ b/lib/src/domain/services/wallet_service.dart @@ -88,14 +88,17 @@ class WalletService extends Wallet { Future> getKeys() => _currentWallet.getKeys(); @override - int getFullBalance() => _currentWallet.getFullBalance(); + Future getFullBalance() => _currentWallet.getFullBalance(); @override - int getUnlockedBalance() => _currentWallet.getUnlockedBalance(); + Future getUnlockedBalance() => _currentWallet.getUnlockedBalance(); @override int getCurrentHeight() => _currentWallet.getCurrentHeight(); + @override + bool isRefreshing() => currentWallet.isRefreshing(); + @override Future getNodeHeight() => _currentWallet.getNodeHeight(); diff --git a/lib/src/screens/address_book/address_book_page.dart b/lib/src/screens/address_book/address_book_page.dart index 1850e0f4..7efbca25 100644 --- a/lib/src/screens/address_book/address_book_page.dart +++ b/lib/src/screens/address_book/address_book_page.dart @@ -17,15 +17,9 @@ class AddressBookPage extends BasePage { final bool isEditable; - @override - bool get isModalBackButton => true; - @override String get title => S.current.address_book; - @override - AppBarStyle get appBarStyle => AppBarStyle.withShadow; - @override Widget trailing(BuildContext context) { if (!isEditable) return null; diff --git a/lib/src/screens/seed/seed_page.dart b/lib/src/screens/seed/seed_page.dart index fd0b563e..2ead2c89 100644 --- a/lib/src/screens/seed/seed_page.dart +++ b/lib/src/screens/seed/seed_page.dart @@ -67,7 +67,7 @@ class SeedPage extends BasePage { padding: EdgeInsets.only(bottom: 20.0), margin: EdgeInsets.only(bottom: 10.0), child: Text( - walletSeedStore.name, + walletSeedStore.name ?? '', textAlign: TextAlign.center, style: TextStyle( fontSize: 18.0, diff --git a/lib/src/screens/settings/settings.dart b/lib/src/screens/settings/settings.dart index 38610666..7209498c 100644 --- a/lib/src/screens/settings/settings.dart +++ b/lib/src/screens/settings/settings.dart @@ -30,9 +30,6 @@ class SettingsPage extends BasePage { @override String get title => S.current.settings_title; - @override - bool get isModalBackButton => true; - @override Color get backgroundColor => Palette.lightGrey2; diff --git a/lib/src/screens/stake/stake_page.dart b/lib/src/screens/stake/stake_page.dart index 81dc7508..4372611f 100644 --- a/lib/src/screens/stake/stake_page.dart +++ b/lib/src/screens/stake/stake_page.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:oxen_coin/oxen_coin_structs.dart'; @@ -36,154 +38,169 @@ class StakePageBody extends StatefulWidget { } class StakePageBodyState extends State { - List allStakes = getAllStakes(); - - Color get stakeColor => - allStakes.isEmpty ? OxenPalette.lightRed : OxenPalette.lime; - - int get totalAmountStaked { - var totalAmount = 0; - for (final stake in allStakes) { - totalAmount += stake.amount; - } - return totalAmount; - } - - double get stakePercentage { - if (allStakes.isEmpty) return 1; - final percentage = oxenAmountToDouble(totalAmountStaked) / 15000; - if (percentage > 1) return 1; - return percentage; - } - @override Widget build(BuildContext context) { return SingleChildScrollView( - child: ListView( - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - children: [ - SizedBox( - height: 220.0, - child: Stack( - children: [ - Center( - child: Container( + child: FutureBuilder>( + future: getAllStakes(), + builder: (BuildContext context, AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + return Center( + child: Container( width: 200, - height: 200, - child: CircularProgressIndicator( - strokeWidth: 15, - value: stakePercentage, - valueColor: AlwaysStoppedAnimation(stakeColor), - ), + height: 400, + child: Center( + child: Text(snapshot.error.toString()), + ) + ), + ); + } + final allStakes = snapshot.data; + final stakeColor = allStakes.isEmpty ? OxenPalette.lightRed : OxenPalette.lime; + var totalAmountStaked = 0; + for (final stake in allStakes) { + totalAmountStaked += stake.amount; + } + final stakePercentage = allStakes.isEmpty ? 1.0 : min(oxenAmountToDouble(totalAmountStaked) / 15000, 1.0); + return ListView( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + children: [ + SizedBox( + height: 220.0, + child: Stack( + children: [ + Center( + child: Container( + width: 200, + height: 200, + child: CircularProgressIndicator( + strokeWidth: 15, + value: stakePercentage, + valueColor: AlwaysStoppedAnimation(stakeColor), + ), + ), + ), + Center( + child: Text(allStakes.isNotEmpty + ? oxenAmountToString(totalAmountStaked, + detail: AmountDetail.none) + : S.current.nothing_staked)), + ], ), ), - Center( - child: Text(allStakes.isNotEmpty - ? oxenAmountToString(totalAmountStaked, - detail: AmountDetail.none) - : S.current.nothing_staked)), - ], - ), - ), - Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: Icon(Icons.arrow_upward_rounded), - onPressed: () => Navigator.of(context, rootNavigator: true) - .pushNamed(Routes.newStake), + Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: Icon(Icons.arrow_upward_rounded), + onPressed: () => Navigator.of(context, rootNavigator: true) + .pushNamed(Routes.newStake), + ), + Text(allStakes.isEmpty + ? S.current.start_staking + : S.current.stake_more) + ], + ), ), - Text(allStakes.isEmpty - ? S.current.start_staking - : S.current.stake_more) - ], - ), - ), - if (allStakes.isNotEmpty) - NavListHeader(title: S.current.your_contributions), - if (allStakes.isNotEmpty) - ListView.builder( - shrinkWrap: true, - itemCount: allStakes.length, - itemBuilder: (BuildContext context, int index) { - final stake = allStakes[index]; - final serviceNodeKey = stake.serviceNodeKey; - final nodeName = - '${serviceNodeKey.substring(0, 12)}...${serviceNodeKey.substring(serviceNodeKey.length - 4)}'; + if (allStakes.isNotEmpty) + NavListHeader(title: S.current.your_contributions), + if (allStakes.isNotEmpty) + ListView.builder( + shrinkWrap: true, + itemCount: allStakes.length, + itemBuilder: (BuildContext context, int index) { + final stake = allStakes[index]; + final serviceNodeKey = stake.serviceNodeKey; + final nodeName = + '${serviceNodeKey.substring(0, 12)}...${serviceNodeKey.substring(serviceNodeKey.length - 4)}'; - return Dismissible( - key: Key(stake.serviceNodeKey), - confirmDismiss: (direction) async { - if (!canRequestUnstake(stake.serviceNodeKey)) { - Scaffold.of(context).showSnackBar(SnackBar( - content: Text(S.of(context).unable_unlock_stake), - backgroundColor: Colors.red, - )); - return false; - } - var isSuccessful = false; - var isAuthenticated = false; + return Dismissible( + key: Key(stake.serviceNodeKey), + confirmDismiss: (direction) async { + if (!canRequestUnstake(stake.serviceNodeKey)) { + Scaffold.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).unable_unlock_stake), + backgroundColor: Colors.red, + )); + return false; + } + var isSuccessful = false; + var isAuthenticated = false; - await Navigator.of(context).pushNamed(Routes.auth, - arguments: (bool isAuthenticatedSuccessfully, - AuthPageState auth) async { - if (isAuthenticatedSuccessfully) { - isAuthenticated = true; - Navigator.of(auth.context).pop(); - } - }); + await Navigator.of(context).pushNamed(Routes.auth, + arguments: (bool isAuthenticatedSuccessfully, + AuthPageState auth) async { + if (isAuthenticatedSuccessfully) { + isAuthenticated = true; + Navigator.of(auth.context).pop(); + } + }); - if (isAuthenticated) { - await showConfirmOxenDialog( - context, - S.of(context).title_confirm_unlock_stake, - S.of(context).body_confirm_unlock_stake( - stake.serviceNodeKey), - onDismiss: (buildContext) { - isSuccessful = false; - Navigator.of(buildContext).pop(); - }, onConfirm: (buildContext) { - isSuccessful = true; - Navigator.of(buildContext).pop(); - }); - } + if (isAuthenticated) { + await showConfirmOxenDialog( + context, + S.of(context).title_confirm_unlock_stake, + S.of(context).body_confirm_unlock_stake( + stake.serviceNodeKey), + onDismiss: (buildContext) { + isSuccessful = false; + Navigator.of(buildContext).pop(); + }, onConfirm: (buildContext) { + isSuccessful = true; + Navigator.of(buildContext).pop(); + }); + } - return isSuccessful; - }, - onDismissed: (direction) async { - await submitStakeUnlock(stake.serviceNodeKey); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text(S.of(context).unlock_stake_requested), - backgroundColor: Colors.green, - )); - }, - direction: DismissDirection.endToStart, - background: Container( - padding: EdgeInsets.only(right: 10.0), - alignment: AlignmentDirectional.centerEnd, - color: OxenPalette.red, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Icons.arrow_downward_sharp, - color: Colors.white, - ) - ], - )), - child: NavListTrailing( - leading: CircularProgressIndicator( - valueColor: - AlwaysStoppedAnimation(stakeColor), - value: stake.ownedPercentage), - text: nodeName, - )); - }), - ], - ), + return isSuccessful; + }, + onDismissed: (direction) async { + await submitStakeUnlock(stake.serviceNodeKey); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text(S.of(context).unlock_stake_requested), + backgroundColor: Colors.green, + )); + }, + direction: DismissDirection.endToStart, + background: Container( + padding: EdgeInsets.only(right: 10.0), + alignment: AlignmentDirectional.centerEnd, + color: OxenPalette.red, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.arrow_downward_sharp, + color: Colors.white, + ) + ], + )), + child: NavListTrailing( + leading: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(stakeColor), + value: stake.ownedPercentage), + text: nodeName, + )); + }), + ], + ); + } else { + return Center( + child: Container( + width: 200, + height: 400, + child: Center( + child: CircularProgressIndicator(), + ) + ), + ); + } + }, + ) ); } } diff --git a/lib/src/screens/transaction_details/transaction_details_page.dart b/lib/src/screens/transaction_details/transaction_details_page.dart index c4ec9f75..506e894b 100644 --- a/lib/src/screens/transaction_details/transaction_details_page.dart +++ b/lib/src/screens/transaction_details/transaction_details_page.dart @@ -14,9 +14,6 @@ class TransactionDetailsPage extends BasePage { final TransactionInfo transactionInfo; - @override - bool get isModalBackButton => true; - @override String get title => S.current.transaction_details_title; diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 439d3637..c3c9fcce 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -14,15 +14,10 @@ import 'package:oxen_wallet/src/screens/wallet_list/wallet_menu.dart'; import 'package:oxen_wallet/src/widgets/picker.dart'; class WalletListPage extends BasePage { - @override - bool get isModalBackButton => true; @override String get title => S.current.wallet_list_title; - @override - AppBarStyle get appBarStyle => AppBarStyle.withShadow; - @override Widget body(BuildContext context) => WalletListBody(); } diff --git a/lib/src/stores/action_list/action_list_store.dart b/lib/src/stores/action_list/action_list_store.dart index 52048bbd..0be357ac 100644 --- a/lib/src/stores/action_list/action_list_store.dart +++ b/lib/src/stores/action_list/action_list_store.dart @@ -141,7 +141,7 @@ abstract class ActionListBase with Store { // } Future _updateTransactionsList() async { - await _history.refresh(); + // await _history.refresh(); final _transactions = await _history.getAll(); await _setTransactions(_transactions); } diff --git a/lib/src/stores/balance/balance_store.dart b/lib/src/stores/balance/balance_store.dart index a778f547..5e840abb 100644 --- a/lib/src/stores/balance/balance_store.dart +++ b/lib/src/stores/balance/balance_store.dart @@ -134,8 +134,8 @@ abstract class BalanceStoreBase with Store { return; } - fullBalance = _walletService.getFullBalance(); - unlockedBalance = _walletService.getUnlockedBalance(); + fullBalance = await _walletService.getFullBalance(); + unlockedBalance = await _walletService.getUnlockedBalance(); await updateFiatBalance(); } diff --git a/lib/src/wallet/oxen/oxen_wallet.dart b/lib/src/wallet/oxen/oxen_wallet.dart index 1da2ce6e..b3af722c 100644 --- a/lib/src/wallet/oxen/oxen_wallet.dart +++ b/lib/src/wallet/oxen/oxen_wallet.dart @@ -1,7 +1,7 @@ import 'dart:async'; +import 'dart:developer'; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; import 'package:oxen_coin/stake.dart' as oxen_stake; import 'package:oxen_coin/transaction_history.dart' as transaction_history; @@ -30,11 +30,6 @@ const oxenBlockSize = 1000; class OxenWallet extends Wallet { OxenWallet({this.walletInfoSource, this.walletInfo}) { _cachedBlockchainHeight = 0; - _isSaving = false; - _lastSaveTime = 0; - _lastRefreshedTime = 0; - _refreshHeight = 0; - _lastSyncHeight = 0; _name = BehaviorSubject(); _address = BehaviorSubject(); _syncStatus = BehaviorSubject(); @@ -129,11 +124,6 @@ class OxenWallet extends Wallet { BehaviorSubject _address; BehaviorSubject _subaddress; int _cachedBlockchainHeight; - bool _isSaving; - int _lastSaveTime; - int _lastRefreshedTime; - int _refreshHeight; - int _lastSyncHeight; TransactionHistory _cachedTransactionHistory; SubaddressList _cachedSubaddressList; @@ -171,16 +161,23 @@ class OxenWallet extends Wallet { Future getSeed() async => oxen_wallet.getSeed(); @override - int getFullBalance() => - oxen_wallet.getFullBalance(accountIndex: _account.value.id); + Future getFullBalance() async { + final balance = await oxen_wallet.getFullBalance(accountIndex: _account.value.id); + return balance; + } @override - int getUnlockedBalance() => - oxen_wallet.getUnlockedBalance(accountIndex: _account.value.id); + Future getUnlockedBalance() async { + final balance = await oxen_wallet.getUnlockedBalance(accountIndex: _account.value.id); + return balance; + } @override int getCurrentHeight() => oxen_wallet.getCurrentHeight(); + @override + bool isRefreshing() => oxen_wallet.isRefreshing(); + @override Future getNodeHeight() async { _cachedGetNodeHeightOrUpdateRequest ??= @@ -277,16 +274,6 @@ class OxenWallet extends Wallet { } } - Future askForSave() async { - final diff = DateTime.now().millisecondsSinceEpoch - _lastSaveTime; - - if (_lastSaveTime != 0 && diff < 120000) { - return; - } - - await store(); - } - Future getNodeHeightOrUpdate(int baseHeight) async { if (_cachedBlockchainHeight < baseHeight) { _cachedBlockchainHeight = await getNodeHeight(); @@ -299,8 +286,11 @@ class OxenWallet extends Wallet { Future createStake( TransactionCreationCredentials credentials) async { final _credentials = credentials as OxenStakeTransactionCreationCredentials; + if (_credentials.amount == null || _credentials.address == null) { + return Future.error('Amount and address cannot be null.'); + } final transactionDescription = - await oxen_stake.createStake(_credentials.address, _credentials.amount); + await oxen_stake.createStake(_credentials.address, _credentials.amount); return PendingTransaction.fromTransactionDescription( transactionDescription); @@ -339,12 +329,12 @@ class OxenWallet extends Wallet { await walletInfo.save(); } - void askForUpdateBalance() { - final fullBalance = getFullBalance(); - final unlockedBalance = getUnlockedBalance(); + Future askForUpdateBalance() async { + final fullBalance = await getFullBalance(); + final unlockedBalance = await getUnlockedBalance(); final needToChange = _onBalanceChange.value != null ? _onBalanceChange.value.fullBalance != fullBalance || - _onBalanceChange.value.unlockedBalance != unlockedBalance + _onBalanceChange.value.unlockedBalance != unlockedBalance : true; if (!needToChange) { @@ -355,7 +345,9 @@ class OxenWallet extends Wallet { fullBalance: fullBalance, unlockedBalance: unlockedBalance)); } - Future askForUpdateTransactionHistory() async => await getHistory().update(); + Future askForUpdateTransactionHistory() async { + await getHistory().update(); + } void changeCurrentSubaddress(Subaddress subaddress) => _subaddress.value = subaddress; @@ -369,43 +361,29 @@ class OxenWallet extends Wallet { .then((subaddresses) => _subaddress.value = subaddresses[0]); } - Future store() async { - if (_isSaving) { - return; - } - - try { - _isSaving = true; - await oxen_wallet.store(); - _isSaving = false; - } on PlatformException catch (e) { - print(e); - _isSaving = false; - rethrow; - } - } - oxen_wallet.SyncListener setListeners() => oxen_wallet.setListeners(_onNewBlock, _onNewTransaction); - Future _onNewBlock(int height, int blocksLeft, double ptc) async { + Future _onNewBlock(int height, int blocksLeft, double ptc, bool isRefreshing) async { try { - await askForUpdateTransactionHistory(); - askForUpdateBalance(); + if (isRefreshing) { + _syncStatus.add(SyncingSyncStatus(blocksLeft, ptc)); + } else { + await askForUpdateTransactionHistory(); + await askForUpdateBalance(); - if (blocksLeft < 100) { - _syncStatus.add(SyncedSyncStatus()); - await oxen_wallet.store(); + if (blocksLeft < 100) { + _syncStatus.add(SyncedSyncStatus()); + await oxen_wallet.store(); - if (walletInfo.isRecovery) { - await setAsRecovered(); + if (walletInfo.isRecovery) { + await setAsRecovered(); + } } - } else { - _syncStatus.add(SyncingSyncStatus(blocksLeft, ptc)); - } - if (blocksLeft <= 1) { - oxen_wallet.setRefreshFromBlockHeight(height: height); + if (blocksLeft <= 1) { + oxen_wallet.setRefreshFromBlockHeight(height: height); + } } } catch (e) { print(e.toString()); @@ -423,6 +401,7 @@ class OxenWallet extends Wallet { } final currentHeight = getCurrentHeight(); + print('setInitialHeight() $currentHeight'); if (currentHeight <= 1) { final height = _getHeightByDate(walletInfo.date); @@ -451,49 +430,10 @@ class OxenWallet extends Wallet { return nodeHeight - heightDistance; } - Future _onNeedToRefresh() async { - try { - final currentHeight = getCurrentHeight(); - final nodeHeight = await getNodeHeightOrUpdate(currentHeight); - - // no blocks - maybe we're not connected to the node ? - if (currentHeight <= 1 || nodeHeight == 0) { - return; - } - - if (_syncStatus.value is FailedSyncStatus) { - return; - } - - await askForUpdateBalance(); - - final heightDifference = nodeHeight - currentHeight; - final isRefreshed = heightDifference < oxenBlockSize; - - if (isRefreshed) { - _syncStatus.add(SyncedSyncStatus()); - - if (isRecovery) { - await setAsRecovered(); - } - } - - final now = DateTime.now().millisecondsSinceEpoch; - final lastRefreshedTimeDifference = now - _lastRefreshedTime; - - if (lastRefreshedTimeDifference >= 60000) { - await askForSave(); - _lastRefreshedTime = now; - } - } catch (e) { - print(e); - } - } - Future _onNewTransaction() async { try { await askForUpdateTransactionHistory(); - askForUpdateBalance(); + await askForUpdateBalance(); } catch (e) { print(e.toString()); } diff --git a/lib/src/wallet/wallet.dart b/lib/src/wallet/wallet.dart index b5f49ebc..029c0bb2 100644 --- a/lib/src/wallet/wallet.dart +++ b/lib/src/wallet/wallet.dart @@ -36,12 +36,14 @@ abstract class Wallet { Future> getKeys(); - int getFullBalance(); + Future getFullBalance(); - int getUnlockedBalance(); + Future getUnlockedBalance(); int getCurrentHeight(); + bool isRefreshing(); + Future getNodeHeight(); Future isConnected(); diff --git a/oxen_coin/ios/Classes/oxen_api.cpp b/oxen_coin/ios/Classes/oxen_api.cpp index a7d98627..b0e29d6b 100644 --- a/oxen_coin/ios/Classes/oxen_api.cpp +++ b/oxen_coin/ios/Classes/oxen_api.cpp @@ -418,6 +418,12 @@ extern "C" return get_current_wallet()->daemonBlockChainHeight(); } + EXPORT + bool is_refreshing() + { + return get_current_wallet()->isRefreshing(); + } + EXPORT bool connect_to_node(char *error) { diff --git a/oxen_coin/lib/src/native/stake.dart b/oxen_coin/lib/src/native/stake.dart index ee8f8021..bf982ba3 100644 --- a/oxen_coin/lib/src/native/stake.dart +++ b/oxen_coin/lib/src/native/stake.dart @@ -31,7 +31,7 @@ final submitStakeUnlockNative = oxenApi PendingTransactionDescription createStakeSync( String serviceNodeKey, String amount) { final serviceNodeKeyPointer = Utf8.toUtf8(serviceNodeKey); - final amountPointer = amount != null ? Utf8.toUtf8(amount) : nullptr; + final amountPointer = Utf8.toUtf8(amount); final errorMessagePointer = allocate(); final pendingTransactionRawPointer = allocate(); final created = stakeCreateNative(serviceNodeKeyPointer, amountPointer, diff --git a/oxen_coin/lib/src/native/wallet.dart b/oxen_coin/lib/src/native/wallet.dart index c2f8f37a..1285e026 100644 --- a/oxen_coin/lib/src/native/wallet.dart +++ b/oxen_coin/lib/src/native/wallet.dart @@ -44,6 +44,10 @@ final getNodeHeightNative = oxenApi .lookup>('get_node_height') .asFunction(); +final isRefreshingNative = oxenApi + .lookup>('is_refreshing') + .asFunction(); + final isConnectedNative = oxenApi .lookup>('is_connected') .asFunction(); @@ -119,6 +123,8 @@ final rescanBlockchainAsyncNative = oxenApi int getNodeHeightSync() => getNodeHeightNative(); +bool isRefreshingSync() => isRefreshingNative() != 0; + bool isConnectedSync() => isConnectedNative() != 0; bool setupNodeSync( diff --git a/oxen_coin/lib/src/structs/stake_row.dart b/oxen_coin/lib/src/structs/stake_row.dart index 9aad0a05..aece5162 100644 --- a/oxen_coin/lib/src/structs/stake_row.dart +++ b/oxen_coin/lib/src/structs/stake_row.dart @@ -1,7 +1,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -class StakeRow extends Struct { +class StakeRowPointer extends Struct { Pointer _serviceNodeKey; @Uint64() @@ -10,3 +10,13 @@ class StakeRow extends Struct { String get serviceNodeKey => Utf8.fromUtf8(_serviceNodeKey); int get amount => _amount; } + +class StakeRow { + StakeRow(StakeRowPointer pointer) { + amount = pointer.amount; + serviceNodeKey = pointer.serviceNodeKey; + } + + int amount; + String serviceNodeKey; +} diff --git a/oxen_coin/lib/src/util/signatures.dart b/oxen_coin/lib/src/util/signatures.dart index 475ce6e3..112dc3a4 100644 --- a/oxen_coin/lib/src/util/signatures.dart +++ b/oxen_coin/lib/src/util/signatures.dart @@ -39,6 +39,8 @@ typedef get_current_height = Int64 Function(); typedef get_node_height = Int64 Function(); +typedef is_refreshing = Int8 Function(); + typedef is_connected = Int8 Function(); typedef setup_node = Int8 Function( diff --git a/oxen_coin/lib/src/util/types.dart b/oxen_coin/lib/src/util/types.dart index fe6dbf3c..43ecc79a 100644 --- a/oxen_coin/lib/src/util/types.dart +++ b/oxen_coin/lib/src/util/types.dart @@ -39,6 +39,8 @@ typedef GetCurrentHeight = int Function(); typedef GetNodeHeight = int Function(); +typedef IsRefreshing = int Function(); + typedef IsConnected = int Function(); typedef SetupNode = int Function( diff --git a/oxen_coin/lib/stake.dart b/oxen_coin/lib/stake.dart index e3507656..081404ff 100644 --- a/oxen_coin/lib/stake.dart +++ b/oxen_coin/lib/stake.dart @@ -7,16 +7,19 @@ import 'package:oxen_coin/src/native/stake.dart' as stake_native; int countOfTransactions() => stake_native.stakeCountNative(); -List getAllStakes() { +List _getAllStakesSync(int _) { final size = countOfTransactions(); final stakePointer = stake_native.stakeGetAllNative(); final stakeAddresses = stakePointer.asTypedList(size); return stakeAddresses - .map((addr) => Pointer.fromAddress(addr).ref) + .map((addr) => StakeRow(Pointer.fromAddress(addr).ref)) .toList(); } +Future> getAllStakes() => + compute>(_getAllStakesSync, 0); + PendingTransactionDescription _createStakeSync(Map args) { final serviceNodeKey = args['service_node_key'] as String; final amount = args['amount'] as String; diff --git a/oxen_coin/lib/wallet.dart b/oxen_coin/lib/wallet.dart index 293b21ac..e767838a 100644 --- a/oxen_coin/lib/wallet.dart +++ b/oxen_coin/lib/wallet.dart @@ -31,16 +31,24 @@ String getAddress({int accountIndex = 0, int addressIndex = 0}) => convertUTF8ToString( pointer: oxen_wallet.getAddressNative(accountIndex, addressIndex)); -int getFullBalance({int accountIndex = 0}) => +int _getFullBalanceSync(int accountIndex) => oxen_wallet.getFullBalanceNative(accountIndex); -int getUnlockedBalance({int accountIndex = 0}) => +Future getFullBalance({int accountIndex = 0}) => + compute(_getFullBalanceSync, accountIndex); + +int _getUnlockedBalanceSync(int accountIndex) => oxen_wallet.getUnlockedBalanceNative(accountIndex); +Future getUnlockedBalance({int accountIndex = 0}) => + compute(_getUnlockedBalanceSync, accountIndex); + int getCurrentHeight() => oxen_wallet.getCurrentHeightNative(); int getNodeHeightSync() => oxen_wallet.getNodeHeightNative(); +bool isRefreshingSync() => oxen_wallet.isRefreshingNative() != 0; + bool isConnectedSync() => oxen_wallet.isConnectedNative() != 0; bool setupNodeSync( @@ -114,7 +122,7 @@ class SyncListener { _initialSyncHeight = 0; } - void Function(int, int, double) onNewBlock; + void Function(int, int, double, bool) onNewBlock; void Function() onNewTransaction; Timer _updateSyncInfoTimer; @@ -136,15 +144,13 @@ class SyncListener { _initialSyncHeight = 0; _updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async { - if (isNewTransactionExist()) { - onNewTransaction?.call(); - } + // var syncHeight = getSyncingHeight(); + // + // if (syncHeight <= 0) { + // syncHeight = getCurrentHeight(); + // } - var syncHeight = getSyncingHeight(); - - if (syncHeight <= 0) { - syncHeight = getCurrentHeight(); - } + final syncHeight = getCurrentHeight(); if (_initialSyncHeight <= 0) { _initialSyncHeight = syncHeight; @@ -166,15 +172,22 @@ class SyncListener { return; } + final refreshing = isRefreshing(); + if (!refreshing) { + if (isNewTransactionExist()) { + onNewTransaction?.call(); + } + } + // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; - onNewBlock?.call(syncHeight, left, ptc); + onNewBlock?.call(syncHeight, left, ptc, refreshing); }); } void stop() => _updateSyncInfoTimer?.cancel(); } -SyncListener setListeners(void Function(int, int, double) onNewBlock, +SyncListener setListeners(void Function(int, int, double, bool) onNewBlock, void Function() onNewTransaction) { final listener = SyncListener(onNewBlock, onNewTransaction); oxen_wallet.setListenerNative(); @@ -202,6 +215,8 @@ bool _setupNodeSync(Map args) { bool _isConnected(Object _) => isConnectedSync(); +bool isRefreshing() => isRefreshingSync(); + int _getNodeHeight(Object _) => getNodeHeightSync(); void startRefresh() => startRefreshSync(); diff --git a/pubspec.yaml b/pubspec.yaml index 1b2b973c..e7e6f9d7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: A Wallet for Oxen. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.6+6 +version: 1.0.7+9 # keytool -genkey -v -keystore c:\Users\konst\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias key @@ -53,6 +53,7 @@ dependencies: package_info: ^2.0.0 devicelocale: ^0.4.1 auto_size_text: ^2.1.0 + synchronized: ^3.0.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.