WIP firo redesign

This commit is contained in:
julian 2022-09-06 17:27:14 -06:00
parent 94941dfb94
commit fbc398bc14
9 changed files with 938 additions and 159 deletions

View file

@ -13,7 +13,9 @@ import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selectio
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart'; import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart'; import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/address_utils.dart'; import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -37,6 +39,8 @@ import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'sub_widgets/firo_balance_selection_sheet.dart';
class SendView extends ConsumerStatefulWidget { class SendView extends ConsumerStatefulWidget {
const SendView({ const SendView({
Key? key, Key? key,
@ -82,6 +86,9 @@ class _SendViewState extends ConsumerState<SendView> {
Decimal? _cachedAmountToSend; Decimal? _cachedAmountToSend;
String? _address; String? _address;
String? _privateBalanceString;
String? _publicBalanceString;
bool _addressToggleFlag = false; bool _addressToggleFlag = false;
bool _cryptoAmountChangeLock = false; bool _cryptoAmountChangeLock = false;
@ -196,6 +203,26 @@ class _SendViewState extends ConsumerState<SendView> {
return cachedFees[amount]!; return cachedFees[amount]!;
} }
Future<String?> _firoBalanceFuture(
ChangeNotifierProvider<Manager> provider, String locale) async {
final wallet = ref.read(provider).wallet as FiroWallet?;
if (wallet != null) {
Decimal? balance;
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
balance = await wallet.availablePrivateBalance();
} else {
balance = await wallet.availablePublicBalance();
}
return Format.localizedStringAsFixed(
value: balance, locale: locale, decimalPlaces: 8);
}
return null;
}
@override @override
void initState() { void initState() {
ref.refresh(feeSheetSessionCacheProvider); ref.refresh(feeSheetSessionCacheProvider);
@ -334,21 +361,59 @@ class _SendViewState extends ConsumerState<SendView> {
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
Assets.svg.iconFor(coin: coin), Assets.svg.iconFor(coin: coin),
width: 18, width: 22,
height: 18, height: 22,
), ),
const SizedBox( const SizedBox(
width: 6, width: 6,
), ),
Text( if (coin != Coin.firo &&
ref.watch(provider coin != Coin.firoTestNet)
.select((value) => value.walletName)), Text(
style: STextStyles.titleBold12, ref.watch(provider
), .select((value) => value.walletName)),
style: STextStyles.titleBold12,
),
if (coin == Coin.firo ||
coin == Coin.firoTestNet)
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
ref.watch(provider.select(
(value) => value.walletName)),
style: STextStyles.titleBold12
.copyWith(fontSize: 14),
),
// const SizedBox(
// height: 2,
// ),
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
style: STextStyles.label
.copyWith(fontSize: 10),
),
],
),
const Spacer(), const Spacer(),
FutureBuilder( FutureBuilder(
future: ref.watch(provider.select( future: (coin != Coin.firo &&
(value) => value.availableBalance)), coin != Coin.firoTestNet)
? ref.watch(provider.select(
(value) => value.availableBalance))
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? (ref.watch(provider).wallet
as FiroWallet)
.availablePrivateBalance()
: (ref.watch(provider).wallet
as FiroWallet)
.availablePublicBalance(),
builder: builder:
(_, AsyncSnapshot<Decimal> snapshot) { (_, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState == if (snapshot.connectionState ==
@ -423,6 +488,9 @@ class _SendViewState extends ConsumerState<SendView> {
fontSize: 10, fontSize: 10,
), ),
), ),
const SizedBox(
height: 2,
),
AnimatedText( AnimatedText(
stringsToLoopThrough: const [ stringsToLoopThrough: const [
"Loading balance ", "Loading balance ",
@ -730,6 +798,141 @@ class _SendViewState extends ConsumerState<SendView> {
} }
}, },
), ),
if (coin == Coin.firo)
const SizedBox(
height: 12,
),
if (coin == Coin.firo)
Text(
"Send from",
style: STextStyles.smallMed12,
textAlign: TextAlign.left,
),
if (coin == Coin.firo)
const SizedBox(
height: 8,
),
if (coin == Coin.firo)
Stack(
children: [
const TextField(
readOnly: true,
textInputAction: TextInputAction.none,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
),
child: RawMaterialButton(
splashColor: CFColors.splashLight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => FiroBalanceSelectionSheet(
walletId: walletId,
),
);
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
style: STextStyles.itemSubtitle12,
),
const SizedBox(
width: 10,
),
FutureBuilder(
future: _firoBalanceFuture(
provider, locale),
builder: (context,
AsyncSnapshot<String?>
snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
_privateBalanceString =
snapshot.data!;
} else {
_publicBalanceString =
snapshot.data!;
}
}
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private" &&
_privateBalanceString !=
null) {
return Text(
"$_privateBalanceString ${coin.ticker}",
style:
STextStyles.itemSubtitle,
);
} else if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Public" &&
_publicBalanceString !=
null) {
return Text(
"$_publicBalanceString ${coin.ticker}",
style:
STextStyles.itemSubtitle,
);
} else {
return AnimatedText(
stringsToLoopThrough: const [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance...",
],
style:
STextStyles.itemSubtitle,
);
}
},
),
],
),
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: CFColors.gray3,
),
],
),
),
)
],
),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
@ -744,10 +947,34 @@ class _SendViewState extends ConsumerState<SendView> {
BlueTextButton( BlueTextButton(
text: "Send all ${coin.ticker}", text: "Send all ${coin.ticker}",
onTap: () async { onTap: () async {
cryptoAmountController.text = (await ref if (coin == Coin.firo ||
.read(provider) coin == Coin.firoTestNet) {
.availableBalance) final firoWallet =
.toStringAsFixed(Constants.decimalPlaces); ref.read(provider).wallet as FiroWallet;
if (ref
.read(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private") {
cryptoAmountController.text =
(await firoWallet
.availablePrivateBalance())
.toStringAsFixed(
Constants.decimalPlaces);
} else {
cryptoAmountController.text =
(await firoWallet
.availablePublicBalance())
.toStringAsFixed(
Constants.decimalPlaces);
}
} else {
cryptoAmountController.text = (await ref
.read(provider)
.availableBalance)
.toStringAsFixed(Constants.decimalPlaces);
}
}, },
), ),
], ],

View file

@ -0,0 +1,287 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/animated_text.dart';
class FiroBalanceSelectionSheet extends ConsumerStatefulWidget {
const FiroBalanceSelectionSheet({
Key? key,
required this.walletId,
}) : super(key: key);
final String walletId;
@override
ConsumerState<FiroBalanceSelectionSheet> createState() =>
_FiroBalanceSelectionSheetState();
}
class _FiroBalanceSelectionSheetState
extends ConsumerState<FiroBalanceSelectionSheet> {
late final String walletId;
final stringsToLoopThrough = [
"Loading balance",
"Loading balance.",
"Loading balance..",
"Loading balance...",
];
@override
void initState() {
walletId = widget.walletId;
super.initState();
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)));
final firoWallet = manager.wallet as FiroWallet;
return Container(
decoration: const BoxDecoration(
color: CFColors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Padding(
padding: const EdgeInsets.only(
left: 24,
right: 24,
top: 10,
bottom: 0,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: CFColors.fieldGray,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
width: 60,
height: 4,
),
),
const SizedBox(
height: 36,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Select balance",
style: STextStyles.pageTitleH2,
textAlign: TextAlign.left,
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Private") {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Private";
}
Navigator.of(context).pop();
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: CFColors.link2,
value: "Private",
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
.state,
onChanged: (x) {
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Private";
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
Text(
"Private balance",
style: STextStyles.titleBold12.copyWith(
color: const Color(0xFF44464E),
),
textAlign: TextAlign.left,
),
const SizedBox(
width: 2,
),
FutureBuilder(
future: firoWallet.availablePrivateBalance(),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle,
textAlign: TextAlign.left,
);
} else {
return AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle,
);
}
},
)
],
),
// ],
// ),
)
],
),
),
),
const SizedBox(
height: 16,
),
GestureDetector(
onTap: () {
final state =
ref.read(publicPrivateBalanceStateProvider.state).state;
if (state != "Public") {
ref.read(publicPrivateBalanceStateProvider.state).state =
"Public";
}
Navigator.of(context).pop();
},
child: Container(
color: Colors.transparent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: CFColors.link2,
value: "Public",
groupValue: ref
.watch(
publicPrivateBalanceStateProvider.state)
.state,
onChanged: (x) {
ref
.read(publicPrivateBalanceStateProvider
.state)
.state = "Public";
Navigator.of(context).pop();
},
),
),
],
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
Text(
"Public balance",
style: STextStyles.titleBold12.copyWith(
color: const Color(0xFF44464E),
),
textAlign: TextAlign.left,
),
const SizedBox(
width: 2,
),
FutureBuilder(
future: firoWallet.availablePublicBalance(),
builder:
(context, AsyncSnapshot<Decimal> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
return Text(
"${snapshot.data!} ${manager.coin.ticker}",
style: STextStyles.itemSubtitle,
textAlign: TextAlign.left,
);
} else {
return AnimatedText(
stringsToLoopThrough:
stringsToLoopThrough,
style: STextStyles.itemSubtitle,
);
}
},
)
// ],
// ),
],
),
),
],
),
),
),
const SizedBox(
height: 16,
),
const SizedBox(
height: 24,
),
],
),
],
),
),
);
}
}

View file

@ -1,8 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart'; import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart'; import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
@ -18,6 +20,9 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final maxHeight = MediaQuery.of(context).size.height * 0.60; final maxHeight = MediaQuery.of(context).size.height * 0.60;
final coin = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId).coin));
return Container( return Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: CFColors.white, color: CFColors.white,
@ -105,25 +110,44 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox( const SizedBox(
width: 12, width: 12,
), ),
Column( if (coin != Coin.firo && coin != Coin.firoTestNet)
crossAxisAlignment: CrossAxisAlignment.start, Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Text( children: [
"Available balance", Text(
style: STextStyles.titleBold12, "Available balance",
), style: STextStyles.titleBold12,
const SizedBox(
height: 2,
),
// TODO need text from design
Text(
"Current spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
), ),
), const SizedBox(
], height: 2,
), ),
Text(
"Current spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
if (coin == Coin.firo || coin == Coin.firoTestNet)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Private balance",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 2,
),
Text(
"Current private spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
], ],
), ),
), ),
@ -172,25 +196,44 @@ class WalletBalanceToggleSheet extends ConsumerWidget {
const SizedBox( const SizedBox(
width: 12, width: 12,
), ),
Column( if (coin != Coin.firo && coin != Coin.firoTestNet)
crossAxisAlignment: CrossAxisAlignment.start, Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Text( children: [
"Full balance", Text(
style: STextStyles.titleBold12, "Full balance",
), style: STextStyles.titleBold12,
const SizedBox(
height: 2,
),
// TODO need text from design
Text(
"Total wallet balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
), ),
), const SizedBox(
], height: 2,
), ),
Text(
"Total wallet balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
if (coin == Coin.firo || coin == Coin.firoTestNet)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Public balance",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 2,
),
Text(
"Current public spendable (unlocked) balance",
style: STextStyles.itemSubtitle12.copyWith(
color: CFColors.neutral60,
),
),
],
),
], ],
), ),
), ),

View file

@ -6,6 +6,7 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_balance_toggle_
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_refresh_button.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart'; import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -67,15 +68,25 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
Expanded( Expanded(
child: Consumer( child: Consumer(
builder: (_, ref, __) { builder: (_, ref, __) {
final totalBalanceFuture = ref
.watch(managerProvider.select((value) => value.totalBalance));
final availableBalanceFuture = ref.watch(
managerProvider.select((value) => value.availableBalance));
final Coin coin = final Coin coin =
ref.watch(managerProvider.select((value) => value.coin)); ref.watch(managerProvider.select((value) => value.coin));
Future<Decimal>? totalBalanceFuture;
Future<Decimal>? availableBalanceFuture;
if (coin == Coin.firo || coin == Coin.firoTestNet) {
final firoWallet =
ref.watch(managerProvider.select((value) => value.wallet))
as FiroWallet;
totalBalanceFuture = firoWallet.availablePublicBalance();
availableBalanceFuture = firoWallet.availablePrivateBalance();
} else {
totalBalanceFuture = ref.watch(
managerProvider.select((value) => value.totalBalance));
availableBalanceFuture = ref.watch(
managerProvider.select((value) => value.availableBalance));
}
final locale = ref.watch(localeServiceChangeNotifierProvider final locale = ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale)); .select((value) => value.locale));
@ -114,12 +125,20 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
onTap: showSheet, onTap: showSheet,
child: Row( child: Row(
children: [ children: [
Text( if (coin == Coin.firo || coin == Coin.firoTestNet)
"${_showAvailable ? "Available" : "Full"} Balance", Text(
style: STextStyles.subtitle.copyWith( "${_showAvailable ? "Private" : "Public"} Balance",
fontWeight: FontWeight.w500, style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
),
if (coin != Coin.firo && coin != Coin.firoTestNet)
Text(
"${_showAvailable ? "Available" : "Full"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
), ),
),
const SizedBox( const SizedBox(
width: 4, width: 4,
), ),
@ -166,12 +185,20 @@ class _WalletSummaryInfoState extends State<WalletSummaryInfo> {
onTap: showSheet, onTap: showSheet,
child: Row( child: Row(
children: [ children: [
Text( if (coin == Coin.firo || coin == Coin.firoTestNet)
"${_showAvailable ? "Available" : "Full"} Balance", Text(
style: STextStyles.subtitle.copyWith( "${_showAvailable ? "Private" : "Public"} Balance",
fontWeight: FontWeight.w500, style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
),
if (coin != Coin.firo && coin != Coin.firoTestNet)
Text(
"${_showAvailable ? "Available" : "Full"} Balance",
style: STextStyles.subtitle.copyWith(
fontWeight: FontWeight.w500,
),
), ),
),
const SizedBox( const SizedBox(
width: 4, width: 4,
), ),

View file

@ -71,11 +71,11 @@ class _TransactionDetailsViewState
fee = Format.satoshisToAmount(_transaction.fees); fee = Format.satoshisToAmount(_transaction.fees);
amountPrefix = _transaction.txType.toLowerCase() == "sent" ? "- " : "+ "; amountPrefix = _transaction.txType.toLowerCase() == "sent" ? "- " : "+ ";
if (coin == Coin.firo || coin == Coin.firoTestNet) { // if (coin == Coin.firo || coin == Coin.firoTestNet) {
showFeePending = true; // showFeePending = true;
} else { // } else {
showFeePending = false; // showFeePending = false;
} // }
super.initState(); super.initState();
} }
@ -86,9 +86,10 @@ class _TransactionDetailsViewState
String whatIsIt(String type) { String whatIsIt(String type) {
if (type == "Received") { if (type == "Received") {
if (_transaction.isMinting) { // if (_transaction.isMinting) {
return "Minting"; // return "Minting";
} else if (_transaction.confirmedStatus) { // } else
if (_transaction.confirmedStatus) {
return "Received"; return "Received";
} else { } else {
return "Receiving"; return "Receiving";
@ -507,83 +508,83 @@ class _TransactionDetailsViewState
], ],
), ),
), ),
if ((coin == Coin.firoTestNet || coin == Coin.firo) && // if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
_transaction.subType == "mint") // _transaction.subType == "mint")
const SizedBox( // const SizedBox(
height: 12, // height: 12,
), // ),
if ((coin == Coin.firoTestNet || coin == Coin.firo) && // if ((coin == Coin.firoTestNet || coin == Coin.firo) &&
_transaction.subType == "mint") // _transaction.subType == "mint")
RoundedWhiteContainer( // RoundedWhiteContainer(
child: Column( // child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
children: [ // children: [
Row( // Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ // children: [
Text( // Text(
"Mint Transaction ID", // "Mint Transaction ID",
style: STextStyles.itemSubtitle, // style: STextStyles.itemSubtitle,
), // ),
], // ],
), // ),
const SizedBox( // const SizedBox(
height: 8, // height: 8,
), // ),
// Flexible( // // Flexible(
// child: FittedBox( // // child: FittedBox(
// fit: BoxFit.scaleDown, // // fit: BoxFit.scaleDown,
// child: // // child:
SelectableText( // SelectableText(
_transaction.otherData ?? "Unknown", // _transaction.otherData ?? "Unknown",
style: STextStyles.itemSubtitle12, // style: STextStyles.itemSubtitle12,
), // ),
// ), // // ),
// ), // // ),
const SizedBox( // const SizedBox(
height: 8, // height: 8,
), // ),
BlueTextButton( // BlueTextButton(
text: "Open in block explorer", // text: "Open in block explorer",
onTap: () async { // onTap: () async {
final uri = getBlockExplorerTransactionUrlFor( // final uri = getBlockExplorerTransactionUrlFor(
coin: coin, // coin: coin,
txid: _transaction.otherData ?? "Unknown", // txid: _transaction.otherData ?? "Unknown",
); // );
// ref // // ref
// .read( // // .read(
// shouldShowLockscreenOnResumeStateProvider // // shouldShowLockscreenOnResumeStateProvider
// .state) // // .state)
// .state = false; // // .state = false;
try { // try {
await launchUrl( // await launchUrl(
uri, // uri,
mode: LaunchMode.externalApplication, // mode: LaunchMode.externalApplication,
); // );
} catch (_) { // } catch (_) {
showDialog<void>( // unawaited(showDialog<void>(
context: context, // context: context,
builder: (_) => StackOkDialog( // builder: (_) => StackOkDialog(
title: "Could not open in block explorer", // title: "Could not open in block explorer",
message: // message:
"Failed to open \"${uri.toString()}\"", // "Failed to open \"${uri.toString()}\"",
), // ),
); // ));
} finally { // } finally {
// Future<void>.delayed( // // Future<void>.delayed(
// const Duration(seconds: 1), // // const Duration(seconds: 1),
// () => ref // // () => ref
// .read( // // .read(
// shouldShowLockscreenOnResumeStateProvider // // shouldShowLockscreenOnResumeStateProvider
// .state) // // .state)
// .state = true, // // .state = true,
// ); // // );
} // }
}, // },
), // ),
], // ],
), // ),
), // ),
if (coin == Coin.epicCash) if (coin == Coin.epicCash)
const SizedBox( const SizedBox(
height: 12, height: 12,

View file

@ -1,9 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:event_bus/event_bus.dart'; import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart'; import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/home_view/home_view.dart';
@ -22,6 +24,7 @@ import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
@ -31,12 +34,18 @@ import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart'; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import '../../providers/wallet/public_private_balance_state_provider.dart';
import '../../providers/wallet/wallet_balance_toggle_state_provider.dart';
import '../../utilities/enums/wallet_balance_toggle_state.dart';
/// [eventBus] should only be set during testing /// [eventBus] should only be set during testing
class WalletView extends ConsumerStatefulWidget { class WalletView extends ConsumerStatefulWidget {
const WalletView({ const WalletView({
@ -271,10 +280,78 @@ class _WalletViewState extends ConsumerState<WalletView> {
} }
} }
Future<void> attemptAnonymize() async {
bool shouldPop = false;
unawaited(
showDialog(
context: context,
builder: (context) => WillPopScope(
child: const CustomLoadingOverlay(
message: "Anonymizing balance",
eventBus: null,
),
onWillPop: () async => shouldPop,
),
),
);
final firoWallet = ref.read(managerProvider).wallet as FiroWallet;
final publicBalance = await firoWallet.availablePublicBalance();
if (publicBalance <= Decimal.zero) {
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "No funds available to anonymize!",
context: context,
),
);
}
return;
}
try {
await firoWallet.anonymizeAllPublicFunds();
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
);
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "Anonymize transaction submitted",
context: context,
),
);
}
} catch (e) {
shouldPop = true;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(WalletView.routeName),
);
await showDialog<dynamic>(
context: context,
builder: (_) => StackOkDialog(
title: "Anonymize all failed",
message: "Reason: $e",
),
);
}
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
final coin = ref.watch(managerProvider.select((value) => value.coin));
return WillPopScope( return WillPopScope(
onWillPop: _onWillPop, onWillPop: _onWillPop,
child: Scaffold( child: Scaffold(
@ -283,9 +360,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
title: Row( title: Row(
children: [ children: [
SvgPicture.asset( SvgPicture.asset(
Assets.svg.iconFor( Assets.svg.iconFor(coin: coin),
coin: ref
.watch(managerProvider.select((value) => value.coin))),
// color: CFColors.stackAccent, // color: CFColors.stackAccent,
width: 24, width: 24,
height: 24, height: 24,
@ -440,6 +515,69 @@ class _WalletViewState extends ConsumerState<WalletView> {
), ),
), ),
), ),
if (coin == Coin.firo)
const SizedBox(
height: 10,
),
if (coin == Coin.firo)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: TextButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (context) => StackDialog(
title: "Attention!",
message:
"You're about to anonymize all of your public funds.",
leftButton: TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Cancel",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
),
),
rightButton: TextButton(
onPressed: () async {
Navigator.of(context).pop();
unawaited(attemptAnonymize());
},
style: Theme.of(context)
.textButtonTheme
.style
?.copyWith(
backgroundColor:
MaterialStateProperty.all<Color>(
CFColors.stackAccent,
),
),
child: Text(
"Continue",
style: STextStyles.button,
),
),
),
);
},
child: Text(
"Anonymize funds",
style: STextStyles.button.copyWith(
color: CFColors.stackAccent,
),
),
),
),
],
),
),
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
@ -550,6 +688,25 @@ class _WalletViewState extends ConsumerState<WalletView> {
ref.read(managerProvider).walletId; ref.read(managerProvider).walletId;
final coin = final coin =
ref.read(managerProvider).coin; ref.read(managerProvider).coin;
switch (ref
.read(walletBalanceToggleStateProvider
.state)
.state) {
case WalletBalanceToggleState.full:
ref
.read(
publicPrivateBalanceStateProvider
.state)
.state = "Public";
break;
case WalletBalanceToggleState.available:
ref
.read(
publicPrivateBalanceStateProvider
.state)
.state = "Private";
break;
}
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
SendView.routeName, SendView.routeName,
arguments: Tuple2( arguments: Tuple2(

View file

@ -0,0 +1,4 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
final publicPrivateBalanceStateProvider =
StateProvider<String>((_) => "Private");

View file

@ -1761,7 +1761,12 @@ class FiroWallet extends CoinServiceAPI {
balances.add( balances.add(
(lelantusBalance + utxosValue + _unconfirmedLelantusBalance) * price); (lelantusBalance + utxosValue + _unconfirmedLelantusBalance) * price);
balances.add(utxosValue); int availableSats =
utxos.satoshiBalance - utxos.satoshiBalanceUnconfirmed;
if (availableSats < 0) {
availableSats = 0;
}
balances.add(Format.satoshisToAmount(availableSats));
Logging.instance.log("balances $balances", level: LogLevel.Info); Logging.instance.log("balances $balances", level: LogLevel.Info);
await DB.instance.put<dynamic>( await DB.instance.put<dynamic>(
@ -2782,6 +2787,7 @@ class FiroWallet extends CoinServiceAPI {
Decimal currentPrice = await firoPrice; Decimal currentPrice = await firoPrice;
final List<Map<String, dynamic>> outputArray = []; final List<Map<String, dynamic>> outputArray = [];
int satoshiBalance = 0; int satoshiBalance = 0;
int satoshiBalancePending = 0;
for (int i = 0; i < utxoData.length; i++) { for (int i = 0; i < utxoData.length; i++) {
for (int j = 0; j < utxoData[i].length; j++) { for (int j = 0; j < utxoData[i].length; j++) {
@ -2795,16 +2801,22 @@ class FiroWallet extends CoinServiceAPI {
); );
final Map<String, dynamic> tx = {}; final Map<String, dynamic> tx = {};
final int confirmations = txn["confirmations"] as int? ?? 0;
final bool confirmed = confirmations >= MINIMUM_CONFIRMATIONS;
if (!confirmed) {
satoshiBalancePending += value;
}
tx["txid"] = txn["txid"]; tx["txid"] = txn["txid"];
tx["vout"] = utxoData[i][j]["tx_pos"]; tx["vout"] = utxoData[i][j]["tx_pos"];
tx["value"] = value; tx["value"] = value;
tx["status"] = <String, dynamic>{}; tx["status"] = <String, dynamic>{};
tx["status"]["confirmed"] = confirmed;
tx["status"]["confirmations"] = confirmations;
tx["status"]["confirmed"] = tx["status"]["confirmed"] =
txn["confirmations"] == null ? false : txn["confirmations"] > 0; txn["confirmations"] == null ? false : txn["confirmations"] > 0;
tx["status"]["confirmations"] =
txn["confirmations"] == null ? 0 : txn["confirmations"]!;
tx["status"]["block_height"] = txn["height"]; tx["status"]["block_height"] = txn["height"];
tx["status"]["block_hash"] = txn["blockhash"]; tx["status"]["block_hash"] = txn["blockhash"];
tx["status"]["block_time"] = txn["blocktime"]; tx["status"]["block_time"] = txn["blocktime"];
@ -2832,6 +2844,7 @@ class FiroWallet extends CoinServiceAPI {
.toDecimal(scaleOnInfinitePrecision: Constants.decimalPlaces) .toDecimal(scaleOnInfinitePrecision: Constants.decimalPlaces)
.toString(), .toString(),
"outputArray": outputArray, "outputArray": outputArray,
"unconfirmed": satoshiBalancePending,
}; };
final dataModel = UtxoData.fromJson(result); final dataModel = UtxoData.fromJson(result);

View file

@ -37,10 +37,30 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
if (coin == Coin.epicCash && _transaction.slateId == null) { if (coin == Coin.epicCash && _transaction.slateId == null) {
return "Restored Funds"; return "Restored Funds";
} }
if (_transaction.subType == "mint") {
// if (type == "Received") {
if (_transaction.confirmedStatus) {
return "Anonymized";
} else {
return "Anonymizing";
}
// } else if (type == "Sent") {
// if (_transaction.confirmedStatus) {
// return "Sent MINT";
// } else {
// return "Sending MINT";
// }
// } else {
// return type;
// }
}
if (type == "Received") { if (type == "Received") {
if (_transaction.isMinting) { // if (_transaction.isMinting) {
return "Minting"; // return "Minting";
} else if (_transaction.confirmedStatus) { // } else
if (_transaction.confirmedStatus) {
return "Received"; return "Received";
} else { } else {
return "Receiving"; return "Receiving";