Merge branch 'main' into CW-285-Show-number-of-confirmations-in-transactions-screen-for-Monero-and-Haven-if-10

This commit is contained in:
Serhii
2023-02-10 00:06:57 +02:00
24 changed files with 1100 additions and 1020 deletions
+1
View File
@@ -29,6 +29,7 @@ class PreferencesKey {
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
static const pinTimeOutDuration = 'pin_timeout_duration';
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
static const lastPopupDate = 'last_popup_date';
static String moneroWalletUpdateV1Key(String name)
+1 -1
View File
@@ -330,7 +330,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
getIt.get<ContactListPage>());
case Routes.pickerAddressBook:
final selectedCurrency = settings.arguments as CryptoCurrency;
final selectedCurrency = settings.arguments as CryptoCurrency?;
return MaterialPageRoute<void>(
builder: (_) => getIt.get<ContactListPage>(param1: selectedCurrency));
@@ -1,10 +1,8 @@
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/material.dart';
@@ -15,7 +13,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:mobx/mobx.dart';
import 'package:share_plus/share_plus.dart';
class AddressPage extends BasePage {
AddressPage({
@@ -100,7 +97,12 @@ class AddressPage extends BasePage {
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: () => Share.share(addressListViewModel.address.address),
onPressed: () {
ShareUtil.share(
text: addressListViewModel.address.address,
context: context,
);
},
icon: shareImage,
),
) : null;
@@ -112,7 +112,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
width: 16,
color: Theme.of(context).primaryTextTheme!.overline!.color!);
_setEffects(context);
_setEffects();
return Container(
child: ScrollableWithBottomSection(
@@ -234,7 +234,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
);
}
void _setEffects(BuildContext context) {
void _setEffects() {
if (_effectsInstalled) {
return;
}
@@ -245,12 +245,12 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
builder: (BuildContext popupContext) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertTitle: S.of(popupContext).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
buttonText: S.of(popupContext).ok,
buttonAction: () => Navigator.of(popupContext).pop());
});
});
}
@@ -259,118 +259,24 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
builder: (BuildContext popupContext) {
return ConfirmSendingAlert(
alertTitle: S.of(context).confirm_sending,
amount: S.of(context).send_amount,
alertTitle: S.of(popupContext).confirm_sending,
amount: S.of(popupContext).send_amount,
amountValue: widget.exchangeTradeViewModel.sendViewModel
.pendingTransaction!.amountFormatted,
fee: S.of(context).send_fee,
fee: S.of(popupContext).send_fee,
feeValue: widget.exchangeTradeViewModel.sendViewModel
.pendingTransaction!.feeFormatted,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(popupContext).ok,
leftButtonText: S.of(popupContext).cancel,
actionRightButton: () async {
Navigator.of(context).pop();
Navigator.of(popupContext).pop();
await widget.exchangeTradeViewModel.sendViewModel
.commitTransaction();
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return Observer(builder: (_) {
final state = widget
.exchangeTradeViewModel.sendViewModel.state;
if (state is TransactionCommitted) {
return Stack(
children: <Widget>[
Container(
color: Theme.of(context).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
Center(
child: Padding(
padding: EdgeInsets.only(
top: 220, left: 24, right: 24),
child: Text(
S.of(context).send_success(widget
.exchangeTradeViewModel
.wallet
.currency
.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color,
decoration: TextDecoration.none,
),
),
),
),
Positioned(
left: 24,
right: 24,
bottom: 24,
child: PrimaryButton(
onPressed: () =>
Navigator.of(context).pop(),
text: S.of(context).send_got_it,
color: Theme.of(context)
.accentTextTheme!
.bodyText1!
.color!,
textColor: Colors.white))
],
);
}
return Stack(
children: <Widget>[
Container(
color: Theme.of(context).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.backgroundColor
.withOpacity(0.25)),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 220),
child: Text(
S.of(context).send_sending,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
decoration: TextDecoration.none,
),
),
),
),
),
)
],
);
});
});
transactionStatePopup();
},
actionLeftButton: () => Navigator.of(context).pop(),
actionLeftButton: () => Navigator.of(popupContext).pop(),
feeFiatAmount: widget.exchangeTradeViewModel
.pendingTransactionFeeFiatAmountFormatted,
fiatAmountValue: widget.exchangeTradeViewModel
@@ -385,12 +291,12 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
builder: (BuildContext popupContext) {
return AlertWithOneAction(
alertTitle: S.of(context).sending,
alertContent: S.of(context).transaction_sent,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
alertTitle: S.of(popupContext).sending,
alertContent: S.of(popupContext).transaction_sent,
buttonText: S.of(popupContext).ok,
buttonAction: () => Navigator.of(popupContext).pop());
});
});
}
@@ -398,4 +304,102 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
_effectsInstalled = true;
}
void transactionStatePopup() {
showPopUp<void>(
context: context,
builder: (BuildContext popupContext) {
return Observer(builder: (_) {
final state = widget
.exchangeTradeViewModel.sendViewModel.state;
if (state is TransactionCommitted) {
return Stack(
children: <Widget>[
Container(
color: Theme.of(popupContext).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
Center(
child: Padding(
padding: EdgeInsets.only(
top: 220, left: 24, right: 24),
child: Text(
S.of(popupContext).send_success(widget
.exchangeTradeViewModel
.wallet
.currency
.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(popupContext)
.primaryTextTheme!
.headline6!
.color,
decoration: TextDecoration.none,
),
),
),
),
Positioned(
left: 24,
right: 24,
bottom: 24,
child: PrimaryButton(
onPressed: () =>
Navigator.of(popupContext).pop(),
text: S.of(popupContext).send_got_it,
color: Theme.of(popupContext)
.accentTextTheme!
.bodyText1!
.color!,
textColor: Colors.white))
],
);
}
return Stack(
children: <Widget>[
Container(
color: Theme.of(popupContext).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(popupContext)
.backgroundColor
.withOpacity(0.25)),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 220),
child: Text(
S.of(popupContext).send_sending,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(popupContext).primaryTextTheme!.headline6!.color!,
decoration: TextDecoration.none,
),
),
),
),
),
)
],
);
});
});
}
}
+7 -4
View File
@@ -1,13 +1,11 @@
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:share_plus/share_plus.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/di.dart';
@@ -101,7 +99,12 @@ class ReceivePage extends BasePage {
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: () => Share.share(addressListViewModel.address.address),
onPressed: () {
ShareUtil.share(
text: addressListViewModel.address.address,
context: context,
);
},
icon: shareImage
)
);
+7 -4
View File
@@ -1,12 +1,11 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/share_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:share_plus/share_plus.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@@ -159,8 +158,12 @@ class WalletSeedPage extends BasePage {
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () =>
Share.share(walletSeedViewModel.seed),
onPressed: () {
ShareUtil.share(
text: walletSeedViewModel.seed,
context: context,
);
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white),
+1 -1
View File
@@ -339,7 +339,7 @@ class SendPage extends BasePage {
showErrorValidationAlert(context);
return;
}
await sendViewModel.createTransaction();
},
@@ -2,6 +2,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -35,10 +36,11 @@ class ConnectionSyncPage extends BasePage {
handler: (context) => _presentReconnectAlert(context),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SettingsCellWithArrow(
title: S.current.rescan,
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
),
if (dashboardViewModel.hasRescan)
SettingsCellWithArrow(
title: S.current.rescan,
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
NodeHeaderListRow(
title: S.of(context).add_new_node,
@@ -1,131 +0,0 @@
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu_item.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/palette.dart';
class WalletMenu {
WalletMenu(this.context, this.walletListViewModel);
final WalletListViewModel walletListViewModel;
final BuildContext context;
final List<WalletMenuItem> menuItems = [
WalletMenuItem(
title: S.current.wallet_list_load_wallet,
firstGradientColor: Palette.cornflower,
secondGradientColor: Palette.royalBlue,
image: Image.asset('assets/images/load.png',
height: 24, width: 24, color: Colors.white)),
WalletMenuItem(
title: S.current.show_seed,
firstGradientColor: Palette.moderateOrangeYellow,
secondGradientColor: Palette.moderateOrange,
image: Image.asset('assets/images/eye_action.png',
height: 24, width: 24, color: Colors.white)),
WalletMenuItem(
title: S.current.remove,
firstGradientColor: Palette.lightRed,
secondGradientColor: Palette.persianRed,
image: Image.asset('assets/images/trash.png',
height: 24, width: 24, color: Colors.white)),
WalletMenuItem(
title: S.current.rescan,
firstGradientColor: Palette.shineGreen,
secondGradientColor: Palette.moderateGreen,
image: Image.asset('assets/images/scanner.png',
height: 24, width: 24, color: Colors.white))
];
List<WalletMenuItem> generateItemsForWalletMenu(bool isCurrentWallet) {
final items = <WalletMenuItem>[];
if (!isCurrentWallet) items.add(menuItems[0]);
if (isCurrentWallet) items.add(menuItems[1]);
if (!isCurrentWallet) items.add(menuItems[2]);
if (isCurrentWallet) items.add(menuItems[3]);
return items;
}
Future<void> action(
int index, WalletListItem wallet) async {
switch (index) {
case 0:
await Navigator.of(context).pushNamed(Routes.auth, arguments:
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
try {
auth.changeProcessText(
S.of(context).wallet_list_loading_wallet(wallet.name));
await walletListViewModel.loadWallet(wallet);
auth.close();
Navigator.of(context).pop();
} catch (e) {
auth.changeProcessText(S
.of(context)
.wallet_list_failed_to_load(wallet.name, e.toString()));
}
});
break;
case 1:
await Navigator.of(context).pushNamed(Routes.auth, arguments:
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
auth.close();
await Navigator.of(context).pushNamed(Routes.seed, arguments: false);
});
break;
case 2:
final isComfirmed = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: 'Remove wallet',
alertContent: S.of(context).confirm_delete_wallet,
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).remove,
actionLeftButton: () => Navigator.of(context).pop(false),
actionRightButton: () => Navigator.of(context).pop(true));
});
if (isComfirmed == null || !isComfirmed) {
return;
}
await Navigator.of(context).pushNamed(Routes.auth, arguments:
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
try {
auth.changeProcessText(
S.of(context).wallet_list_removing_wallet(wallet.name));
await walletListViewModel.remove(wallet);
auth.close();
} catch (e) {
auth.changeProcessText(S
.of(context)
.wallet_list_failed_to_remove(wallet.name, e.toString()));
}
});
break;
case 3:
await Navigator.of(context).pushNamed(Routes.rescan);
break;
default:
break;
}
}
}
@@ -1,16 +0,0 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
class WalletMenuItem {
WalletMenuItem({
required this.title,
required this.firstGradientColor,
required this.secondGradientColor,
required this.image
});
final String title;
final Color firstGradientColor;
final Color secondGradientColor;
final Image image;
}
@@ -1,112 +0,0 @@
import 'dart:ui';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
class WalletMenuAlert extends StatelessWidget {
WalletMenuAlert({
required this.wallet,
required this.walletMenu,
required this.items
});
final WalletListItem wallet;
final WalletMenu walletMenu;
final List<WalletMenuItem> items;
final closeButton = Image.asset('assets/images/close.png',
color: Palette.darkBlueCraiola,
);
@override
Widget build(BuildContext context) {
return AlertBackground(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(
left: 24,
right: 24,
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
padding: EdgeInsets.only(left: 24),
child: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: items.length,
separatorBuilder: (context, _) => Container(
height: 1,
color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!,
),
itemBuilder: (_, index) {
final item = items[index];
return GestureDetector(
onTap: () {
Navigator.of(context).pop();
walletMenu.action(
walletMenu.menuItems.indexOf(item),
wallet);
},
child: Container(
height: 60,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 32,
width: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(4)),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
item.firstGradientColor,
item.secondGradientColor
]
)
),
child: Center(
child: item.image,
),
),
SizedBox(width: 12),
Expanded(
child: Text(
item.title,
style: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
decoration: TextDecoration.none
),
)
)
],
),
),
);
},
),
),
),
),
AlertCloseButton(image: closeButton)
],
),
);
}
}
+34 -6
View File
@@ -1,16 +1,20 @@
import 'dart:convert';
import 'dart:io';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mailer/flutter_mailer.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ExceptionHandler {
static bool _hasError = false;
static const _coolDownDurationInDays = 7;
static void _saveException(String? error, StackTrace? stackTrace) async {
final appDocDir = await getApplicationDocumentsDirectory();
@@ -58,18 +62,33 @@ class ExceptionHandler {
}
}
static void onError(FlutterErrorDetails errorDetails) {
static void onError(FlutterErrorDetails errorDetails) async {
if (kDebugMode) {
FlutterError.presentError(errorDetails);
return;
}
if (_ignoreError(errorDetails.exception.toString())) {
return;
}
_saveException(errorDetails.exception.toString(), errorDetails.stack);
if (_hasError) {
final sharedPrefs = await SharedPreferences.getInstance();
final lastPopupDate =
DateTime.tryParse(sharedPrefs.getString(PreferencesKey.lastPopupDate) ?? '') ??
DateTime.now().subtract(Duration(days: _coolDownDurationInDays + 1));
final durationSinceLastReport = DateTime.now().difference(lastPopupDate).inDays;
if (_hasError || durationSinceLastReport < _coolDownDurationInDays) {
return;
}
_hasError = true;
sharedPrefs.setString(PreferencesKey.lastPopupDate, DateTime.now().toString());
WidgetsBinding.instance.addPostFrameCallback(
(timeStamp) async {
await showPopUp<void>(
@@ -98,8 +117,17 @@ class ExceptionHandler {
}
/// Ignore User related errors or system errors
static bool _ignoreError(String error) {
return error.contains("errno = 103") || // SocketException: Software caused connection abort
error.contains("errno = 9"); // SocketException: Bad file descriptor (iOS socket exception)
}
static bool _ignoreError(String error) =>
_ignoredErrors.any((element) => error.contains(element));
static const List<String> _ignoredErrors = const [
"errno = 103", // SocketException: Software caused connection abort
"errno = 9", // SocketException: Bad file descriptor
"errno = 32", // SocketException: Write failed (OS Error: Broken pipe)
"errno = 60", // SocketException: Operation timed out
"errno = 54", // SocketException: Connection reset by peer
"errno = 49", // SocketException: Can't assign requested address
"errno = 28", // OS Error: No space left on device
"PERMISSION_NOT_GRANTED",
];
}
+13
View File
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:share_plus/share_plus.dart';
class ShareUtil {
static void share({required String text, required BuildContext context}) {
final box = context.findRenderObject() as RenderBox?;
Share.share(
text,
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
}
}
@@ -239,7 +239,7 @@ abstract class DashboardViewModelBase with Store {
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
wallet;
bool get hasRescan => wallet.type == WalletType.monero;
bool get hasRescan => wallet.type == WalletType.monero || wallet.type == WalletType.haven;
BalanceViewModel balanceViewModel;
@@ -192,7 +192,7 @@ abstract class ExchangeViewModelBase with Store {
ObservableList<ExchangeTemplate> get templates =>
_exchangeTemplateStore.templates;
@computed
TransactionPriority get transactionPriority {
final priority = _settingsStore.priority[wallet.type];
@@ -308,10 +308,11 @@ abstract class ExchangeViewModelBase with Store {
Future<void> _calculateBestRate() async {
final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
final _providers = _tradeAvailableProviders
.where((element) => !isFixedRateMode || element.supportsFixedRate).toList();
final result = await Future.wait<double>(
_tradeAvailableProviders
.where((element) => !isFixedRateMode || element.supportsFixedRate)
.map((element) => element.fetchRate(
_providers.map((element) => element.fetchRate(
from: depositCurrency,
to: receiveCurrency,
amount: amount,
@@ -324,7 +325,12 @@ abstract class ExchangeViewModelBase with Store {
for (int i=0;i<result.length;i++) {
if (result[i] != 0) {
/// add this provider as its valid for this trade
_sortedAvailableProviders[result[i]] = _tradeAvailableProviders[i];
try {
_sortedAvailableProviders[result[i]] = _providers[i];
} catch (e) {
// will throw "Concurrent modification during iteration" error if modified at the same
// time [createTrade] is called, as this is not a normal map, but a sorted map
}
}
}
if (_sortedAvailableProviders.isNotEmpty) {
+2 -2
View File
@@ -42,7 +42,7 @@ abstract class SupportViewModelBase with Store {
title: 'Telegram',
icon: 'assets/images/Telegram.png',
linkTitle: '@cakewallet_bot',
link: 'https:t.me/cakewallet_bot'),
link: 'https://t.me/cakewallet_bot'),
LinkListItem(
title: 'Twitter',
icon: 'assets/images/Twitter.png',
@@ -84,7 +84,7 @@ abstract class SupportViewModelBase with Store {
// link: 'mailto:support@y.at')
];
static const url = 'https://cakewallet.com/guide/';
static const url = 'https://guides.cakewallet.com';
List<SettingsListItem> items;
}