-
Notifications
You must be signed in to change notification settings - Fork 133
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Used for displaying the balance of the active Satscard slot and allowing the user to sweep it into their wallet
- Loading branch information
1 parent
b55ecf1
commit 00ea63e
Showing
5 changed files
with
1,084 additions
and
0 deletions.
There are no files selected for viewing
156 changes: 156 additions & 0 deletions
156
lib/routes/satscard_balance/broadcast_slot_sweep_transaction.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import 'dart:typed_data'; | ||
|
||
import 'package:breez/bloc/account/account_actions.dart'; | ||
import 'package:breez/bloc/account/account_bloc.dart'; | ||
import 'package:breez/bloc/blocs_provider.dart'; | ||
import 'package:breez/bloc/satscard/satscard_actions.dart'; | ||
import 'package:breez/bloc/satscard/satscard_bloc.dart'; | ||
import 'package:breez/routes/satscard_balance/satscard_balance_page.dart'; | ||
import 'package:breez/services/breezlib/data/messages.pb.dart'; | ||
import 'package:breez/services/injector.dart'; | ||
import 'package:breez/widgets/back_button.dart' as backBtn; | ||
import 'package:breez/widgets/error_dialog.dart'; | ||
import 'package:breez/widgets/flushbar.dart'; | ||
import 'package:breez/widgets/link_launcher.dart'; | ||
import 'package:breez/widgets/single_button_bottom_bar.dart'; | ||
import 'package:breez_translations/breez_translations_locales.dart'; | ||
import 'package:breez_translations/generated/breez_translations.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
class BroadcastSlotSweepTransactionPage extends StatefulWidget { | ||
final Function() onBack; | ||
final Function() onDone; | ||
final AddressInfo Function() getAddressInfo; | ||
final Uint8List Function() getPrivateKey; | ||
final RawSlotSweepTransaction Function() getTransaction; | ||
|
||
const BroadcastSlotSweepTransactionPage( | ||
{@required this.onBack, | ||
@required this.onDone, | ||
@required this.getAddressInfo, | ||
@required this.getPrivateKey, | ||
@required this.getTransaction}); | ||
|
||
@override | ||
State<StatefulWidget> createState() => | ||
BroadcastSlotSweepTransactionPageState(); | ||
} | ||
|
||
class BroadcastSlotSweepTransactionPageState | ||
extends State<BroadcastSlotSweepTransactionPage> { | ||
TransactionDetails _signedTransaction; | ||
Future<void> _future; | ||
|
||
@override | ||
void didChangeDependencies() { | ||
super.didChangeDependencies(); | ||
_broadcastTransaction(context); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return FutureBuilder( | ||
future: _future, | ||
builder: (context, snapshot) { | ||
final themeData = Theme.of(context); | ||
final texts = context.texts(); | ||
final showError = snapshot.hasError; | ||
return Scaffold( | ||
appBar: AppBar( | ||
title: Text( | ||
texts.satscard_broadcast_title, | ||
style: themeData.appBarTheme.titleTextStyle, | ||
), | ||
leading: backBtn.BackButton( | ||
onPressed: widget.onBack, | ||
), | ||
), | ||
bottomNavigationBar: | ||
!snapshot.hasError || _signedTransaction == null | ||
? null | ||
: Padding( | ||
padding: const EdgeInsets.only(top: 10), | ||
child: SingleButtonBottomBar( | ||
stickToBottom: true, | ||
text: texts.satscard_balance_button_retry_label, | ||
onPressed: () => _broadcastTransaction(context), | ||
), | ||
), | ||
body: showError | ||
? buildErrorBody( | ||
themeData, _getErrorText(texts, snapshot.error)) | ||
: buildLoaderBody(themeData, _getLoaderText(texts)), | ||
); | ||
}); | ||
} | ||
|
||
String _getErrorText(BreezTranslations texts, Object error) { | ||
return _signedTransaction == null | ||
? texts.satscard_broadcast_error_signing(error) | ||
: texts.satscard_broadcast_error_broadcasting(error); | ||
} | ||
|
||
String _getLoaderText(BreezTranslations texts) { | ||
return _signedTransaction == null | ||
? texts.satscard_broadcast_signing_label | ||
: texts.satscard_broadcast_broadcasting_label; | ||
} | ||
|
||
void _broadcastTransaction(BuildContext context) { | ||
if (!context.mounted) { | ||
return; | ||
} | ||
setState(() { | ||
_future = Future.sync(() { | ||
// Sign the selected transaction if we haven't already | ||
if (_signedTransaction == null) { | ||
final satscardBloc = AppBlocsProvider.of<SatscardBloc>(context); | ||
final info = widget.getAddressInfo(); | ||
final tx = widget.getTransaction(); | ||
final key = widget.getPrivateKey(); | ||
final action = SignSlotSweepTransaction(info, tx, key); | ||
satscardBloc.actionsSink.add(action); | ||
return action.future.then((result) { | ||
if (context.mounted) { | ||
setState(() { | ||
_signedTransaction = result as TransactionDetails; | ||
}); | ||
} | ||
}); | ||
} | ||
}).then((_) { | ||
final accountBloc = AppBlocsProvider.of<AccountBloc>(context); | ||
final action = PublishTransaction(_signedTransaction.tx); | ||
accountBloc.userActionsSink.add(action); | ||
return action.future; | ||
}).then((_) { | ||
if (context.mounted) { | ||
final texts = context.texts(); | ||
final tx = _signedTransaction; | ||
|
||
widget.onDone(); | ||
promptMessage( | ||
context, | ||
texts.satscard_broadcast_complete_title, | ||
Builder( | ||
builder: (context) => LinkLauncher( | ||
linkName: tx.txHash, | ||
linkAddress: "https://blockstream.info/tx/${tx.txHash}", | ||
onCopy: () { | ||
ServiceInjector().device.setClipboardText(tx.txHash); | ||
showFlushbar( | ||
context, | ||
message: texts.add_funds_transaction_id_copied, | ||
duration: const Duration(seconds: 3), | ||
); | ||
}, | ||
), | ||
), | ||
contentPadding: | ||
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 32.0), | ||
); | ||
} | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import 'dart:typed_data'; | ||
|
||
import 'package:auto_size_text/auto_size_text.dart'; | ||
import 'package:breez/bloc/account/account_model.dart'; | ||
import 'package:breez/routes/satscard_balance/broadcast_slot_sweep_transaction.dart'; | ||
import 'package:breez/routes/satscard_balance/slot_balance_page.dart'; | ||
import 'package:breez/routes/satscard_balance/sweep_slot_page.dart'; | ||
import 'package:breez/services/breezlib/data/messages.pb.dart'; | ||
import 'package:breez/services/injector.dart'; | ||
import 'package:breez/theme_data.dart' as theme; | ||
import 'package:breez/utils/min_font_size.dart'; | ||
import 'package:breez/widgets/circular_progress.dart'; | ||
import 'package:breez/widgets/flushbar.dart'; | ||
import 'package:breez/widgets/warning_box.dart'; | ||
import 'package:breez_translations/generated/breez_translations.dart'; | ||
import 'package:cktap_protocol/cktapcard.dart'; | ||
import 'package:fixnum/fixnum.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
class SatscardBalancePage extends StatefulWidget { | ||
final Satscard _card; | ||
final Slot _slot; | ||
|
||
const SatscardBalancePage(this._card, this._slot); | ||
|
||
@override | ||
State<StatefulWidget> createState() => SatscardBalancePageState(); | ||
} | ||
|
||
class SatscardBalancePageState extends State<SatscardBalancePage> { | ||
final _pageController = PageController(); | ||
|
||
AddressInfo _recentAddressInfo; | ||
RawSlotSweepTransaction _selectedTransaction; | ||
Uint8List _slotPrivateKey; | ||
|
||
@override | ||
void dispose() { | ||
_pageController.dispose(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
body: PageView( | ||
controller: _pageController, | ||
physics: const NeverScrollableScrollPhysics(), | ||
children: [ | ||
SlotBalancePage( | ||
widget._card, | ||
widget._slot, | ||
onBack: () => Navigator.pop(context), | ||
onSweep: (balance) { | ||
_recentAddressInfo = balance; | ||
_pageController.nextPage( | ||
duration: const Duration(milliseconds: 250), | ||
curve: Curves.easeInOut, | ||
); | ||
}, | ||
), | ||
SweepSlotPage( | ||
widget._card, | ||
widget._slot, | ||
onBack: () => _pageController.previousPage( | ||
duration: const Duration(milliseconds: 250), | ||
curve: Curves.easeInOut, | ||
), | ||
onUnsealed: (transaction, privateKey) { | ||
_selectedTransaction = transaction; | ||
_slotPrivateKey = privateKey; | ||
_pageController.nextPage( | ||
duration: const Duration(milliseconds: 250), | ||
curve: Curves.easeInOut, | ||
); | ||
}, | ||
getAddressInfo: () => _recentAddressInfo, | ||
getCachedPrivateKey: () { | ||
// Allow for unsealed slots | ||
if (_slotPrivateKey != null && _slotPrivateKey.isEmpty) { | ||
return widget._slot.privkey; | ||
} | ||
return _slotPrivateKey; | ||
}, | ||
), | ||
BroadcastSlotSweepTransactionPage( | ||
onBack: () => _pageController.previousPage( | ||
duration: const Duration(milliseconds: 250), | ||
curve: Curves.easeInOut, | ||
), | ||
onDone: () => Navigator.of(context).pop(), | ||
getAddressInfo: () => _recentAddressInfo, | ||
getPrivateKey: () => _slotPrivateKey, | ||
getTransaction: () => _selectedTransaction, | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} | ||
|
||
Widget buildErrorBody(ThemeData themeData, String title) { | ||
return Stack( | ||
children: <Widget>[ | ||
Positioned.fill( | ||
child: Column( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
children: [ | ||
buildWarning(themeData, title: title), | ||
], | ||
), | ||
), | ||
], | ||
); | ||
} | ||
|
||
Widget buildLoaderBody(ThemeData themeData, String title) { | ||
return Stack( | ||
children: <Widget>[ | ||
Positioned.fill( | ||
child: buildIndicator(themeData, title: title), | ||
), | ||
], | ||
); | ||
} | ||
|
||
Widget buildIndicator(ThemeData themeData, {String title}) { | ||
return CircularProgress( | ||
size: 64, | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
crossAxisAlignment: CrossAxisAlignment.center, | ||
color: themeData.progressIndicatorTheme.color, | ||
title: title, | ||
); | ||
} | ||
|
||
ListTile buildSlotPageTextTile( | ||
BuildContext context, | ||
MinFontSize minFont, { | ||
String titleText, | ||
Color titleColor, | ||
String trailingText, | ||
Color trailingColor, | ||
String copyMessage, | ||
}) { | ||
final style = theme.FieldTextStyle.labelStyle | ||
.copyWith(color: titleColor ?? Colors.white); | ||
final trailing = AutoSizeText( | ||
trailingText ?? "", | ||
style: style.copyWith(color: trailingColor ?? Colors.white), | ||
maxLines: 1, | ||
minFontSize: minFont.minFontSize, | ||
stepGranularity: 0.1, | ||
); | ||
|
||
return ListTile( | ||
visualDensity: const VisualDensity(horizontal: 0, vertical: -4), | ||
title: AutoSizeText( | ||
titleText ?? "", | ||
style: style, | ||
maxLines: 1, | ||
minFontSize: minFont.minFontSize, | ||
stepGranularity: 0.1, | ||
), | ||
trailing: copyMessage == null | ||
? trailing | ||
: GestureDetector( | ||
onTap: () { | ||
ServiceInjector().device.setClipboardText(trailingText); | ||
showFlushbar(context, message: copyMessage); | ||
}, | ||
child: trailing, | ||
), | ||
); | ||
} | ||
|
||
Widget buildWarning(ThemeData themeData, {String title}) { | ||
return WarningBox( | ||
child: Text( | ||
title, | ||
style: themeData.textTheme.titleLarge, | ||
textAlign: TextAlign.left, | ||
), | ||
); | ||
} | ||
|
||
String formatBalanceValue( | ||
BreezTranslations texts, AccountModel acc, Int64 sats) { | ||
if (acc == null) { | ||
return sats.toString(); | ||
} | ||
final satsString = acc.currency.format(sats); | ||
if (acc.fiatCurrency == null || sats <= 0) { | ||
return texts.satscard_balance_value_no_fiat(satsString); | ||
} else { | ||
final fiat = acc.fiatCurrency.format(sats); | ||
return texts.satscard_balance_value_with_fiat(satsString, fiat); | ||
} | ||
} |
Oops, something went wrong.