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">
<plist version="1.0">
<dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
@ -29,7 +31,7 @@
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<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>
<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>

View file

@ -28,8 +28,6 @@ class AddWalletNextButton extends ConsumerWidget {
final selectedCoin =
ref.read(addWalletSelectedCoinStateProvider.state).state;
//todo: check if print needed
// debugPrint("Next pressed with ${selectedCoin!.name} selected!");
Navigator.of(context).pushNamed(
CreateOrRestoreWalletView.routeName,
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,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(31),
child: CoinImage(
CoinImage(
coin: coin,
isDesktop: isDesktop,
),
),
const Spacer(
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) {
return SvgPicture.asset(
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
Widget build(BuildContext 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
final isDesktop = Util.isDesktop;
@ -186,21 +186,33 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
height: 12,
),
Flexible(
child: SingleChildScrollView(
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
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: Table(
columnWidths: const {
0: IntrinsicColumnWidth(),
1: FlexColumnWidth(),
},
child: RoundedWhiteContainer(
child: Row(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
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: [
Container(
padding: const EdgeInsets.all(7.5),
@ -208,42 +220,52 @@ class _FiatSelectionViewState extends State<FiatSelectionView> {
color: Theme.of(context)
.extension<StackColors>()!
.currencyListItemBG,
borderRadius: BorderRadius.circular(4),
borderRadius:
BorderRadius.circular(4),
),
child: Text(
format.simpleCurrencySymbol(
_fiats[index].ticker.toUpperCase()),
style: STextStyles.subtitle(context).apply(
e.ticker.toUpperCase()),
style: STextStyles.subtitle(context)
.apply(
fontSizeFactor: (1 /
format
.simpleCurrencySymbol(_fiats[index]
.ticker
.toUpperCase())
.simpleCurrencySymbol(
e.ticker.toUpperCase())
.length * // Couldn't get pow() working here
format
.simpleCurrencySymbol(_fiats[index]
.ticker
.toUpperCase())
.length)),
.simpleCurrencySymbol(
e.ticker.toUpperCase())
.length),
),
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(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
_fiats[index].name,
style: STextStyles.largeMedium14(context),
e.name,
style:
STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
_fiats[index].ticker.toUpperCase(),
e.ticker.toUpperCase(),
style: STextStyles.smallMed12(context)
.copyWith(
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_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:stackwallet/db/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
@ -36,7 +35,8 @@ class UtxoCard extends ConsumerStatefulWidget {
}
class _UtxoCardState extends ConsumerState<UtxoCard> {
late final UTXO utxo;
late Stream<UTXO?> stream;
late UTXO utxo;
late bool _selected;
@ -44,6 +44,8 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
void initState() {
_selected = widget.initialSelectedState;
utxo = widget.utxo;
stream = MainDB.instance.watchUTXO(id: utxo.id);
super.initState();
}
@ -57,19 +59,6 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
final currentChainHeight = ref.watch(walletsChangeNotifierProvider
.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(
condition: widget.onPressed != null,
builder: (child) => MaterialButton(
@ -92,7 +81,13 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
color: widget.onPressed == null
? Theme.of(context).extension<StackColors>()!.popupBG
: Colors.transparent,
child: Row(
child: StreamBuilder<UTXO?>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
utxo = snapshot.data!;
}
return Row(
children: [
ConditionalParent(
condition: widget.canSelect,
@ -112,7 +107,8 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
)
? UTXOStatusIconStatus.confirmed
: UTXOStatusIconStatus.unconfirmed,
background: Theme.of(context).extension<StackColors>()!.popupBG,
background:
Theme.of(context).extension<StackColors>()!.popupBG,
selected: _selected,
width: 32,
height: 32,
@ -140,7 +136,9 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
children: [
Flexible(
child: Text(
label ?? utxo.address ?? utxo.txid,
utxo.name.isNotEmpty
? utxo.name
: utxo.address ?? utxo.txid,
style: STextStyles.w500_12(context).copyWith(
color: Theme.of(context)
.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(
height: _spacing,
),
if (label != null && label!.value.isNotEmpty)
RoundedWhiteContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
@ -190,15 +188,23 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Address label",
"Label",
style: STextStyles.w500_14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
SimpleCopyButton(
data: label!.value,
SimpleEditButton(
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,
),
Text(
label!.value,
utxo!.name,
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(
height: _spacing,
),
@ -309,48 +351,6 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
const SizedBox(
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)
Column(
mainAxisSize: MainAxisSize.min,

View file

@ -618,6 +618,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
if (walletInitiated) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
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 {
_sendController.text =
@ -848,7 +858,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
enabled: ref.watch(
exchangeFormStateProvider.select((value) => value.canExchange)),
onPressed: onExchangePressed,
label: "Exchange",
label: "Swap",
)
],
);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -111,7 +111,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
// }
},
child: Text(
"Exchange",
"Swap",
style: STextStyles.button(context).copyWith(
fontSize: 14,
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_riverpod/flutter_riverpod.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/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/home_view/home_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/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_settings_view.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/transaction_views/all_transactions_view.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/ui/transaction_filter_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/wallet_balance_toggle_state_provider.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/wallet_sync_status_changed_event.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/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.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/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.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/blue_text_button.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/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';
/// [eventBus] should only be set during testing
@ -239,19 +258,14 @@ class _WalletViewState extends ConsumerState<WalletView> {
}
void _onExchangePressed(BuildContext context) async {
// too expensive
// unawaited(ExchangeDataLoadingService.instance.loadAll(ref));
final coin = ref.read(managerProvider).coin;
if (coin == Coin.epicCash) {
await showDialog<void>(
context: context,
builder: (_) => const StackOkDialog(
title: "Exchange not available for Epic Cash",
),
);
} else if (coin.name.endsWith("TestNet")) {
final currency = ExchangeDataLoadingService.instance.isar.currencies
.where()
.tickerEqualToAnyExchangeNameName(coin.ticker)
.findFirstSync();
if (coin.isTestNet) {
await showDialog<void>(
context: context,
builder: (_) => const StackOkDialog(
@ -259,41 +273,13 @@ class _WalletViewState extends ConsumerState<WalletView> {
),
);
} 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) {
unawaited(
Navigator.of(context).pushNamed(
WalletInitiatedExchangeView.routeName,
arguments: Tuple2(
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 {
bool shouldPop = false;
unawaited(
@ -398,7 +406,9 @@ class _WalletViewState extends ConsumerState<WalletView> {
child: WillPopScope(
onWillPop: _onWillPop,
child: Background(
child: Scaffold(
child: Stack(
children: [
Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
@ -422,8 +432,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
),
Expanded(
child: Text(
ref.watch(
managerProvider.select((value) => value.walletName)),
ref.watch(managerProvider
.select((value) => value.walletName)),
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
@ -482,8 +492,9 @@ class _WalletViewState extends ConsumerState<WalletView> {
: Assets.svg.bell,
width: 20,
height: 20,
color: ref.watch(notificationsProvider.select((value) =>
value.hasUnreadNotificationsFor(walletId)))
color: ref.watch(notificationsProvider.select(
(value) => value
.hasUnreadNotificationsFor(walletId)))
? null
: Theme.of(context)
.extension<StackColors>()!
@ -511,15 +522,15 @@ class _WalletViewState extends ConsumerState<WalletView> {
futures.add(ref
.read(notificationsProvider)
.markAsRead(
unreadNotificationIds.elementAt(i), false));
unreadNotificationIds.elementAt(i),
false));
}
// wait for multiple to update if any
Future.wait(futures).then((_) {
// only notify listeners once
ref
.read(notificationsProvider)
.markAsRead(unreadNotificationIds.last, true);
ref.read(notificationsProvider).markAsRead(
unreadNotificationIds.last, true);
});
});
},
@ -569,7 +580,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
),
body: SafeArea(
child: Container(
color: Theme.of(context).extension<StackColors>()!.background,
color:
Theme.of(context).extension<StackColors>()!.background,
child: Column(
children: [
const SizedBox(
@ -601,7 +613,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context),
.getSecondaryEnabledButtonStyle(
context),
onPressed: () async {
await showDialog<void>(
context: context,
@ -635,7 +648,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
context),
child: Text(
"Continue",
style: STextStyles.button(context),
style:
STextStyles.button(context),
),
),
),
@ -643,7 +657,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
},
child: Text(
"Anonymize funds",
style: STextStyles.button(context).copyWith(
style:
STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
@ -664,7 +679,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
children: [
Text(
"Transactions",
style: STextStyles.itemSubtitle(context).copyWith(
style:
STextStyles.itemSubtitle(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
@ -686,9 +702,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
height: 12,
),
Expanded(
child: Stack(
children: [
Padding(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ClipRRect(
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 &&
ref.watch(managerProvider.select(
(value) => value.coin)) !=
Coin.epicCash,
height: WalletView.navBarHeight,
onExchangePressed: () =>
_onExchangePressed(context),
onReceivePressed: () async {
final coin =
ref.read(managerProvider).coin;
],
),
),
),
),
WalletNavigationBar(
items: [
WalletNavigationBarItemData(
label: "Receive",
icon: const ReceiveNavIcon(),
onTap: () {
final coin = ref.read(managerProvider).coin;
if (mounted) {
unawaited(
Navigator.of(context).pushNamed(
@ -776,32 +776,28 @@ class _WalletViewState extends ConsumerState<WalletView> {
walletId,
coin,
),
));
),
);
}
},
onSendPressed: () {
final walletId =
ref.read(managerProvider).walletId;
final coin =
ref.read(managerProvider).coin;
),
WalletNavigationBarItemData(
label: "Send",
icon: const SendNavIcon(),
onTap: () {
final walletId = ref.read(managerProvider).walletId;
final coin = ref.read(managerProvider).coin;
switch (ref
.read(
walletBalanceToggleStateProvider
.state)
.read(walletBalanceToggleStateProvider.state)
.state) {
case WalletBalanceToggleState.full:
ref
.read(
publicPrivateBalanceStateProvider
.state)
.read(publicPrivateBalanceStateProvider.state)
.state = "Public";
break;
case WalletBalanceToggleState
.available:
case WalletBalanceToggleState.available:
ref
.read(
publicPrivateBalanceStateProvider
.state)
.read(publicPrivateBalanceStateProvider.state)
.state = "Private";
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(
BuyInWalletView.routeName,
arguments: coin,
));
CoinControlView.routeName,
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(
flex: 2,
),
(isSorbet || isForest || isOcean)
? SvgPicture.asset(
SvgPicture.asset(
Assets.svg.stack(context),
width: isDesktop
? 324
: MediaQuery.of(context).size.width / 3,
)
: Image(
image: AssetImage(
Assets.png.stack(context),
),
width: isDesktop
? 324
: MediaQuery.of(context).size.width / 3,
width: isDesktop ? 324 : MediaQuery.of(context).size.width / 3,
),
SizedBox(
height: isDesktop ? 30 : 16,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,8 +21,9 @@ abstract class Constants {
}
static bool enableExchange = Util.isDesktop || !Platform.isIOS;
static bool enableBuy =
true; // true for development, TODO change to "Util.isDesktop || !Platform.isIOS;" as above or even just = enableExchange
// just use enable exchange flag
// static bool enableBuy = enableExchange;
// // true; // true for development,
//TODO: correct for monero?
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 {
switch (this) {
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,
),
),
],
),
),
),
),
),
),
],
),
],
),
),
],
);
}
}