feat: Implement sweep all

This commit is contained in:
Blazebrain 2023-07-14 09:40:10 +01:00
parent 857e355ee6
commit 49734332a1
6 changed files with 166 additions and 63 deletions

View file

@ -260,6 +260,17 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final amount =
output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
final unlockedBalance = balance[currency]?.unlockedBalance;
if (unlockedBalance == null || unlockedBalance == 0) {
final formattedBalance =
moneroAmountToString(amount: unlockedBalance ?? 0);
throw MoneroTransactionCreationException(
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.',
);
}
pendingTransactionDescription = await transaction_history.createTransaction(
address: address!,
amount: amount,
@ -454,8 +465,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
syncStatus = SyncedSyncStatus();
//! Introduce completer
if (!syncCompleter.isCompleted) {
if (blocksLeft == 0) {
syncCompleter.complete();
}
}
if (!_hasSyncAfterStartup) {
_hasSyncAfterStartup = true;
await save();

View file

@ -19,6 +19,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
@ -59,6 +60,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
@ -808,6 +810,11 @@ Future setup({
getIt.registerFactoryParam<RestoreOptionsPage, bool, void>(
(bool isNewInstall, _) => RestoreOptionsPage(isNewInstall: isNewInstall));
getIt.registerFactoryParam<SweepingWalletPage, SweepingWalletPageData, void>(
(sweepingWalletPageData, _) =>
SweepingWalletPage(sweepingWalletPageData: sweepingWalletPageData),
);
getIt.registerFactory(() => RestoreFromBackupViewModel(getIt.get<BackupService>()));
getIt.registerFactory(() => RestoreFromBackupPage(getIt.get<RestoreFromBackupViewModel>()));

View file

@ -199,7 +199,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.sweepingWalletPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SweepingWalletPage>());
builder: (_) => getIt.get<SweepingWalletPage>(
param1: settings.arguments as SweepingWalletPageData,
),
);
case Routes.dashboard:
return CupertinoPageRoute<void>(

View file

@ -1,13 +1,12 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/language_list.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart';
@ -37,8 +36,13 @@ class RestoreOptionsPage extends BasePage {
child: Column(
children: <Widget>[
RestoreButton(
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall),
onPressed: () {
Navigator.pushNamed(
context,
Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall,
);
},
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,
description: S.of(context).restore_description_from_seed_keys),
@ -49,7 +53,9 @@ class RestoreOptionsPage extends BasePage {
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup),
description:
S.of(context).restore_description_from_backup,
),
),
Padding(
padding: EdgeInsets.only(top: 24),
@ -61,24 +67,46 @@ class RestoreOptionsPage extends BasePage {
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
setupPinContext.close();
isPinSet = true;
});
},
);
}
if (!isNewInstall || isPinSet) {
try {
final restoreWallet =
await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
final restoreWallet = await WalletRestoreFromQRCode
.scanQRCodeForRestoring(context);
final restoreFromQRViewModel =
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
getIt.get<WalletRestorationFromQRVM>(
param1: restoreWallet.type,
);
await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
if (restoreWallet.txId != null &&
restoreWallet.txId!.isNotEmpty) {
Navigator.pushNamed(
context,
Routes.sweepingWalletPage,
arguments: SweepingWalletPageData(
restorationFromQRVM: restoreFromQRViewModel,
restoredWallet: restoreWallet,
),
);
} else {
await restoreFromQRViewModel.create(
restoreWallet: restoreWallet,
);
if (restoreFromQRViewModel.state is FailureState) {
final errorState = restoreFromQRViewModel.state as FailureState;
_onWalletCreateFailure(context,
'Create wallet state: ${errorState.error}');
if (restoreFromQRViewModel.state
is FailureState) {
final errorState = restoreFromQRViewModel.state
as FailureState;
_onWalletCreateFailure(
context,
'Create wallet state: ${errorState.error}',
);
}
}
} catch (e) {
_onWalletCreateFailure(context, e.toString());
}
@ -94,15 +122,19 @@ class RestoreOptionsPage extends BasePage {
);
}
void _onWalletCreateFailure(BuildContext context, String error) {
showPopUp<void>(
void _onWalletCreateFailure(BuildContext context, String error) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.error,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
buttonAction: () => Navigator.of(context).pop(),
);
},
);
Navigator.pop(context);
}
}

View file

@ -1,18 +1,23 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/scheduler.dart';
class SweepingWalletPage extends BasePage {
SweepingWalletPage();
SweepingWalletPage({required this.sweepingWalletPageData});
final SweepingWalletPageData sweepingWalletPageData;
static const aspectRatioImage = 1.25;
final welcomeImageLight = Image.asset('assets/images/welcome_light.png');
final welcomeImageDark = Image.asset('assets/images/welcome.png');
@override
Widget build(BuildContext context) {
return Scaffold(
@ -23,23 +28,31 @@ class SweepingWalletPage extends BasePage {
@override
Widget body(BuildContext context) {
final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight;
final welcomeImage = currentTheme.type == ThemeType.dark
? welcomeImageDark
: welcomeImageLight;
return SweepingWalletWidget(
aspectRatioImage: aspectRatioImage,
welcomeImage: welcomeImage,
restoredWallet: sweepingWalletPageData.restoredWallet,
aspectRatioImage: aspectRatioImage,
restoreFromQRViewModel: sweepingWalletPageData.restorationFromQRVM,
);
}
}
class SweepingWalletWidget extends StatefulWidget {
const SweepingWalletWidget({
required this.aspectRatioImage,
required this.welcomeImage,
required this.restoredWallet,
required this.aspectRatioImage,
required this.restoreFromQRViewModel,
});
final double aspectRatioImage;
final Image welcomeImage;
final double aspectRatioImage;
final RestoredWallet restoredWallet;
final WalletRestorationFromQRVM restoreFromQRViewModel;
@override
State<SweepingWalletWidget> createState() => _SweepingWalletWidgetState();
@ -49,11 +62,42 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
@override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) async {
await _initializeRestoreFromQR();
});
super.initState();
}
Future<void> _initializeRestoreFromQR() async {
try {
await widget.restoreFromQRViewModel
.createFlowForSweepAll(restoreWallet: widget.restoredWallet);
if (widget.restoreFromQRViewModel.state is FailureState) {
final errorState = widget.restoreFromQRViewModel.state as FailureState;
_onWalletCreateFailure(
context, 'Create wallet state: ${errorState.error}');
}
} catch (e) {
_onWalletCreateFailure(context, e.toString());
}
}
void _onWalletCreateFailure(BuildContext context, String error) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.error,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return WillPopScope(
@ -61,15 +105,20 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
child: Container(
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
flex: 2,
flex: 4,
child: AspectRatio(
aspectRatio: widget.aspectRatioImage,
child: FittedBox(child: widget.welcomeImage, fit: BoxFit.fill))),
child: FittedBox(
child: widget.welcomeImage,
fit: BoxFit.fill,
),
),
),
Flexible(
flex: 3,
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
@ -83,7 +132,7 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.accentTextTheme
.displayMedium!
.color,
),
@ -98,7 +147,7 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme!
.primaryTextTheme
.titleLarge!
.color!,
),
@ -113,7 +162,7 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.accentTextTheme
.displayMedium!
.color,
),
@ -129,4 +178,12 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
}
}
class SweepingWalletPageData {
final WalletRestorationFromQRVM restorationFromQRVM;
final RestoredWallet restoredWallet;
SweepingWalletPageData({
required this.restorationFromQRVM,
required this.restoredWallet,
});
}

View file

@ -68,18 +68,7 @@ abstract class WalletCreationVMBase with Store {
bool typeExists(WalletType type) => walletCreationService.typeExists(type);
Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async {
// if (restoreWallet != null &&
// restoreWallet.restoreMode == WalletRestoreMode.txids) {
await _createFlowForSweepAll(options, restoreWallet);
// }
// await _createTransactionFlowNormally(options, restoreWallet);
}
Future<void> _createTransactionFlowNormally(
dynamic options,
RestoredWallet? restoreWallet,
) async {
print('Inside normal create function');
try {
final restoredWallet = await _createNewWalletWithoutSwitching(
options: options,
@ -104,10 +93,11 @@ abstract class WalletCreationVMBase with Store {
}
}
Future<void> _createFlowForSweepAll(
Future<void> createFlowForSweepAll({
dynamic options,
RestoredWallet? restoreWallet,
) async {
}) async {
print('Inside sweep all create function');
state = IsExecutingState();
final type = restoreWallet?.type ?? this.type;
@ -195,7 +185,7 @@ abstract class WalletCreationVMBase with Store {
name: name,
type: type,
//TODO(David): Ask Omar about this, was previous isRecovery
isRecovery: restoreWallet != null ? true : false,
isRecovery: restoreWallet != null,
restoreHeight: credentials.height ?? 0,
date: DateTime.now(),
path: path,
@ -311,7 +301,8 @@ abstract class WalletCreationVMBase with Store {
Future<void> _createTransaction(WalletBase wallet, Object credentials) async {
try {
print('about to enter wallet create transaction function');
pendingTransaction = await wallet.createTransaction(credentials);
pendingTransaction =
await wallet.createTransactionForSweepAll(credentials);
} catch (e) {
state = FailureState(e.toString());
}