Merge branch 'staging' into ui-fixes

This commit is contained in:
julian-CStack 2023-02-10 14:22:00 -06:00 committed by GitHub
commit 54dba90825
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 733 additions and 533 deletions

View file

@ -295,6 +295,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) { await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) {
if (Constants.enableExchange) { if (Constants.enableExchange) {
await ExchangeDataLoadingService.instance.init(); await ExchangeDataLoadingService.instance.init();
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
unawaited(ExchangeDataLoadingService.instance.loadAll()); unawaited(ExchangeDataLoadingService.instance.loadAll());
} }
// if (Constants.enableBuy) { // if (Constants.enableBuy) {

View file

@ -163,7 +163,7 @@ class ExchangeFormState extends ChangeNotifier {
} }
} }
void setCurrencies(AggregateCurrency from, AggregateCurrency to) { void setCurrencies(AggregateCurrency? from, AggregateCurrency? to) {
_sendCurrency = from; _sendCurrency = from;
_receiveCurrency = to; _receiveCurrency = to;
} }

View file

@ -468,9 +468,10 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
header: Container( header: Container(
color: Colors.transparent, color: Colors.transparent,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.only(
vertical: 8.0, top: 8.0,
horizontal: 16, bottom: 8.0,
right: 10,
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -504,67 +505,90 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
), ),
body: Container( body: Container(
color: Colors.transparent, color: Colors.transparent,
child: ClipRRect( child: Column(
borderRadius: BorderRadius.circular( children: [
Constants.size.circularBorderRadius, ClipRRect(
), borderRadius: BorderRadius.circular(
child: TextField( Constants.size.circularBorderRadius,
key: const Key("mnemonicPassphraseFieldKey1"), ),
focusNode: passwordFocusNode, child: TextField(
controller: passwordController, key: const Key("mnemonicPassphraseFieldKey1"),
style: isDesktop focusNode: passwordFocusNode,
? STextStyles.desktopTextMedium(context).copyWith( controller: passwordController,
height: 2, style: isDesktop
) ? STextStyles.desktopTextMedium(context)
: STextStyles.field(context), .copyWith(
obscureText: hidePassword, height: 2,
enableSuggestions: false, )
autocorrect: false, : STextStyles.field(context),
decoration: standardInputDecoration( obscureText: hidePassword,
"Recovery phrase password", enableSuggestions: false,
passwordFocusNode, autocorrect: false,
context, decoration: standardInputDecoration(
).copyWith( "Recovery phrase password",
suffixIcon: UnconstrainedBox( passwordFocusNode,
child: ConditionalParent( context,
condition: isDesktop, ).copyWith(
builder: (child) => SizedBox( suffixIcon: UnconstrainedBox(
height: 70, child: ConditionalParent(
child: child, condition: isDesktop,
), builder: (child) => SizedBox(
child: Row( height: 70,
children: [ child: child,
SizedBox(
width: isDesktop ? 24 : 16,
), ),
GestureDetector( child: Row(
key: const Key( children: [
"mnemonicPassphraseFieldShowPasswordButtonKey"), SizedBox(
onTap: () async { width: isDesktop ? 24 : 16,
setState(() { ),
hidePassword = !hidePassword; GestureDetector(
}); key: const Key(
}, "mnemonicPassphraseFieldShowPasswordButtonKey"),
child: SvgPicture.asset( onTap: () async {
hidePassword setState(() {
? Assets.svg.eye hidePassword = !hidePassword;
: Assets.svg.eyeSlash, });
color: Theme.of(context) },
.extension<StackColors>()! child: SvgPicture.asset(
.textDark3, hidePassword
width: isDesktop ? 24 : 16, ? Assets.svg.eye
height: isDesktop ? 24 : 16, : Assets.svg.eyeSlash,
), color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
width: isDesktop ? 24 : 16,
height: isDesktop ? 24 : 16,
),
),
const SizedBox(
width: 12,
),
],
), ),
const SizedBox( ),
width: 12,
),
],
), ),
), ),
), ),
), ),
), const SizedBox(
height: 8,
),
RoundedWhiteContainer(
child: Center(
child: Text(
"If the recovery phrase you are about to restore was created with an optional passphrase you can enter it here.",
style: isDesktop
? STextStyles.desktopTextExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.itemSubtitle(context),
),
),
),
],
), ),
), ),
), ),

View file

@ -108,6 +108,8 @@ class _ExchangeCurrencySelectionViewState
return ExchangeDataLoadingService.instance.isar.currencies return ExchangeDataLoadingService.instance.isar.currencies
.where() .where()
.filter() .filter()
.isFiatEqualTo(false)
.and()
.tickerEqualTo(ticker, caseSensitive: false) .tickerEqualTo(ticker, caseSensitive: false)
.group((q) => widget.isFixedRate .group((q) => widget.isFixedRate
? q ? q
@ -150,6 +152,8 @@ class _ExchangeCurrencySelectionViewState
return ExchangeDataLoadingService.instance.isar.currencies return ExchangeDataLoadingService.instance.isar.currencies
.where() .where()
.filter() .filter()
.isFiatEqualTo(false)
.and()
.group((q) => widget.isFixedRate .group((q) => widget.isFixedRate
? q ? q
.rateTypeEqualTo(SupportedRateType.both) .rateTypeEqualTo(SupportedRateType.both)

View file

@ -36,7 +36,10 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
ExchangeDataLoadingService.cacheVersion) { ExchangeDataLoadingService.cacheVersion) {
_initialCachePopulationUnderway = true; _initialCachePopulationUnderway = true;
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
}); });
@ -51,7 +54,10 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
ExchangeDataLoadingService.cacheVersion) { ExchangeDataLoadingService.cacheVersion) {
_initialCachePopulationUnderway = true; _initialCachePopulationUnderway = true;
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
}); });

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/exchange_view/exchange_form.dart'; import 'package:stackwallet/pages/exchange_view/exchange_form.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
import 'package:stackwallet/providers/exchange/exchange_form_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart'; import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -48,7 +49,10 @@ class _WalletInitiatedExchangeViewState
ExchangeDataLoadingService.cacheVersion) { ExchangeDataLoadingService.cacheVersion) {
_initialCachePopulationUnderway = true; _initialCachePopulationUnderway = true;
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
}); });
@ -63,7 +67,10 @@ class _WalletInitiatedExchangeViewState
ExchangeDataLoadingService.cacheVersion) { ExchangeDataLoadingService.cacheVersion) {
_initialCachePopulationUnderway = true; _initialCachePopulationUnderway = true;
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
}); });

View file

@ -559,6 +559,7 @@ class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
height: isDesktop ? 56 : 48, height: isDesktop ? 56 : 48,
width: isDesktop ? 490 : null, width: isDesktop ? 490 : null,
child: Toggle( child: Toggle(
key: UniqueKey(),
onColor: Theme.of(context).extension<StackColors>()!.popupBG, onColor: Theme.of(context).extension<StackColors>()!.popupBG,
onText: onText:
"Following (${ref.watch(myPaynymAccountStateProvider.state).state?.following.length ?? 0})", "Following (${ref.watch(myPaynymAccountStateProvider.state).state?.following.length ?? 0})",

View file

@ -11,6 +11,7 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart'; import 'package:stackwallet/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart'; import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart'; import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/paynym/desktop_paynym_send_dialog.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart'; import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -102,8 +103,6 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
builder: (context) => ConfirmPaynymConnectDialog( builder: (context) => ConfirmPaynymConnectDialog(
nymName: widget.accountLite.nymName, nymName: widget.accountLite.nymName,
onConfirmPressed: () { onConfirmPressed: () {
//
print("CONFIRM NOTIF TX: $preparedTx");
Navigator.of(context, rootNavigator: true).pop(); Navigator.of(context, rootNavigator: true).pop();
unawaited( unawaited(
showDialog( showDialog(
@ -148,7 +147,13 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
} }
Future<void> _onSend() async { Future<void> _onSend() async {
print("sned"); await showDialog<void>(
context: context,
builder: (context) => DesktopPaynymSendDialog(
walletId: widget.walletId,
accountLite: widget.accountLite,
),
);
} }
@override @override

View file

@ -586,7 +586,9 @@ class _ConfirmTransactionViewState
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Send to", widget.isPaynymTransaction
? "PayNym recipient"
: "Send to",
style: STextStyles.desktopTextExtraExtraSmall( style: STextStyles.desktopTextExtraExtraSmall(
context), context),
), ),
@ -594,7 +596,11 @@ class _ConfirmTransactionViewState
height: 2, height: 2,
), ),
Text( Text(
"${transactionInfo["address"] ?? "ERROR"}", widget.isPaynymTransaction
? (transactionInfo["paynymAccountLite"]
as PaynymAccountLite)
.nymName
: "${transactionInfo["address"] ?? "ERROR"}",
style: STextStyles.desktopTextExtraExtraSmall( style: STextStyles.desktopTextExtraExtraSmall(
context) context)
.copyWith( .copyWith(
@ -606,6 +612,64 @@ class _ConfirmTransactionViewState
], ],
), ),
), ),
if (widget.isPaynymTransaction)
Container(
height: 1,
color: Theme.of(context)
.extension<StackColors>()!
.background,
),
if (widget.isPaynymTransaction)
Padding(
padding: const EdgeInsets.all(12),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Transaction fee",
style: STextStyles.desktopTextExtraExtraSmall(
context),
),
const SizedBox(
height: 2,
),
Builder(
builder: (context) {
final coin = ref
.watch(walletsChangeNotifierProvider
.select((value) =>
value.getManager(walletId)))
.coin;
final fee = Format.satoshisToAmount(
transactionInfo["fee"] as int,
coin: coin,
);
return Text(
"${Format.localizedStringAsFixed(
value: fee,
locale: ref.watch(
localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces:
Constants.decimalPlacesForCoin(coin),
)} ${coin.ticker}",
style:
STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
);
},
),
],
),
),
// Container( // Container(
// height: 1, // height: 1,
// color: Theme.of(context) // color: Theme.of(context)
@ -725,7 +789,7 @@ class _ConfirmTransactionViewState
], ],
), ),
), ),
if (isDesktop) if (isDesktop && !widget.isPaynymTransaction)
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 32, left: 32,
@ -735,22 +799,23 @@ class _ConfirmTransactionViewState
style: STextStyles.desktopTextExtraExtraSmall(context), style: STextStyles.desktopTextExtraExtraSmall(context),
), ),
), ),
if (isDesktop) if (isDesktop && !widget.isPaynymTransaction)
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 10, top: 10,
left: 32, left: 32,
right: 32, right: 32,
),
child: RoundedContainer(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 18,
), ),
child: RoundedContainer( color: Theme.of(context)
padding: const EdgeInsets.symmetric( .extension<StackColors>()!
horizontal: 16, .textFieldDefaultBG,
vertical: 18, child: Builder(
), builder: (context) {
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
child: Builder(builder: (context) {
final coin = ref final coin = ref
.watch(walletsChangeNotifierProvider .watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId))) .select((value) => value.getManager(walletId)))
@ -770,64 +835,10 @@ class _ConfirmTransactionViewState
)} ${coin.ticker}", )} ${coin.ticker}",
style: STextStyles.itemSubtitle(context), style: STextStyles.itemSubtitle(context),
); );
}), },
)
// DropdownButtonHideUnderline(
// child: DropdownButton2(
// offset: const Offset(0, -10),
// isExpanded: true,
//
// dropdownElevation: 0,
// value: _fee,
// items: [
// ..._dropDownItems.map(
// (e) {
// String message = _fee.toString();
//
// return DropdownMenuItem(
// value: e,
// child: Text(message),
// );
// },
// ),
// ],
// onChanged: (value) {
// if (value is int) {
// setState(() {
// _fee = value;
// });
// }
// },
// icon: SvgPicture.asset(
// Assets.svg.chevronDown,
// width: 12,
// height: 6,
// color:
// Theme.of(context).extension<StackColors>()!.textDark3,
// ),
// buttonPadding: const EdgeInsets.symmetric(
// horizontal: 16,
// vertical: 8,
// ),
// buttonDecoration: BoxDecoration(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG,
// borderRadius: BorderRadius.circular(
// Constants.size.circularBorderRadius,
// ),
// ),
// dropdownDecoration: BoxDecoration(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG,
// borderRadius: BorderRadius.circular(
// Constants.size.circularBorderRadius,
// ),
// ),
// ),
// ),
), ),
),
),
if (!isDesktop) const Spacer(), if (!isDesktop) const Spacer(),
SizedBox( SizedBox(
height: isDesktop ? 23 : 12, height: isDesktop ? 23 : 12,

View file

@ -297,44 +297,6 @@ class _SendViewState extends ConsumerState<SendView> {
final manager = final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId); ref.read(walletsChangeNotifierProvider).getManager(walletId);
// // TODO: remove the need for this!!
// final bool isOwnAddress =
// await manager.isOwnAddress(_address!);
// if (isOwnAddress && coin != Coin.dogecoinTestNet) {
// await showDialog<dynamic>(
// context: context,
// useSafeArea: false,
// barrierDismissible: true,
// builder: (context) {
// return StackDialog(
// title: "Transaction failed",
// message:
// "Sending to self is currently disabled",
// rightButton: TextButton(
// style: Theme.of(context)
// .extension<StackColors>()!
// .getSecondaryEnabledButtonColor(
// context),
// child: Text(
// "Ok",
// style: STextStyles.button(
// context)
// .copyWith(
// color: Theme.of(context)
// .extension<
// StackColors>()!
// .accentColorDark),
// ),
// onPressed: () {
// Navigator.of(context).pop();
// },
// ),
// );
// },
// );
// return;
// }
final amount = Format.decimalAmountToSatoshis(_amountToSend!, coin); final amount = Format.decimalAmountToSatoshis(_amountToSend!, coin);
int availableBalance; int availableBalance;
if ((coin == Coin.firo || coin == Coin.firoTestNet)) { if ((coin == Coin.firo || coin == Coin.firoTestNet)) {

View file

@ -21,7 +21,7 @@ class AllWallets extends StatelessWidget {
Text( Text(
"All wallets", "All wallets",
style: STextStyles.itemSubtitle(context).copyWith( style: STextStyles.itemSubtitle(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark, color: Theme.of(context).extension<StackColors>()!.textDark3,
), ),
), ),
CustomTextButton( CustomTextButton(

View file

@ -106,12 +106,12 @@ class _FavoriteWalletsState extends ConsumerState<FavoriteWallets> {
ManageFavoritesView.routeName, ManageFavoritesView.routeName,
); );
}, },
) ),
], ],
), ),
), ),
const SizedBox( const SizedBox(
height: 12, height: 20,
), ),
!hasFavorites !hasFavorites
? Padding( ? Padding(

View file

@ -5,6 +5,8 @@ import 'package:stackwallet/pages/wallets_view/sub_widgets/all_wallets.dart';
import 'package:stackwallet/pages/wallets_view/sub_widgets/empty_wallets.dart'; import 'package:stackwallet/pages/wallets_view/sub_widgets/empty_wallets.dart';
import 'package:stackwallet/pages/wallets_view/sub_widgets/favorite_wallets.dart'; import 'package:stackwallet/pages/wallets_view/sub_widgets/favorite_wallets.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/color_theme_provider.dart';
import 'package:stackwallet/utilities/theme/color_theme.dart';
class WalletsView extends ConsumerWidget { class WalletsView extends ConsumerWidget {
const WalletsView({Key? key}) : super(key: key); const WalletsView({Key? key}) : super(key: key);
@ -22,8 +24,11 @@ class WalletsView extends ConsumerWidget {
return SafeArea( return SafeArea(
child: hasWallets child: hasWallets
? Padding( ? Padding(
padding: const EdgeInsets.only( padding: EdgeInsets.only(
top: 20, top: ref.watch(colorThemeProvider).themeType ==
ThemeType.fruitSorbet
? 6
: 20,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,

View file

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/exchange_view/exchange_form.dart'; import 'package:stackwallet/pages/exchange_view/exchange_form.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart';
import 'package:stackwallet/providers/exchange/exchange_form_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart'; import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
@ -32,7 +33,10 @@ class _DesktopExchangeViewState extends ConsumerState<DesktopExchangeView> {
ExchangeDataLoadingService.cacheVersion) { ExchangeDataLoadingService.cacheVersion) {
_initialCachePopulationUnderway = true; _initialCachePopulationUnderway = true;
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
}); });
@ -47,7 +51,10 @@ class _DesktopExchangeViewState extends ConsumerState<DesktopExchangeView> {
ExchangeDataLoadingService.cacheVersion) { ExchangeDataLoadingService.cacheVersion) {
_initialCachePopulationUnderway = true; _initialCachePopulationUnderway = true;
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider),
);
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
}); });

View file

@ -0,0 +1,197 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/price_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopPaynymSendDialog extends ConsumerStatefulWidget {
const DesktopPaynymSendDialog({
Key? key,
required this.walletId,
this.autoFillData,
this.clipboard = const ClipboardWrapper(),
this.barcodeScanner = const BarcodeScannerWrapper(),
this.accountLite,
}) : super(key: key);
final String walletId;
final SendViewAutoFillData? autoFillData;
final ClipboardInterface clipboard;
final BarcodeScannerInterface barcodeScanner;
final PaynymAccountLite? accountLite;
@override
ConsumerState<DesktopPaynymSendDialog> createState() =>
_DesktopPaynymSendDialogState();
}
class _DesktopPaynymSendDialogState
extends ConsumerState<DesktopPaynymSendDialog> {
@override
Widget build(BuildContext context) {
final manager = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(widget.walletId)));
final String locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale));
final coin = manager.coin;
final isFiro = coin == Coin.firo || coin == Coin.firoTestNet;
return DesktopDialog(
maxHeight: double.infinity,
maxWidth: 580,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"Send ${manager.coin.ticker.toUpperCase()}",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: RoundedWhiteContainer(
borderColor:
Theme.of(context).extension<StackColors>()!.background,
// Theme.of(context).extension<StackColors>()!.textSubtitle4,
child: Row(
children: [
SvgPicture.asset(
Assets.svg.iconFor(coin: coin),
width: 36,
height: 36,
),
const SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
manager.walletName,
style: STextStyles.titleBold12(context),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(
height: 2,
),
Text(
isFiro
? "${ref.watch(publicPrivateBalanceStateProvider.state).state} balance"
: "Available balance",
style: STextStyles.baseXS(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
const Spacer(),
Container(
color: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"${Format.localizedStringAsFixed(
value: !isFiro
? manager.balance.getSpendable()
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? (manager.wallet as FiroWallet)
.availablePrivateBalance()
: (manager.wallet as FiroWallet)
.availablePublicBalance(),
locale: locale,
decimalPlaces: 8,
)} ${coin.ticker}",
style: STextStyles.titleBold12(context),
textAlign: TextAlign.right,
),
const SizedBox(
height: 2,
),
Text(
"${Format.localizedStringAsFixed(
value: (!isFiro
? manager.balance.getSpendable()
: ref
.watch(
publicPrivateBalanceStateProvider
.state)
.state ==
"Private"
? (manager.wallet as FiroWallet)
.availablePrivateBalance()
: (manager.wallet as FiroWallet)
.availablePublicBalance()) *
ref.watch(
priceAnd24hChangeNotifierProvider.select(
(value) => value.getPrice(coin).item1)),
locale: locale,
decimalPlaces: 2,
)} ${ref.watch(prefsChangeNotifierProvider.select((value) => value.currency))}",
style: STextStyles.baseXS(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
textAlign: TextAlign.right,
)
],
),
),
],
),
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: DesktopSend(
walletId: manager.walletId,
accountLite: widget.accountLite,
),
),
],
),
);
}
}

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:bip47/bip47.dart';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -7,6 +8,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart'; import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart'; import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
@ -20,6 +22,7 @@ import 'package:stackwallet/providers/ui/preview_tx_button_state_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart'; import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart';
import 'package:stackwallet/utilities/address_utils.dart'; import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/barcode_scanner_interface.dart'; import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
@ -51,12 +54,14 @@ class DesktopSend extends ConsumerStatefulWidget {
this.autoFillData, this.autoFillData,
this.clipboard = const ClipboardWrapper(), this.clipboard = const ClipboardWrapper(),
this.barcodeScanner = const BarcodeScannerWrapper(), this.barcodeScanner = const BarcodeScannerWrapper(),
this.accountLite,
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final SendViewAutoFillData? autoFillData; final SendViewAutoFillData? autoFillData;
final ClipboardInterface clipboard; final ClipboardInterface clipboard;
final BarcodeScannerInterface barcodeScanner; final BarcodeScannerInterface barcodeScanner;
final PaynymAccountLite? accountLite;
@override @override
ConsumerState<DesktopSend> createState() => _DesktopSendState(); ConsumerState<DesktopSend> createState() => _DesktopSendState();
@ -93,78 +98,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
bool _cryptoAmountChangeLock = false; bool _cryptoAmountChangeLock = false;
late VoidCallback onCryptoAmountChanged; late VoidCallback onCryptoAmountChanged;
bool get isPaynymSend => widget.accountLite != null;
Future<void> previewSend() async { Future<void> previewSend() async {
final manager = final manager =
ref.read(walletsChangeNotifierProvider).getManager(walletId); ref.read(walletsChangeNotifierProvider).getManager(walletId);
// // TODO: remove the need for this!!
// final bool isOwnAddress = await manager.isOwnAddress(_address!);
// if (isOwnAddress) {
// await showDialog<dynamic>(
// context: context,
// useSafeArea: false,
// barrierDismissible: true,
// builder: (context) {
// return DesktopDialog(
// maxWidth: 400,
// maxHeight: double.infinity,
// child: Padding(
// padding: const EdgeInsets.only(
// left: 32,
// bottom: 32,
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// "Transaction failed",
// style: STextStyles.desktopH3(context),
// ),
// const DesktopDialogCloseButton(),
// ],
// ),
// const SizedBox(
// height: 12,
// ),
// Text(
// "Sending to self is currently disabled",
// textAlign: TextAlign.left,
// style: STextStyles.desktopTextExtraExtraSmall(context)
// .copyWith(
// fontSize: 18,
// ),
// ),
// const SizedBox(
// height: 40,
// ),
// Row(
// children: [
// Expanded(
// child: SecondaryButton(
// buttonHeight: ButtonHeight.l,
// label: "Ok",
// onPressed: () {
// Navigator.of(context).pop();
// },
// ),
// ),
// const SizedBox(
// width: 32,
// ),
// ],
// ),
// ],
// ),
// ),
// );
// },
// );
// return;
// }
final amount = Format.decimalAmountToSatoshis(_amountToSend!, coin); final amount = Format.decimalAmountToSatoshis(_amountToSend!, coin);
int availableBalance; int availableBalance;
if ((coin == Coin.firo || coin == Coin.firoTestNet)) { if ((coin == Coin.firo || coin == Coin.firoTestNet)) {
@ -304,7 +243,19 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
Map<String, dynamic> txData; Map<String, dynamic> txData;
if ((coin == Coin.firo || coin == Coin.firoTestNet) && if (isPaynymSend) {
final wallet = manager.wallet as PaynymWalletInterface;
final paymentCode = PaymentCode.fromPaymentCode(
widget.accountLite!.code,
wallet.networkType,
);
final feeRate = ref.read(feeRateTypeStateProvider);
txData = await wallet.preparePaymentCodeSend(
paymentCode: paymentCode,
satoshiAmount: amount,
args: {"feeRate": feeRate},
);
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state != ref.read(publicPrivateBalanceStateProvider.state).state !=
"Private") { "Private") {
txData = await (manager.wallet as FiroWallet).prepareSendPublic( txData = await (manager.wallet as FiroWallet).prepareSendPublic(
@ -321,8 +272,13 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
} }
if (!wasCancelled && mounted) { if (!wasCancelled && mounted) {
txData["note"] = _note ?? ""; if (isPaynymSend) {
txData["address"] = _address; txData["paynymAccountLite"] = widget.accountLite!;
txData["note"] = _note ?? "PayNym send";
} else {
txData["address"] = _address;
txData["note"] = _note ?? "";
}
// pop building dialog // pop building dialog
Navigator.of( Navigator.of(
context, context,
@ -338,6 +294,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
child: ConfirmTransactionView( child: ConfirmTransactionView(
transactionInfo: txData, transactionInfo: txData,
walletId: walletId, walletId: walletId,
isPaynymTransaction: isPaynymSend,
routeOnSuccessName: DesktopHomeView.routeName, routeOnSuccessName: DesktopHomeView.routeName,
), ),
), ),
@ -439,9 +396,9 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
_cachedAmountToSend == _amountToSend) { _cachedAmountToSend == _amountToSend) {
return; return;
} }
_cachedAmountToSend = _amountToSend;
Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend", Logging.instance.log("it changed $_amountToSend $_cachedAmountToSend",
level: LogLevel.Info); level: LogLevel.Info);
_cachedAmountToSend = _amountToSend;
final price = final price =
ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1; ref.read(priceAnd24hChangeNotifierProvider).getPrice(coin).item1;
@ -457,6 +414,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
} }
} else { } else {
_amountToSend = null; _amountToSend = null;
_cachedAmountToSend = null;
baseAmountController.text = ""; baseAmountController.text = "";
} }
@ -475,87 +433,19 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
} }
void _updatePreviewButtonState(String? address, Decimal? amount) { void _updatePreviewButtonState(String? address, Decimal? amount) {
final isValidAddress = ref if (isPaynymSend) {
.read(walletsChangeNotifierProvider) ref.read(previewTxButtonStateProvider.state).state =
.getManager(walletId) (amount != null && amount > Decimal.zero);
.validateAddress(address ?? ""); } else {
ref.read(previewTxButtonStateProvider.state).state = final isValidAddress = ref
(isValidAddress && amount != null && amount > Decimal.zero); .read(walletsChangeNotifierProvider)
.getManager(walletId)
.validateAddress(address ?? "");
ref.read(previewTxButtonStateProvider.state).state =
(isValidAddress && amount != null && amount > Decimal.zero);
}
} }
// late Future<String> _calculateFeesFuture;
// Map<int, String> cachedFees = {};
// Map<int, String> cachedFiroPrivateFees = {};
// Map<int, String> cachedFiroPublicFees = {};
// Future<String> calculateFees(int amount) async {
// if (amount <= 0) {
// return "0";
// }
//
// if (coin == Coin.firo || coin == Coin.firoTestNet) {
// if (ref.read(publicPrivateBalanceStateProvider.state).state ==
// "Private") {
// if (cachedFiroPrivateFees[amount] != null) {
// return cachedFiroPrivateFees[amount]!;
// }
// } else {
// if (cachedFiroPublicFees[amount] != null) {
// return cachedFiroPublicFees[amount]!;
// }
// }
// } else if (cachedFees[amount] != null) {
// return cachedFees[amount]!;
// }
//
// final manager =
// ref.read(walletsChangeNotifierProvider).getManager(walletId);
// final feeObject = await manager.fees;
//
// late final int feeRate;
//
// switch (ref.read(feeRateTypeStateProvider.state).state) {
// case FeeRateType.fast:
// feeRate = feeObject.fast;
// break;
// case FeeRateType.average:
// feeRate = feeObject.medium;
// break;
// case FeeRateType.slow:
// feeRate = feeObject.slow;
// break;
// }
//
// int fee;
//
// if (coin == Coin.firo || coin == Coin.firoTestNet) {
// if (ref.read(publicPrivateBalanceStateProvider.state).state ==
// "Private") {
// fee = await manager.estimateFeeFor(amount, feeRate);
//
// cachedFiroPrivateFees[amount] = Format.satoshisToAmount(fee)
// .toStringAsFixed(Constants.decimalPlaces);
//
// return cachedFiroPrivateFees[amount]!;
// } else {
// fee = await (manager.wallet as FiroWallet)
// .estimateFeeForPublic(amount, feeRate);
//
// cachedFiroPublicFees[amount] = Format.satoshisToAmount(fee)
// .toStringAsFixed(Constants.decimalPlaces);
//
// return cachedFiroPublicFees[amount]!;
// }
// } else {
// fee = await manager.estimateFeeFor(amount, feeRate);
// cachedFees[amount] =
// Format.satoshisToAmount(fee).toStringAsFixed(Constants.decimalPlaces);
//
// return cachedFees[amount]!;
// }
// }
Future<String?> _firoBalanceFuture( Future<String?> _firoBalanceFuture(
ChangeNotifierProvider<Manager> provider, ChangeNotifierProvider<Manager> provider,
String locale, String locale,
@ -773,7 +663,10 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
@override @override
void initState() { void initState() {
ref.refresh(feeSheetSessionCacheProvider); WidgetsBinding.instance.addPostFrameCallback((_) {
ref.refresh(feeSheetSessionCacheProvider);
ref.read(previewTxButtonStateProvider.state).state = false;
});
// _calculateFeesFuture = calculateFees(0); // _calculateFeesFuture = calculateFees(0);
_data = widget.autoFillData; _data = widget.autoFillData;
@ -799,6 +692,10 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
_addressToggleFlag = true; _addressToggleFlag = true;
} }
if (isPaynymSend) {
sendToController.text = widget.accountLite!.nymName;
}
_cryptoFocus.addListener(() { _cryptoFocus.addListener(() {
if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) { if (!_cryptoFocus.hasFocus && !_baseFocus.hasFocus) {
if (_amountToSend == null) { if (_amountToSend == null) {
@ -845,21 +742,6 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
final String locale = ref.watch( final String locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale)); localeServiceChangeNotifierProvider.select((value) => value.locale));
// if (coin == Coin.firo || coin == Coin.firoTestNet) {
// ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
// if (_amountToSend == null) {
// setState(() {
// _calculateFeesFuture = calculateFees(0);
// });
// } else {
// setState(() {
// _calculateFeesFuture =
// calculateFees(Format.decimalAmountToSatoshis(_amountToSend!));
// });
// }
// });
// }
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -975,6 +857,36 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
if (isPaynymSend)
Text(
"Send to PayNym address",
style: STextStyles.smallMed12(context),
textAlign: TextAlign.left,
),
if (isPaynymSend)
const SizedBox(
height: 10,
),
if (isPaynymSend)
TextField(
key: const Key("sendViewPaynymAddressFieldKey"),
controller: sendToController,
enabled: false,
readOnly: true,
style: STextStyles.desktopTextFieldLabel(context).copyWith(
fontSize: 16,
),
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 18,
horizontal: 16,
),
),
),
if (isPaynymSend)
const SizedBox(
height: 20,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -1020,6 +932,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
? newValue ? newValue
: oldValue), : oldValue),
], ],
onChanged: (newValue) {},
decoration: InputDecoration( decoration: InputDecoration(
contentPadding: const EdgeInsets.only( contentPadding: const EdgeInsets.only(
top: 22, top: 22,
@ -1108,199 +1021,204 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
Text( if (!isPaynymSend)
"Send to", Text(
style: STextStyles.desktopTextExtraSmall(context).copyWith( "Send to",
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveSearchIconRight,
),
textAlign: TextAlign.left,
),
const SizedBox(
height: 10,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
minLines: 1,
maxLines: 5,
key: const Key("sendViewAddressFieldKey"),
controller: sendToController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(
// RegExp("[a-zA-Z0-9]{34}")),
// ],
toolbarOptions: const ToolbarOptions(
copy: false,
cut: false,
paste: true,
selectAll: false,
),
onChanged: (newValue) {
_address = newValue;
_updatePreviewButtonState(_address, _amountToSend);
setState(() {
_addressToggleFlag = newValue.isNotEmpty;
});
},
focusNode: _addressFocusNode,
style: STextStyles.desktopTextExtraSmall(context).copyWith( style: STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
.textFieldActiveText, .textFieldActiveSearchIconRight,
height: 1.8,
), ),
decoration: standardInputDecoration( textAlign: TextAlign.left,
"Enter ${coin.ticker} address", ),
_addressFocusNode, if (!isPaynymSend)
context, const SizedBox(
desktopMed: true, height: 10,
).copyWith( ),
contentPadding: const EdgeInsets.only( if (!isPaynymSend)
left: 16, ClipRRect(
top: 11, borderRadius: BorderRadius.circular(
bottom: 12, Constants.size.circularBorderRadius,
right: 5, ),
child: TextField(
minLines: 1,
maxLines: 5,
key: const Key("sendViewAddressFieldKey"),
controller: sendToController,
readOnly: false,
autocorrect: false,
enableSuggestions: false,
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(
// RegExp("[a-zA-Z0-9]{34}")),
// ],
toolbarOptions: const ToolbarOptions(
copy: false,
cut: false,
paste: true,
selectAll: false,
), ),
suffixIcon: Padding( onChanged: (newValue) {
padding: sendToController.text.isEmpty _address = newValue;
? const EdgeInsets.only(right: 8) _updatePreviewButtonState(_address, _amountToSend);
: const EdgeInsets.only(right: 0),
child: UnconstrainedBox( setState(() {
child: Row( _addressToggleFlag = newValue.isNotEmpty;
mainAxisAlignment: MainAxisAlignment.spaceAround, });
children: [ },
_addressToggleFlag focusNode: _addressFocusNode,
? TextFieldIconButton( style: STextStyles.desktopTextExtraSmall(context).copyWith(
key: const Key( color: Theme.of(context)
"sendViewClearAddressFieldButtonKey"), .extension<StackColors>()!
onTap: () { .textFieldActiveText,
sendToController.text = ""; height: 1.8,
_address = ""; ),
_updatePreviewButtonState( decoration: standardInputDecoration(
_address, _amountToSend); "Enter ${coin.ticker} address",
setState(() { _addressFocusNode,
_addressToggleFlag = false; context,
}); desktopMed: true,
}, ).copyWith(
child: const XIcon(), contentPadding: const EdgeInsets.only(
) left: 16,
: TextFieldIconButton( top: 11,
key: const Key( bottom: 12,
"sendViewPasteAddressFieldButtonKey"), right: 5,
onTap: pasteAddress, ),
child: sendToController.text.isEmpty suffixIcon: Padding(
? const ClipboardIcon() padding: sendToController.text.isEmpty
: const XIcon(), ? const EdgeInsets.only(right: 8)
), : const EdgeInsets.only(right: 0),
if (sendToController.text.isEmpty) child: UnconstrainedBox(
TextFieldIconButton( child: Row(
key: const Key("sendViewAddressBookButtonKey"), mainAxisAlignment: MainAxisAlignment.spaceAround,
onTap: () async { children: [
final entry = _addressToggleFlag
await showDialog<ContactAddressEntry?>( ? TextFieldIconButton(
context: context, key: const Key(
builder: (context) => DesktopDialog( "sendViewClearAddressFieldButtonKey"),
maxWidth: 696, onTap: () {
maxHeight: 600, sendToController.text = "";
child: Column( _address = "";
mainAxisSize: MainAxisSize.min, _updatePreviewButtonState(
children: [ _address, _amountToSend);
Row( setState(() {
mainAxisAlignment: _addressToggleFlag = false;
MainAxisAlignment.spaceBetween, });
children: [ },
Padding( child: const XIcon(),
padding: const EdgeInsets.only( )
left: 32, : TextFieldIconButton(
), key: const Key(
child: Text( "sendViewPasteAddressFieldButtonKey"),
"Address book", onTap: pasteAddress,
style: child: sendToController.text.isEmpty
STextStyles.desktopH3(context), ? const ClipboardIcon()
), : const XIcon(),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: AddressBookAddressChooser(
coin: coin,
),
),
],
),
), ),
); if (sendToController.text.isEmpty)
TextFieldIconButton(
if (entry != null) { key: const Key("sendViewAddressBookButtonKey"),
sendToController.text = onTap: () async {
entry.other ?? entry.label; final entry =
await showDialog<ContactAddressEntry?>(
_address = entry.address; context: context,
builder: (context) => DesktopDialog(
_updatePreviewButtonState( maxWidth: 696,
_address, maxHeight: 600,
_amountToSend, child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Address book",
style: STextStyles.desktopH3(
context),
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(
child: AddressBookAddressChooser(
coin: coin,
),
),
],
),
),
); );
setState(() { if (entry != null) {
_addressToggleFlag = true; sendToController.text =
}); entry.other ?? entry.label;
}
}, _address = entry.address;
child: const AddressBookIcon(),
), _updatePreviewButtonState(
// if (sendToController.text.isEmpty) _address,
// TextFieldIconButton( _amountToSend,
// key: const Key("sendViewScanQrButtonKey"), );
// onTap: scanQr,
// child: const QrCodeIcon(), setState(() {
// ) _addressToggleFlag = true;
], });
}
},
child: const AddressBookIcon(),
),
// if (sendToController.text.isEmpty)
// TextFieldIconButton(
// key: const Key("sendViewScanQrButtonKey"),
// onTap: scanQr,
// child: const QrCodeIcon(),
// )
],
),
), ),
), ),
), ),
), ),
), ),
), if (!isPaynymSend)
Builder( Builder(
builder: (_) { builder: (_) {
final error = _updateInvalidAddressText( final error = _updateInvalidAddressText(
_address ?? "", _address ?? "",
ref.read(walletsChangeNotifierProvider).getManager(walletId), ref.read(walletsChangeNotifierProvider).getManager(walletId),
); );
if (error == null || error.isEmpty) { if (error == null || error.isEmpty) {
return Container(); return Container();
} else { } else {
return Align( return Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 12.0, left: 12.0,
top: 4.0, top: 4.0,
), ),
child: Text( child: Text(
error, error,
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: STextStyles.label(context).copyWith( style: STextStyles.label(context).copyWith(
color: color: Theme.of(context)
Theme.of(context).extension<StackColors>()!.textError, .extension<StackColors>()!
.textError,
),
), ),
), ),
), );
); }
} },
}, ),
),
// const SizedBox( // const SizedBox(
// height: 20, // height: 20,
// ), // ),
@ -1368,9 +1286,10 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
// ), // ),
// ), // ),
// ), // ),
const SizedBox( if (!isPaynymSend)
height: 20, const SizedBox(
), height: 20,
),
if (coin != Coin.epicCash) if (coin != Coin.epicCash)
Text( Text(
"Transaction fee (estimated)", "Transaction fee (estimated)",

View file

@ -1,12 +1,16 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/hive/db.dart'; import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/exchange/aggregate_currency.dart';
import 'package:stackwallet/models/exchange/exchange_form_state.dart';
import 'package:stackwallet/models/isar/exchange_cache/currency.dart'; import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
import 'package:stackwallet/models/isar/exchange_cache/pair.dart'; import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart'; import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/stack_file_system.dart'; import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:tuple/tuple.dart';
class ExchangeDataLoadingService { class ExchangeDataLoadingService {
ExchangeDataLoadingService._(); ExchangeDataLoadingService._();
@ -50,6 +54,51 @@ class ExchangeDataLoadingService {
); );
} }
Future<void> setCurrenciesIfEmpty(ExchangeFormState state) async {
if (state.sendCurrency == null && state.receiveCurrency == null) {
if (await isar.currencies.count() > 0) {
final sendCurrency = await getAggregateCurrency(
"BTC",
state.exchangeRateType,
);
final receiveCurrency = await getAggregateCurrency(
"XMR",
state.exchangeRateType,
);
state.setCurrencies(sendCurrency, receiveCurrency);
}
}
}
Future<AggregateCurrency?> getAggregateCurrency(
String ticker, ExchangeRateType rateType) async {
final currencies = await ExchangeDataLoadingService.instance.isar.currencies
.filter()
.group((q) => rateType == ExchangeRateType.fixed
? q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.fixed)
: q
.rateTypeEqualTo(SupportedRateType.both)
.or()
.rateTypeEqualTo(SupportedRateType.estimated))
.and()
.tickerEqualTo(
ticker,
caseSensitive: false,
)
.findAll();
final items = currencies
.map((e) => Tuple2(e.exchangeName, e))
.toList(growable: false);
return items.isNotEmpty
? AggregateCurrency(exchangeCurrencyPairs: items)
: null;
}
bool get isLoading => _locked; bool get isLoading => _locked;
bool _locked = false; bool _locked = false;

View file

@ -306,7 +306,7 @@ mixin PaynymWalletInterface {
{required PaymentCode paymentCode, {required PaymentCode paymentCode,
required int satoshiAmount, required int satoshiAmount,
Map<String, dynamic>? args}) async { Map<String, dynamic>? args}) async {
if (!(await hasConnected(paymentCode.notificationAddressP2PKH()))) { if (!(await hasConnected(paymentCode.toString()))) {
throw PaynymSendException( throw PaynymSendException(
"No notification transaction sent to $paymentCode"); "No notification transaction sent to $paymentCode");
} else { } else {