Merge pull request #400 from cypherstack/coin_control

Coin control
This commit is contained in:
Diego Salazar 2023-03-10 21:36:33 -07:00 committed by GitHub
commit 8be8666f70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1720 additions and 2602 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
@ -29,7 +31,7 @@
<key>LSSupportsOpeningDocumentsInPlace</key> <key>LSSupportsOpeningDocumentsInPlace</key>
<true/> <true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>App requires access to the Camera to scan QR codes from other people's installed app in order to coordinate sending Firo.</string> <string>App requires access to the Camera to scan QR codes from other people's installed app in order to coordinate sending.</string>
<key>NSFaceIDUsageDescription</key> <key>NSFaceIDUsageDescription</key>
<string>This app requires Face ID permissions so that the user can securely lock their wallet if their device uses Face ID. It will be useful feature for all users, and especially those prone to forget their login pin on iPhones where Touch ID is not available.</string> <string>This app requires Face ID permissions so that the user can securely lock their wallet if their device uses Face ID. It will be useful feature for all users, and especially those prone to forget their login pin on iPhones where Touch ID is not available.</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>

View file

@ -28,8 +28,6 @@ class AddWalletNextButton extends ConsumerWidget {
final selectedCoin = final selectedCoin =
ref.read(addWalletSelectedCoinStateProvider.state).state; ref.read(addWalletSelectedCoinStateProvider.state).state;
//todo: check if print needed
// debugPrint("Next pressed with ${selectedCoin!.name} selected!");
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
CreateOrRestoreWalletView.routeName, CreateOrRestoreWalletView.routeName,
arguments: selectedCoin, arguments: selectedCoin,

View file

@ -89,20 +89,18 @@ class CreateOrRestoreWalletView extends StatelessWidget {
}, },
), ),
), ),
body: Container( body: SafeArea(
child: Container(
color: Theme.of(context).extension<StackColors>()!.background, color: Theme.of(context).extension<StackColors>()!.background,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Padding( CoinImage(
padding: const EdgeInsets.all(31),
child: CoinImage(
coin: coin, coin: coin,
isDesktop: isDesktop, isDesktop: isDesktop,
), ),
),
const Spacer( const Spacer(
flex: 2, flex: 2,
), ),
@ -128,6 +126,7 @@ class CreateOrRestoreWalletView extends StatelessWidget {
), ),
), ),
), ),
),
); );
} }
} }

View file

@ -18,7 +18,7 @@ class CoinImage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
return SvgPicture.asset( return SvgPicture.asset(
Assets.svg.imageFor(coin: coin, context: context), Assets.svg.imageFor(coin: coin, context: context),
width: isDesktop ? 324 : MediaQuery.of(context).size.width, width: isDesktop ? 324 : MediaQuery.of(context).size.width / 1.6,
); );
} }
} }

View file

@ -76,7 +76,7 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Locale locale = Localizations.localeOf(context); Locale locale = Localizations.localeOf(context);
var format = NumberFormat.simpleCurrency(locale: locale.toString()); final format = NumberFormat.simpleCurrency(locale: locale.toString());
// See https://stackoverflow.com/a/67055685 // See https://stackoverflow.com/a/67055685
final isDesktop = Util.isDesktop; final isDesktop = Util.isDesktop;
@ -186,21 +186,33 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
height: 12, height: 12,
), ),
Flexible( Flexible(
child: SingleChildScrollView(
child: RoundedWhiteContainer( child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
child: ListView.builder( child: Table(
shrinkWrap: true, columnWidths: const {
primary: isDesktop ? false : null, 0: IntrinsicColumnWidth(),
itemCount: _fiats.length, 1: FlexColumnWidth(),
itemBuilder: (builderContext, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: GestureDetector(
onTap: () {
Navigator.of(context).pop(_fiats[index]);
}, },
child: RoundedWhiteContainer( defaultVerticalAlignment: TableCellVerticalAlignment.middle,
child: Row( children: [
..._fiats.map(
(e) {
return TableRow(
children: [
TableCell(
verticalAlignment:
TableCellVerticalAlignment.fill,
child: GestureDetector(
onTap: () => Navigator.of(context).pop(e),
child: Container(
color: Colors.transparent,
padding: const EdgeInsets.only(left: 12),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(7.5), padding: const EdgeInsets.all(7.5),
@ -208,42 +220,52 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.currencyListItemBG, .currencyListItemBG,
borderRadius: BorderRadius.circular(4), borderRadius:
BorderRadius.circular(4),
), ),
child: Text( child: Text(
format.simpleCurrencySymbol( format.simpleCurrencySymbol(
_fiats[index].ticker.toUpperCase()), e.ticker.toUpperCase()),
style: STextStyles.subtitle(context).apply( style: STextStyles.subtitle(context)
.apply(
fontSizeFactor: (1 / fontSizeFactor: (1 /
format format
.simpleCurrencySymbol(_fiats[index] .simpleCurrencySymbol(
.ticker e.ticker.toUpperCase())
.toUpperCase())
.length * // Couldn't get pow() working here .length * // Couldn't get pow() working here
format format
.simpleCurrencySymbol(_fiats[index] .simpleCurrencySymbol(
.ticker e.ticker.toUpperCase())
.toUpperCase()) .length),
.length)), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
const SizedBox( ],
width: 10,
), ),
Expanded( ),
),
),
GestureDetector(
onTap: () => Navigator.of(context).pop(e),
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Text( Text(
_fiats[index].name, e.name,
style: STextStyles.largeMedium14(context), style:
STextStyles.largeMedium14(context),
), ),
const SizedBox( const SizedBox(
height: 2, height: 2,
), ),
Text( Text(
_fiats[index].ticker.toUpperCase(), e.ticker.toUpperCase(),
style: STextStyles.smallMed12(context) style: STextStyles.smallMed12(context)
.copyWith( .copyWith(
color: Theme.of(context) color: Theme.of(context)
@ -254,13 +276,89 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
], ],
), ),
), ),
),
),
], ],
),
),
),
); );
}, },
), ),
],
),
// child: ListView.builder(
// shrinkWrap: true,
// primary: isDesktop ? false : null,
// itemCount: _fiats.length,
// itemBuilder: (builderContext, index) {
// return Padding(
// padding: const EdgeInsets.symmetric(vertical: 4),
// child: GestureDetector(
// onTap: () {
// Navigator.of(context).pop(_fiats[index]);
// },
// child: RoundedWhiteContainer(
// child: Row(
// children: [
// Container(
// padding: const EdgeInsets.all(7.5),
// decoration: BoxDecoration(
// color: Theme.of(context)
// .extension<StackColors>()!
// .currencyListItemBG,
// borderRadius: BorderRadius.circular(4),
// ),
// child: Text(
// format.simpleCurrencySymbol(
// _fiats[index].ticker.toUpperCase()),
// style: STextStyles.subtitle(context).apply(
// fontSizeFactor: (1 /
// format
// .simpleCurrencySymbol(_fiats[index]
// .ticker
// .toUpperCase())
// .length * // Couldn't get pow() working here
// format
// .simpleCurrencySymbol(_fiats[index]
// .ticker
// .toUpperCase())
// .length)),
// textAlign: TextAlign.center,
// ),
// ),
// const SizedBox(
// width: 10,
// ),
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// _fiats[index].name,
// style: STextStyles.largeMedium14(context),
// ),
// const SizedBox(
// height: 2,
// ),
// Text(
// _fiats[index].ticker.toUpperCase(),
// style: STextStyles.smallMed12(context)
// .copyWith(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textSubtitle1,
// ),
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// ),
// );
// },
// ),
),
), ),
), ),
], ],

View file

@ -1,6 +1,5 @@
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:isar/isar.dart';
import 'package:stackwallet/db/main_db.dart'; import 'package:stackwallet/db/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart';
@ -36,7 +35,8 @@ class UtxoCard extends ConsumerStatefulWidget {
} }
class _UtxoCardState extends ConsumerState<UtxoCard> { class _UtxoCardState extends ConsumerState<UtxoCard> {
late final UTXO utxo; late Stream<UTXO?> stream;
late UTXO utxo;
late bool _selected; late bool _selected;
@ -44,6 +44,8 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
void initState() { void initState() {
_selected = widget.initialSelectedState; _selected = widget.initialSelectedState;
utxo = widget.utxo; utxo = widget.utxo;
stream = MainDB.instance.watchUTXO(id: utxo.id);
super.initState(); super.initState();
} }
@ -57,19 +59,6 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
final currentChainHeight = ref.watch(walletsChangeNotifierProvider final currentChainHeight = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId).currentHeight)); .select((value) => value.getManager(widget.walletId).currentHeight));
String? label;
if (utxo.address != null) {
label = MainDB.instance.isar.addressLabels
.where()
.addressStringWalletIdEqualTo(utxo.address!, widget.walletId)
.findFirstSync()
?.value;
if (label != null && label.isEmpty) {
label = null;
}
}
return ConditionalParent( return ConditionalParent(
condition: widget.onPressed != null, condition: widget.onPressed != null,
builder: (child) => MaterialButton( builder: (child) => MaterialButton(
@ -92,7 +81,13 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
color: widget.onPressed == null color: widget.onPressed == null
? Theme.of(context).extension<StackColors>()!.popupBG ? Theme.of(context).extension<StackColors>()!.popupBG
: Colors.transparent, : Colors.transparent,
child: Row( child: StreamBuilder<UTXO?>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
utxo = snapshot.data!;
}
return Row(
children: [ children: [
ConditionalParent( ConditionalParent(
condition: widget.canSelect, condition: widget.canSelect,
@ -112,7 +107,8 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
) )
? UTXOStatusIconStatus.confirmed ? UTXOStatusIconStatus.confirmed
: UTXOStatusIconStatus.unconfirmed, : UTXOStatusIconStatus.unconfirmed,
background: Theme.of(context).extension<StackColors>()!.popupBG, background:
Theme.of(context).extension<StackColors>()!.popupBG,
selected: _selected, selected: _selected,
width: 32, width: 32,
height: 32, height: 32,
@ -140,7 +136,9 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
children: [ children: [
Flexible( Flexible(
child: Text( child: Text(
label ?? utxo.address ?? utxo.txid, utxo.name.isNotEmpty
? utxo.name
: utxo.address ?? utxo.txid,
style: STextStyles.w500_12(context).copyWith( style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
@ -154,7 +152,8 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
), ),
), ),
], ],
), );
}),
), ),
); );
} }

View file

@ -176,11 +176,9 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
], ],
), ),
), ),
if (label != null && label!.value.isNotEmpty)
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
if (label != null && label!.value.isNotEmpty)
RoundedWhiteContainer( RoundedWhiteContainer(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -190,15 +188,23 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"Address label", "Label",
style: STextStyles.w500_14(context).copyWith( style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textSubtitle1, .textSubtitle1,
), ),
), ),
SimpleCopyButton( SimpleEditButton(
data: label!.value, editValue: utxo!.name,
editLabel: "label",
onValueChanged: (newName) {
MainDB.instance.putUTXO(
utxo!.copyWith(
name: newName,
),
);
},
), ),
], ],
), ),
@ -206,7 +212,7 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
height: 4, height: 4,
), ),
Text( Text(
label!.value, utxo!.name,
style: STextStyles.w500_14(context), style: STextStyles.w500_14(context),
), ),
], ],
@ -246,6 +252,42 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
], ],
), ),
), ),
if (label != null && label!.value.isNotEmpty)
const SizedBox(
height: _spacing,
),
if (label != null && label!.value.isNotEmpty)
RoundedWhiteContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Address label",
style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
SimpleCopyButton(
data: label!.value,
),
],
),
const SizedBox(
height: 4,
),
Text(
label!.value,
style: STextStyles.w500_14(context),
),
],
),
),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
@ -309,48 +351,6 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
RoundedWhiteContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Note",
style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
SimpleEditButton(
editValue: utxo!.name,
editLabel: "note",
onValueChanged: (newName) {
MainDB.instance.putUTXO(
utxo!.copyWith(
name: newName,
),
);
},
),
],
),
const SizedBox(
height: 4,
),
Text(
utxo!.name,
style: STextStyles.w500_14(context),
),
],
),
),
const SizedBox(
height: _spacing,
),
if (utxo!.isBlocked) if (utxo!.isBlocked)
Column( Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View file

@ -618,6 +618,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
if (walletInitiated) { if (walletInitiated) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ref.read(exchangeFormStateProvider).reset(shouldNotifyListeners: true); ref.read(exchangeFormStateProvider).reset(shouldNotifyListeners: true);
ExchangeDataLoadingService.instance
.getAggregateCurrency(
coin!.ticker,
ExchangeRateType.estimated,
)
.then((value) {
if (value != null) {
ref.read(exchangeFormStateProvider).updateSendCurrency(value, true);
}
});
}); });
} else { } else {
_sendController.text = _sendController.text =
@ -848,7 +858,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
enabled: ref.watch( enabled: ref.watch(
exchangeFormStateProvider.select((value) => value.canExchange)), exchangeFormStateProvider.select((value) => value.canExchange)),
onPressed: onExchangePressed, onPressed: onExchangePressed,
label: "Exchange", label: "Swap",
) )
], ],
); );

View file

@ -56,7 +56,7 @@ class _Step1ViewState extends State<Step1View> {
}, },
), ),
title: Text( title: Text(
"Exchange", "Swap",
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
), ),
), ),

View file

@ -144,7 +144,7 @@ class _Step2ViewState extends ConsumerState<Step2View> {
}, },
), ),
title: Text( title: Text(
"Exchange", "Swap",
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
), ),
), ),

View file

@ -72,7 +72,7 @@ class _Step3ViewState extends ConsumerState<Step3View> {
}, },
), ),
title: Text( title: Text(
"Exchange", "Swap",
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
), ),
), ),

View file

@ -152,7 +152,7 @@ class _Step4ViewState extends ConsumerState<Step4View> {
), ),
), ),
title: Text( title: Text(
"Exchange", "Swap",
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
), ),
), ),

View file

@ -322,7 +322,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
width: 16, width: 16,
), ),
SelectableText( SelectableText(
"Exchange", "Swap service",
style: STextStyles.desktopTextMedium(context), style: STextStyles.desktopTextMedium(context),
), ),
], ],
@ -1052,7 +1052,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Exchange", "Swap service",
style: STextStyles.itemSubtitle(context), style: STextStyles.itemSubtitle(context),
), ),
if (isDesktop) if (isDesktop)

View file

@ -127,7 +127,7 @@ class _WalletInitiatedExchangeViewState
}, },
), ),
title: Text( title: Text(
"Exchange", "Swap",
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
), ),
), ),

View file

@ -107,23 +107,8 @@ class _HomeViewState extends ConsumerState<HomeView> {
_rotateIconController = RotateIconController(); _rotateIconController = RotateIconController();
_children = [ _children = [
const WalletsView(), const WalletsView(),
if (Constants.enableExchange) if (Constants.enableExchange) const ExchangeView(),
Stack( if (Constants.enableExchange) const BuyView(),
children: [
const ExchangeView(),
// ExchangeLoadingOverlayView(
// unawaitedLoad: _loadCNData,
// ),
],
),
if (Constants.enableBuy)
// Stack(
// children: [
const BuyView(),
// BuyLoadingOverlayView(
// unawaitedLoad: _loadSimplexData,
// ),
// ],
]; ];
ref.read(notificationsProvider).startCheckingWatchedNotifications(); ref.read(notificationsProvider).startCheckingWatchedNotifications();

View file

@ -111,7 +111,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
// } // }
}, },
child: Text( child: Text(
"Exchange", "Swap",
style: STextStyles.button(context).copyWith( style: STextStyles.button(context).copyWith(
fontSize: 14, fontSize: 14,
color: selectedIndex == 1 color: selectedIndex == 1

View file

@ -1,592 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/coin_control/coin_control_view.dart';
import 'package:stackwallet/pages/paynym/paynym_claim_view.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:tuple/tuple.dart';
class WalletNavigationBar extends ConsumerStatefulWidget {
const WalletNavigationBar({
Key? key,
required this.onReceivePressed,
required this.onSendPressed,
required this.onExchangePressed,
required this.onBuyPressed,
required this.height,
required this.enableExchange,
required this.coin,
required this.walletId,
}) : super(key: key);
final VoidCallback onReceivePressed;
final VoidCallback onSendPressed;
final VoidCallback onExchangePressed;
final VoidCallback onBuyPressed;
final double height;
final bool enableExchange;
final Coin coin;
final String walletId;
@override
ConsumerState<WalletNavigationBar> createState() =>
_WalletNavigationBarState();
}
class _WalletNavigationBarState extends ConsumerState<WalletNavigationBar> {
double scale = 0;
final duration = const Duration(milliseconds: 200);
@override
Widget build(BuildContext context) {
final showMore = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(widget.walletId).hasPaynymSupport,
),
) ||
(ref.watch(
walletsChangeNotifierProvider.select(
(value) =>
value.getManager(widget.walletId).hasCoinControlSupport,
),
) &&
ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.enableCoinControl,
),
));
return Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// const Spacer(),
AnimatedScale(
scale: scale,
duration: duration,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// AnimatedOpacity(
// opacity: scale,
// duration: duration,
// child: GestureDetector(
// onTap: () {},
// child: Container(
// padding: const EdgeInsets.all(16),
// width: 146,
// decoration: BoxDecoration(
// color:
// Theme.of(context).extension<StackColors>()!.popupBG,
// boxShadow: [
// Theme.of(context)
// .extension<StackColors>()!
// .standardBoxShadow
// ],
// borderRadius: BorderRadius.circular(
// widget.height / 2.0,
// ),
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// "Whirlpool",
// style: STextStyles.w600_12(context),
// ),
// ],
// ),
// ),
// ),
// ),
// const SizedBox(
// height: 8,
// ),
if (ref.watch(
walletsChangeNotifierProvider.select(
(value) => value
.getManager(widget.walletId)
.hasCoinControlSupport,
),
) &&
ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.enableCoinControl,
),
))
AnimatedOpacity(
opacity: scale,
duration: duration,
child: GestureDetector(
onTap: () {
if (mounted) {
// hide more context menu
setState(() {
scale = 0;
});
Navigator.of(context).pushNamed(
CoinControlView.routeName,
arguments: Tuple2(
widget.walletId,
CoinControlViewType.manage,
),
);
}
},
child: Container(
padding: const EdgeInsets.all(16),
width: 146,
decoration: BoxDecoration(
color:
Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow
],
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Coin control",
style: STextStyles.buttonSmall(context),
),
const SizedBox(
width: 16,
),
SvgPicture.asset(
Assets.svg.coinControl.gamePad,
height: 20,
width: 20,
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavIconIcon,
),
],
),
),
),
),
if (ref.watch(
walletsChangeNotifierProvider.select(
(value) => value
.getManager(widget.walletId)
.hasCoinControlSupport,
),
) &&
ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.enableCoinControl,
),
) &&
ref.watch(
walletsChangeNotifierProvider.select(
(value) =>
value.getManager(widget.walletId).hasPaynymSupport,
),
))
const SizedBox(
height: 8,
),
if (ref.watch(walletsChangeNotifierProvider.select((value) =>
value.getManager(widget.walletId).hasPaynymSupport)))
AnimatedOpacity(
opacity: scale,
duration: duration,
child: Consumer(builder: (context, ref, __) {
return GestureDetector(
onTap: () async {
setState(() {
scale = 0;
});
unawaited(
showDialog(
context: context,
builder: (context) => const LoadingIndicator(
width: 100,
),
),
);
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId);
final paynymInterface =
manager.wallet as PaynymWalletInterface;
final code = await paynymInterface.getPaymentCode(
DerivePathTypeExt.primaryFor(manager.coin));
final account = await ref
.read(paynymAPIProvider)
.nym(code.toString());
Logging.instance.log(
"my nym account: $account",
level: LogLevel.Info,
);
if (mounted) {
Navigator.of(context).pop();
// check if account exists and for matching code to see if claimed
if (account.value != null &&
account.value!.codes.first.claimed) {
ref.read(myPaynymAccountStateProvider.state).state =
account.value!;
await Navigator.of(context).pushNamed(
PaynymHomeView.routeName,
arguments: widget.walletId,
);
} else {
await Navigator.of(context).pushNamed(
PaynymClaimView.routeName,
arguments: widget.walletId,
);
}
}
},
child: Container(
padding: const EdgeInsets.all(16),
width: 146,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.popupBG,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow
],
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Paynym",
style: STextStyles.buttonSmall(context),
),
const SizedBox(
width: 16,
),
SvgPicture.asset(
Assets.svg.robotHead,
height: 20,
width: 20,
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavIconIcon,
),
],
),
),
);
}),
),
const SizedBox(
height: 8,
),
],
),
),
Container(
height: widget.height,
decoration: BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.bottomNavBack,
boxShadow: [
Theme.of(context).extension<StackColors>()!.standardBoxShadow
],
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 4,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const SizedBox(
width: 12,
),
RawMaterialButton(
constraints: const BoxConstraints(
minWidth: 66,
),
onPressed: widget.onReceivePressed,
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(),
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.4),
borderRadius: BorderRadius.circular(
24,
),
),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: SvgPicture.asset(
Assets.svg.arrowDownLeft,
width: 12,
height: 12,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
const SizedBox(
height: 4,
),
Text(
"Receive",
style: STextStyles.buttonSmall(context),
),
const Spacer(),
],
),
),
),
),
RawMaterialButton(
constraints: const BoxConstraints(
minWidth: 66,
),
onPressed: widget.onSendPressed,
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(),
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.4),
borderRadius: BorderRadius.circular(
24,
),
),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: SvgPicture.asset(
Assets.svg.arrowUpRight,
width: 12,
height: 12,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
const SizedBox(
height: 4,
),
Text(
"Send",
style: STextStyles.buttonSmall(context),
),
const Spacer(),
],
),
),
),
),
if (widget.enableExchange)
RawMaterialButton(
constraints: const BoxConstraints(
minWidth: 66,
),
onPressed: widget.onExchangePressed,
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(),
SvgPicture.asset(
Assets.svg.exchange(context),
width: 24,
height: 24,
),
const SizedBox(
height: 4,
),
Text(
"Exchange",
style: STextStyles.buttonSmall(context),
),
const Spacer(),
],
),
),
),
),
if (widget.coin.hasBuySupport)
RawMaterialButton(
constraints: const BoxConstraints(
minWidth: 66,
),
onPressed: widget.onBuyPressed,
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(),
SvgPicture.asset(
Assets.svg.buy(context),
width: 24,
height: 24,
),
const SizedBox(
height: 4,
),
Text(
"Buy",
style: STextStyles.buttonSmall(context),
),
const Spacer(),
],
),
),
),
),
if (showMore)
RawMaterialButton(
constraints: const BoxConstraints(
minWidth: 66,
),
onPressed: () {
if (scale == 0) {
setState(() {
scale = 1;
});
} else if (scale == 1) {
setState(() {
scale = 0;
});
}
},
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Spacer(),
const SizedBox(
height: 2,
),
SvgPicture.asset(
Assets.svg.bars,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavIconIcon,
),
const SizedBox(
height: 6,
),
Text(
"More",
style: STextStyles.buttonSmall(context),
),
const Spacer(),
],
),
),
),
),
const SizedBox(
width: 12,
),
],
),
),
),
],
);
}
}

View file

@ -5,23 +5,29 @@ 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:isar/isar.dart';
import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart'; import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart';
import 'package:stackwallet/pages/coin_control/coin_control_view.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';
import 'package:stackwallet/pages/notification_views/notifications_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart';
import 'package:stackwallet/pages/paynym/paynym_claim_view.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/pages/receive_view/receive_view.dart'; import 'package:stackwallet/pages/receive_view/receive_view.dart';
import 'package:stackwallet/pages/send_view/send_view.dart'; import 'package:stackwallet/pages/send_view/send_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart';
import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart';
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/global/paynym_api_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/providers/wallet/my_paynym_account_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart'; import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.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/firo/firo_wallet.dart';
@ -29,11 +35,15 @@ 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';
import 'package:stackwallet/services/event_bus/global_event_bus.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.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/derive_path_type_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/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/background.dart';
@ -41,7 +51,16 @@ import 'package:stackwallet/widgets/conditional_parent.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/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/wallet_navigation_bar.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
/// [eventBus] should only be set during testing /// [eventBus] should only be set during testing
@ -239,19 +258,14 @@ class _WalletViewState extends ConsumerState<WalletView> {
} }
void _onExchangePressed(BuildContext context) async { void _onExchangePressed(BuildContext context) async {
// too expensive
// unawaited(ExchangeDataLoadingService.instance.loadAll(ref));
final coin = ref.read(managerProvider).coin; final coin = ref.read(managerProvider).coin;
if (coin == Coin.epicCash) { final currency = ExchangeDataLoadingService.instance.isar.currencies
await showDialog<void>( .where()
context: context, .tickerEqualToAnyExchangeNameName(coin.ticker)
builder: (_) => const StackOkDialog( .findFirstSync();
title: "Exchange not available for Epic Cash",
), if (coin.isTestNet) {
);
} else if (coin.name.endsWith("TestNet")) {
await showDialog<void>( await showDialog<void>(
context: context, context: context,
builder: (_) => const StackOkDialog( builder: (_) => const StackOkDialog(
@ -259,41 +273,13 @@ class _WalletViewState extends ConsumerState<WalletView> {
), ),
); );
} else { } else {
// ref.read(currentExchangeNameStateProvider.state).state =
// ChangeNowExchange.exchangeName;
// final walletId = ref.read(managerProvider).walletId;
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
// ExchangeRateType.estimated;
//
// final currencies = ref
// .read(availableChangeNowCurrenciesProvider)
// .currencies
// .where((element) =>
// element.ticker.toLowerCase() == coin.ticker.toLowerCase());
//
// if (currencies.isNotEmpty) {
// ref
// .read(exchangeFormStateProvider(ExchangeRateType.estimated))
// .setCurrencies(
// currencies.first,
// ref
// .read(availableChangeNowCurrenciesProvider)
// .currencies
// .firstWhere(
// (element) =>
// element.ticker.toLowerCase() !=
// coin.ticker.toLowerCase(),
// ),
// );
// }
if (mounted) { if (mounted) {
unawaited( unawaited(
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
WalletInitiatedExchangeView.routeName, WalletInitiatedExchangeView.routeName,
arguments: Tuple2( arguments: Tuple2(
walletId, walletId,
coin, currency == null ? Coin.bitcoin : coin,
), ),
), ),
); );
@ -301,6 +287,28 @@ class _WalletViewState extends ConsumerState<WalletView> {
} }
} }
void _onBuyPressed(BuildContext context) async {
final coin = ref.read(managerProvider).coin;
if (coin.isTestNet) {
await showDialog<void>(
context: context,
builder: (_) => const StackOkDialog(
title: "Buy not available for test net coins",
),
);
} else {
if (mounted) {
unawaited(
Navigator.of(context).pushNamed(
BuyInWalletView.routeName,
arguments: coin.hasBuySupport ? coin : Coin.bitcoin,
),
);
}
}
}
Future<void> attemptAnonymize() async { Future<void> attemptAnonymize() async {
bool shouldPop = false; bool shouldPop = false;
unawaited( unawaited(
@ -398,7 +406,9 @@ class _WalletViewState extends ConsumerState<WalletView> {
child: WillPopScope( child: WillPopScope(
onWillPop: _onWillPop, onWillPop: _onWillPop,
child: Background( child: Background(
child: Scaffold( child: Stack(
children: [
Scaffold(
backgroundColor: backgroundColor:
Theme.of(context).extension<StackColors>()!.background, Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar( appBar: AppBar(
@ -422,8 +432,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
), ),
Expanded( Expanded(
child: Text( child: Text(
ref.watch( ref.watch(managerProvider
managerProvider.select((value) => value.walletName)), .select((value) => value.walletName)),
style: STextStyles.navBarTitle(context), style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -482,8 +492,9 @@ class _WalletViewState extends ConsumerState<WalletView> {
: Assets.svg.bell, : Assets.svg.bell,
width: 20, width: 20,
height: 20, height: 20,
color: ref.watch(notificationsProvider.select((value) => color: ref.watch(notificationsProvider.select(
value.hasUnreadNotificationsFor(walletId))) (value) => value
.hasUnreadNotificationsFor(walletId)))
? null ? null
: Theme.of(context) : Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
@ -511,15 +522,15 @@ class _WalletViewState extends ConsumerState<WalletView> {
futures.add(ref futures.add(ref
.read(notificationsProvider) .read(notificationsProvider)
.markAsRead( .markAsRead(
unreadNotificationIds.elementAt(i), false)); unreadNotificationIds.elementAt(i),
false));
} }
// wait for multiple to update if any // wait for multiple to update if any
Future.wait(futures).then((_) { Future.wait(futures).then((_) {
// only notify listeners once // only notify listeners once
ref ref.read(notificationsProvider).markAsRead(
.read(notificationsProvider) unreadNotificationIds.last, true);
.markAsRead(unreadNotificationIds.last, true);
}); });
}); });
}, },
@ -569,7 +580,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
), ),
body: SafeArea( body: SafeArea(
child: Container( child: Container(
color: Theme.of(context).extension<StackColors>()!.background, color:
Theme.of(context).extension<StackColors>()!.background,
child: Column( child: Column(
children: [ children: [
const SizedBox( const SizedBox(
@ -601,7 +613,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
child: TextButton( child: TextButton(
style: Theme.of(context) style: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context), .getSecondaryEnabledButtonStyle(
context),
onPressed: () async { onPressed: () async {
await showDialog<void>( await showDialog<void>(
context: context, context: context,
@ -635,7 +648,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
context), context),
child: Text( child: Text(
"Continue", "Continue",
style: STextStyles.button(context), style:
STextStyles.button(context),
), ),
), ),
), ),
@ -643,7 +657,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
}, },
child: Text( child: Text(
"Anonymize funds", "Anonymize funds",
style: STextStyles.button(context).copyWith( style:
STextStyles.button(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.buttonTextSecondary, .buttonTextSecondary,
@ -664,7 +679,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
children: [ children: [
Text( Text(
"Transactions", "Transactions",
style: STextStyles.itemSubtitle(context).copyWith( style:
STextStyles.itemSubtitle(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textDark3, .textDark3,
@ -686,9 +702,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
height: 12, height: 12,
), ),
Expanded( Expanded(
child: Stack( child: Padding(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.vertical( borderRadius: BorderRadius.vertical(
@ -741,33 +755,19 @@ class _WalletViewState extends ConsumerState<WalletView> {
), ),
), ),
), ),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(
bottom: 14,
left: 16,
right: 16,
), ),
child: WalletNavigationBar( ],
walletId: widget.walletId, ),
coin: ref.watch(managerProvider ),
.select((value) => value.coin)), ),
enableExchange: ),
Constants.enableExchange && WalletNavigationBar(
ref.watch(managerProvider.select( items: [
(value) => value.coin)) != WalletNavigationBarItemData(
Coin.epicCash, label: "Receive",
height: WalletView.navBarHeight, icon: const ReceiveNavIcon(),
onExchangePressed: () => onTap: () {
_onExchangePressed(context), final coin = ref.read(managerProvider).coin;
onReceivePressed: () async {
final coin =
ref.read(managerProvider).coin;
if (mounted) { if (mounted) {
unawaited( unawaited(
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
@ -776,32 +776,28 @@ class _WalletViewState extends ConsumerState<WalletView> {
walletId, walletId,
coin, coin,
), ),
)); ),
);
} }
}, },
onSendPressed: () { ),
final walletId = WalletNavigationBarItemData(
ref.read(managerProvider).walletId; label: "Send",
final coin = icon: const SendNavIcon(),
ref.read(managerProvider).coin; onTap: () {
final walletId = ref.read(managerProvider).walletId;
final coin = ref.read(managerProvider).coin;
switch (ref switch (ref
.read( .read(walletBalanceToggleStateProvider.state)
walletBalanceToggleStateProvider
.state)
.state) { .state) {
case WalletBalanceToggleState.full: case WalletBalanceToggleState.full:
ref ref
.read( .read(publicPrivateBalanceStateProvider.state)
publicPrivateBalanceStateProvider
.state)
.state = "Public"; .state = "Public";
break; break;
case WalletBalanceToggleState case WalletBalanceToggleState.available:
.available:
ref ref
.read( .read(publicPrivateBalanceStateProvider.state)
publicPrivateBalanceStateProvider
.state)
.state = "Private"; .state = "Private";
break; break;
} }
@ -813,26 +809,105 @@ class _WalletViewState extends ConsumerState<WalletView> {
), ),
); );
}, },
onBuyPressed: () { ),
unawaited( if (Constants.enableExchange)
WalletNavigationBarItemData(
label: "Swap",
icon: const ExchangeNavIcon(),
onTap: () => _onExchangePressed(context),
),
if (Constants.enableExchange)
WalletNavigationBarItemData(
label: "Buy",
icon: const BuyNavIcon(),
onTap: () => _onBuyPressed(context),
),
],
moreItems: [
if (ref.watch(
walletsChangeNotifierProvider.select(
(value) => value
.getManager(widget.walletId)
.hasCoinControlSupport,
),
) &&
ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.enableCoinControl,
),
))
WalletNavigationBarItemData(
label: "Coin control",
icon: const CoinControlNavIcon(),
onTap: () {
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
BuyInWalletView.routeName, CoinControlView.routeName,
arguments: coin, arguments: Tuple2(
)); widget.walletId,
CoinControlViewType.manage,
),
);
}, },
), ),
if (ref.watch(walletsChangeNotifierProvider.select((value) =>
value.getManager(widget.walletId).hasPaynymSupport)))
WalletNavigationBarItemData(
label: "PayNym",
icon: const PaynymNavIcon(),
onTap: () async {
unawaited(
showDialog(
context: context,
builder: (context) => const LoadingIndicator(
width: 100,
),
),
);
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId);
final paynymInterface =
manager.wallet as PaynymWalletInterface;
final code = await paynymInterface.getPaymentCode(
DerivePathTypeExt.primaryFor(manager.coin));
final account = await ref
.read(paynymAPIProvider)
.nym(code.toString());
Logging.instance.log(
"my nym account: $account",
level: LogLevel.Info,
);
if (mounted) {
Navigator.of(context).pop();
// check if account exists and for matching code to see if claimed
if (account.value != null &&
account.value!.codes.first.claimed) {
ref.read(myPaynymAccountStateProvider.state).state =
account.value!;
await Navigator.of(context).pushNamed(
PaynymHomeView.routeName,
arguments: widget.walletId,
);
} else {
await Navigator.of(context).pushNamed(
PaynymClaimView.routeName,
arguments: widget.walletId,
);
}
}
},
), ),
], ],
), ),
], ],
)
],
),
),
],
),
),
),
), ),
), ),
), ),

View file

@ -38,20 +38,9 @@ class EmptyWallets extends ConsumerWidget {
const Spacer( const Spacer(
flex: 2, flex: 2,
), ),
(isSorbet || isForest || isOcean) SvgPicture.asset(
? SvgPicture.asset(
Assets.svg.stack(context), Assets.svg.stack(context),
width: isDesktop width: isDesktop ? 324 : MediaQuery.of(context).size.width / 3,
? 324
: MediaQuery.of(context).size.width / 3,
)
: Image(
image: AssetImage(
Assets.png.stack(context),
),
width: isDesktop
? 324
: MediaQuery.of(context).size.width / 3,
), ),
SizedBox( SizedBox(
height: isDesktop ? 30 : 16, height: isDesktop ? 30 : 16,

View file

@ -95,7 +95,7 @@ class _DesktopExchangeViewState extends ConsumerState<DesktopExchangeView> {
left: 24, left: 24,
), ),
child: Text( child: Text(
"Exchange", "Swap",
style: STextStyles.desktopH3(context), style: STextStyles.desktopH3(context),
), ),
), ),

View file

@ -37,7 +37,7 @@ class DesktopStep1 extends ConsumerWidget {
child: Column( child: Column(
children: [ children: [
DesktopStepItem( DesktopStepItem(
label: "Exchange", label: "Swap",
value: ref.watch(exchangeFormStateProvider value: ref.watch(exchangeFormStateProvider
.select((value) => value.exchange.name)), .select((value) => value.exchange.name)),
), ),

View file

@ -35,7 +35,7 @@ class _DesktopStep3State extends ConsumerState<DesktopStep3> {
child: Column( child: Column(
children: [ children: [
DesktopStepItem( DesktopStepItem(
label: "Exchange", label: "Swap",
value: ref.watch(exchangeFormStateProvider value: ref.watch(exchangeFormStateProvider
.select((value) => value.exchange.name)), .select((value) => value.exchange.name)),
), ),

View file

@ -153,7 +153,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
DesktopMenuItem( DesktopMenuItem(
duration: duration, duration: duration,
icon: const DesktopExchangeIcon(), icon: const DesktopExchangeIcon(),
label: "Exchange", label: "Swap",
value: DesktopMenuItemId.exchange, value: DesktopMenuItemId.exchange,
onChanged: updateSelectedMenuItem, onChanged: updateSelectedMenuItem,
controller: controllers[1], controller: controllers[1],

View file

@ -504,7 +504,7 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
// onPressed: () { // onPressed: () {
// _onExchangePressed(context); // _onExchangePressed(context);
// }, // },
// label: "Exchange", // label: "Swap",
// icon: Container( // icon: Container(
// width: 24, // width: 24,
// height: 24, // height: 24,

View file

@ -21,8 +21,9 @@ abstract class Constants {
} }
static bool enableExchange = Util.isDesktop || !Platform.isIOS; static bool enableExchange = Util.isDesktop || !Platform.isIOS;
static bool enableBuy = // just use enable exchange flag
true; // true for development, TODO change to "Util.isDesktop || !Platform.isIOS;" as above or even just = enableExchange // static bool enableBuy = enableExchange;
// // true; // true for development,
//TODO: correct for monero? //TODO: correct for monero?
static const int _satsPerCoinMonero = 1000000000000; static const int _satsPerCoinMonero = 1000000000000;

View file

@ -195,6 +195,29 @@ extension CoinExt on Coin {
} }
} }
bool get isTestNet {
switch (this) {
case Coin.bitcoin:
case Coin.litecoin:
case Coin.bitcoincash:
case Coin.dogecoin:
case Coin.firo:
case Coin.namecoin:
case Coin.particl:
case Coin.epicCash:
case Coin.monero:
case Coin.wownero:
return false;
case Coin.dogecoinTestNet:
case Coin.bitcoinTestNet:
case Coin.litecoinTestNet:
case Coin.bitcoincashTestnet:
case Coin.firoTestNet:
return true;
}
}
int get requiredConfirmations { int get requiredConfirmations {
switch (this) { switch (this) {
case Coin.bitcoin: case Coin.bitcoin:

View file

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
class BuyNavIcon extends StatelessWidget {
const BuyNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.buy(context),
width: 24,
height: 24,
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class CoinControlNavIcon extends StatelessWidget {
const CoinControlNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.coinControl.gamePad,
height: 20,
width: 20,
color: Theme.of(context).extension<StackColors>()!.bottomNavIconIcon,
);
}
}

View file

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
class ExchangeNavIcon extends StatelessWidget {
const ExchangeNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.exchange(context),
width: 24,
height: 24,
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class PaynymNavIcon extends StatelessWidget {
const PaynymNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.robotHead,
height: 20,
width: 20,
color: Theme.of(context).extension<StackColors>()!.bottomNavIconIcon,
);
}
}

View file

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class ReceiveNavIcon extends StatelessWidget {
const ReceiveNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.4),
borderRadius: BorderRadius.circular(
24,
),
),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: SvgPicture.asset(
Assets.svg.arrowDownLeft,
width: 12,
height: 12,
color: Theme.of(context).extension<StackColors>()!.accentColorDark,
),
),
);
}
}

View file

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class SendNavIcon extends StatelessWidget {
const SendNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.4),
borderRadius: BorderRadius.circular(
24,
),
),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: SvgPicture.asset(
Assets.svg.arrowUpRight,
width: 12,
height: 12,
color: Theme.of(context).extension<StackColors>()!.accentColorDark,
),
),
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class WhirlpoolNavIcon extends StatelessWidget {
const WhirlpoolNavIcon({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.whirlPool,
height: 20,
width: 20,
color: Theme.of(context).extension<StackColors>()!.bottomNavIconIcon,
);
}
}

View file

@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/wallet_navigation_bar.dart';
class WalletNavigationBarItemData {
WalletNavigationBarItemData({
required this.icon,
required this.label,
required this.onTap,
this.isMore = false,
this.overrideText,
});
final Widget icon;
final String? label;
final VoidCallback? onTap;
final bool isMore;
final Widget? overrideText;
}
class WalletNavigationBarItem extends ConsumerWidget {
const WalletNavigationBarItem({
Key? key,
required this.data,
required this.disableDuration,
}) : super(key: key);
final WalletNavigationBarItemData data;
final Duration disableDuration;
@override
Widget build(BuildContext context, WidgetRef ref) {
return GestureDetector(
onTap: data.isMore || !ref.watch(walletNavBarMore.state).state
? data.onTap
: null,
child: RoundedContainer(
color: Colors.transparent,
padding: const EdgeInsets.all(0),
radiusMultiplier: 2,
child: AnimatedOpacity(
opacity:
data.isMore || !ref.watch(walletNavBarMore.state).state ? 1 : 0.2,
duration: disableDuration,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 45,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 24,
height: 24,
child: Center(
child: data.icon,
),
),
const Spacer(),
data.overrideText ??
Text(
data.label ?? "",
style: STextStyles.buttonSmall(context),
),
],
),
),
),
),
),
);
}
}
class WalletNavigationBarMoreItem extends ConsumerWidget {
const WalletNavigationBarMoreItem({
Key? key,
required this.data,
}) : super(key: key);
final WalletNavigationBarItemData data;
@override
Widget build(BuildContext context, WidgetRef ref) {
return GestureDetector(
onTap: () {
data.onTap?.call();
ref.read(walletNavBarMore.state).state = false;
},
child: Material(
color: Colors.transparent,
child: RoundedContainer(
color: Theme.of(context).extension<StackColors>()!.bottomNavBack,
radiusMultiplier: 100,
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 30,
),
child: Row(
children: [
Expanded(
child: Text(
data.label ?? "",
textAlign: TextAlign.center,
style: STextStyles.buttonSmall(context),
),
),
const SizedBox(
width: 10,
),
data.icon,
],
),
),
),
);
}
}

View file

@ -0,0 +1,231 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart';
final walletNavBarMore = StateProvider.autoDispose((ref) => false);
class WalletNavigationBar extends ConsumerStatefulWidget {
const WalletNavigationBar({
Key? key,
required this.items,
required this.moreItems,
}) : super(key: key);
final List<WalletNavigationBarItemData> items;
final List<WalletNavigationBarItemData> moreItems;
@override
ConsumerState<WalletNavigationBar> createState() =>
_WalletNavigationBarState();
}
class _WalletNavigationBarState extends ConsumerState<WalletNavigationBar> {
static const double horizontalPadding = 16;
final _moreDuration = const Duration(milliseconds: 200);
void _onMorePressed() {
ref.read(walletNavBarMore.state).state =
!ref.read(walletNavBarMore.state).state;
}
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width - 40;
final hasMore = widget.moreItems.isNotEmpty;
final buttonCount = widget.items.length + (hasMore ? 1 : 0);
return Stack(
alignment: Alignment.bottomCenter,
children: [
IgnorePointer(
ignoring: !ref.read(walletNavBarMore.state).state,
child: GestureDetector(
onTap: () {
if (ref.read(walletNavBarMore.state).state) {
ref.read(walletNavBarMore.state).state = false;
}
},
child: AnimatedOpacity(
opacity: ref.watch(walletNavBarMore.state).state ? 1 : 0,
duration: _moreDuration,
child: Container(
color: Colors.black.withOpacity(0.7),
),
),
),
),
Padding(
padding: const EdgeInsets.only(
left: horizontalPadding,
right: horizontalPadding,
bottom: horizontalPadding,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
AnimatedScale(
scale: ref.watch(walletNavBarMore.state).state ? 1 : 0,
duration: _moreDuration,
alignment: const Alignment(
0.5,
1.0,
),
child: AnimatedOpacity(
opacity: ref.watch(walletNavBarMore.state).state ? 1 : 0,
duration: _moreDuration,
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
...widget.moreItems.map(
(e) {
return Column(
children: [
WalletNavigationBarMoreItem(data: e),
const SizedBox(
height: 8,
),
],
);
},
),
],
),
),
),
),
Material(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
),
),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavBack,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow
],
borderRadius: BorderRadius.circular(
1000,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 6,
horizontal: 20,
),
// child: IntrinsicWidth(
child: ConditionalParent(
condition: buttonCount > 4,
builder: (child) => SizedBox(
width: width * 0.9,
child: child,
),
child: ConditionalParent(
condition: buttonCount <= 4,
builder: (child) => SizedBox(
width: width * 0.2 * buttonCount,
child: child,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
...widget.items.map(
(e) => Expanded(
child: WalletNavigationBarItem(
data: e,
disableDuration: _moreDuration,
),
),
),
if (hasMore)
Expanded(
child: WalletNavigationBarItem(
data: WalletNavigationBarItemData(
icon: AnimatedCrossFade(
firstChild: SvgPicture.asset(
Assets.svg.bars,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavIconIcon,
),
secondChild: SvgPicture.asset(
Assets.svg.bars,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
crossFadeState: ref
.watch(walletNavBarMore.state)
.state
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: _moreDuration,
),
overrideText: AnimatedCrossFade(
firstChild: Text(
"More",
style: STextStyles.buttonSmall(
context),
),
secondChild: Text(
"More",
style:
STextStyles.buttonSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.infoItemIcons,
),
),
crossFadeState: ref
.watch(walletNavBarMore.state)
.state
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: _moreDuration,
),
label: null,
isMore: true,
onTap: _onMorePressed,
),
disableDuration: _moreDuration,
),
),
],
),
),
),
),
),
),
],
),
],
),
),
],
);
}
}