Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-78-Ethereum

This commit is contained in:
OmarHatem 2023-06-13 23:01:26 +03:00
commit eff0369709
28 changed files with 753 additions and 627 deletions

View file

@ -107,7 +107,6 @@ jobs:
echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const sideShiftApiKey = '${{ secrets.SIDE_SHIFT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart

View file

Before

Width:  |  Height:  |  Size: 193 B

After

Width:  |  Height:  |  Size: 193 B

View file

@ -40,6 +40,7 @@ import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/desktop_sidebar_view_model.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; import 'package:cake_wallet/view_model/anonpay_details_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
@ -876,6 +877,8 @@ Future setup({
getIt.registerFactory(() => IoniaGiftCardsListViewModel(ioniaService: getIt.get<IoniaService>())); getIt.registerFactory(() => IoniaGiftCardsListViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactory(()=> MarketPlaceViewModel(getIt.get<IoniaService>()));
getIt.registerFactory(() => IoniaAuthViewModel(ioniaService: getIt.get<IoniaService>())); getIt.registerFactory(() => IoniaAuthViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactoryParam<IoniaMerchPurchaseViewModel, double, IoniaMerchant>( getIt.registerFactoryParam<IoniaMerchPurchaseViewModel, double, IoniaMerchant>(
@ -905,7 +908,7 @@ Future setup({
return IoniaVerifyIoniaOtp(getIt.get<IoniaAuthViewModel>(), email, isSignIn); return IoniaVerifyIoniaOtp(getIt.get<IoniaAuthViewModel>(), email, isSignIn);
}); });
getIt.registerFactory(() => IoniaWelcomePage(getIt.get<IoniaGiftCardsListViewModel>())); getIt.registerFactory(() => IoniaWelcomePage());
getIt.registerFactoryParam<IoniaBuyGiftCardPage, List, void>((List args, _) { getIt.registerFactoryParam<IoniaBuyGiftCardPage, List, void>((List args, _) {
final merchant = args.first as IoniaMerchant; final merchant = args.first as IoniaMerchant;

View file

@ -55,11 +55,11 @@ class LanguageService {
'cs': 'czk', 'cs': 'czk',
'ur': 'pak', 'ur': 'pak',
'id': 'idn', 'id': 'idn',
'yo': 'yor', 'yo': 'nga',
'ha': 'hau' 'ha': 'hau'
}; };
static final list = <String, String> {}; static final list = <String, String>{};
static void loadLocaleList() { static void loadLocaleList() {
supportedLocales.forEach((key, value) { supportedLocales.forEach((key, value) {

View file

@ -19,10 +19,10 @@ class SideShiftExchangeProvider extends ExchangeProvider {
static const affiliateId = secrets.sideShiftAffiliateId; static const affiliateId = secrets.sideShiftAffiliateId;
static const apiBaseUrl = 'https://sideshift.ai/api'; static const apiBaseUrl = 'https://sideshift.ai/api';
static const rangePath = '/v1/pairs'; static const rangePath = '/v2/pair';
static const orderPath = '/v1/orders'; static const orderPath = '/v2/shifts';
static const quotePath = '/v1/quotes'; static const quotePath = '/v2/quotes';
static const permissionPath = '/v1/permissions'; static const permissionPath = '/v2/permissions';
static const List<CryptoCurrency> _notSupported = [ static const List<CryptoCurrency> _notSupported = [
CryptoCurrency.xhv, CryptoCurrency.xhv,
@ -36,23 +36,22 @@ class SideShiftExchangeProvider extends ExchangeProvider {
CryptoCurrency.scrt, CryptoCurrency.scrt,
CryptoCurrency.stx, CryptoCurrency.stx,
CryptoCurrency.bttc, CryptoCurrency.bttc,
CryptoCurrency.usdt,
CryptoCurrency.eos,
]; ];
static List<ExchangePair> _supportedPairs() { static List<ExchangePair> _supportedPairs() {
final supportedCurrencies = CryptoCurrency.all final supportedCurrencies =
.where((element) => !_notSupported.contains(element)) CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList();
.toList();
return supportedCurrencies return supportedCurrencies
.map((i) => supportedCurrencies .map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.expand((i) => i) .expand((i) => i)
.toList(); .toList();
} }
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description => ExchangeProviderDescription.sideShift;
ExchangeProviderDescription.sideShift;
@override @override
Future<double> fetchRate( Future<double> fetchRate(
@ -65,17 +64,18 @@ class SideShiftExchangeProvider extends ExchangeProvider {
if (amount == 0) { if (amount == 0) {
return 0.0; return 0.0;
} }
final fromCurrency = _normalizeCryptoCurrency(from);
final toCurrency = _normalizeCryptoCurrency(to); final fromCurrency = from.title.toLowerCase();
final url = final toCurrency = to.title.toLowerCase();
apiBaseUrl + rangePath + '/' + fromCurrency + '/' + toCurrency; final depositNetwork = _networkFor(from);
final settleNetwork = _networkFor(to);
final url = "$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork";
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await get(uri); final response = await get(uri);
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final rate = double.parse(responseJSON['rate'] as String); final rate = double.parse(responseJSON['rate'] as String);
final max = double.parse(responseJSON['max'] as String);
if (amount > max) return 0.00;
return rate; return rate;
} catch (_) { } catch (_) {
@ -101,25 +101,38 @@ class SideShiftExchangeProvider extends ExchangeProvider {
} }
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final canCreateOrder = responseJSON['createOrder'] as bool; final cancreateShift = responseJSON['createShift'] as bool;
final canCreateQuote = responseJSON['createQuote'] as bool; return cancreateShift;
return canCreateOrder && canCreateQuote;
} }
@override @override
Future<Trade> createTrade( Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
{required TradeRequest request, required bool isFixedRateMode}) async {
final _request = request as SideShiftRequest; final _request = request as SideShiftRequest;
final quoteId = await _createQuote(_request); String url = '';
final url = apiBaseUrl + orderPath; final depositCoin = request.depositMethod.title.toLowerCase();
final headers = {'Content-Type': 'application/json'}; final settleCoin = request.settleMethod.title.toLowerCase();
final body = { final body = {
'type': 'fixed',
'quoteId': quoteId,
'affiliateId': affiliateId, 'affiliateId': affiliateId,
'settleAddress': _request.settleAddress, 'settleAddress': _request.settleAddress,
'refundAddress': _request.refundAddress 'refundAddress': _request.refundAddress,
}; };
if (isFixedRateMode) {
final quoteId = await _createQuote(_request);
body['quoteId'] = quoteId;
url = apiBaseUrl + orderPath + '/fixed';
} else {
url = apiBaseUrl + orderPath + '/variable';
final depositNetwork = _networkFor(request.depositMethod);
final settleNetwork = _networkFor(request.settleMethod);
body["depositCoin"] = depositCoin;
body["settleCoin"] = settleCoin;
body["settleNetwork"] = settleNetwork;
body["depositNetwork"] = depositNetwork;
}
final headers = {'Content-Type': 'application/json'};
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await post(uri, headers: headers, body: json.encode(body)); final response = await post(uri, headers: headers, body: json.encode(body));
@ -136,8 +149,9 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['id'] as String; final id = responseJSON['id'] as String;
final inputAddress = responseJSON['depositAddress']['address'] as String; final inputAddress = responseJSON['depositAddress'] as String;
final settleAddress = responseJSON['settleAddress']['address'] as String; final settleAddress = responseJSON['settleAddress'] as String;
final depositAmount = responseJSON['depositAmount'] as String?;
return Trade( return Trade(
id: id, id: id,
@ -147,7 +161,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
inputAddress: inputAddress, inputAddress: inputAddress,
refundAddress: settleAddress, refundAddress: settleAddress,
state: TradeState.created, state: TradeState.created,
amount: _request.depositAmount, amount: depositAmount ?? _request.depositAmount,
payoutAddress: settleAddress, payoutAddress: settleAddress,
createdAt: DateTime.now(), createdAt: DateTime.now(),
); );
@ -156,13 +170,17 @@ class SideShiftExchangeProvider extends ExchangeProvider {
Future<String> _createQuote(SideShiftRequest request) async { Future<String> _createQuote(SideShiftRequest request) async {
final url = apiBaseUrl + quotePath; final url = apiBaseUrl + quotePath;
final headers = {'Content-Type': 'application/json'}; final headers = {'Content-Type': 'application/json'};
final depositMethod = _normalizeCryptoCurrency(request.depositMethod); final depositMethod = request.depositMethod.title.toLowerCase();
final settleMethod = _normalizeCryptoCurrency(request.settleMethod); final settleMethod = request.settleMethod.title.toLowerCase();
final depositNetwork = _networkFor(request.depositMethod);
final settleNetwork = _networkFor(request.settleMethod);
final body = { final body = {
'depositMethod': depositMethod, 'depositCoin': depositMethod,
'settleMethod': settleMethod, 'settleCoin': settleMethod,
'affiliateId': affiliateId, 'affiliateId': affiliateId,
'depositAmount': request.depositAmount, 'settleAmount': request.depositAmount,
'settleNetwork': settleNetwork,
'depositNetwork': depositNetwork,
}; };
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await post(uri, headers: headers, body: json.encode(body)); final response = await post(uri, headers: headers, body: json.encode(body));
@ -189,9 +207,15 @@ class SideShiftExchangeProvider extends ExchangeProvider {
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required bool isFixedRateMode}) async { required bool isFixedRateMode}) async {
final fromCurrency = _normalizeCryptoCurrency(from); final fromCurrency = isFixedRateMode ? to : from;
final toCurrency = _normalizeCryptoCurrency(to); final toCurrency = isFixedRateMode ? from : to;
final url = apiBaseUrl + rangePath + '/' + fromCurrency + '/' + toCurrency;
final fromNetwork = _networkFor(fromCurrency);
final toNetwork = _networkFor(toCurrency);
final url =
"$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork";
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await get(uri); final response = await get(uri);
@ -210,6 +234,14 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final min = double.tryParse(responseJSON['min'] as String? ?? ''); final min = double.tryParse(responseJSON['min'] as String? ?? '');
final max = double.tryParse(responseJSON['max'] as String? ?? ''); final max = double.tryParse(responseJSON['max'] as String? ?? '');
if (isFixedRateMode) {
final currentRate = double.parse(responseJSON['rate'] as String);
return Limits(
min: min != null ? (min * currentRate) : null,
max: max != null ? (max * currentRate) : null,
);
}
return Limits(min: min, max: max); return Limits(min: min, max: max);
} }
@ -227,8 +259,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error']['message'] as String; final error = responseJSON['error']['message'] as String;
throw TradeNotFoundException(id, throw TradeNotFoundException(id, provider: description, description: error);
provider: description, description: error);
} }
if (response.statusCode != 200) { if (response.statusCode != 200) {
@ -236,24 +267,21 @@ class SideShiftExchangeProvider extends ExchangeProvider {
} }
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final fromCurrency = responseJSON['depositMethodId'] as String; final fromCurrency = responseJSON['depositCoin'] as String;
final from = CryptoCurrency.fromString(fromCurrency); final from = CryptoCurrency.fromString(fromCurrency);
final toCurrency = responseJSON['settleMethodId'] as String; final toCurrency = responseJSON['settleCoin'] as String;
final to = CryptoCurrency.fromString(toCurrency); final to = CryptoCurrency.fromString(toCurrency);
final inputAddress = responseJSON['depositAddress']['address'] as String; final inputAddress = responseJSON['depositAddress'] as String;
final expectedSendAmount = responseJSON['depositAmount'].toString(); final expectedSendAmount = responseJSON['depositAmount'] as String?;
final deposits = responseJSON['deposits'] as List?; final status = responseJSON['status'] as String?;
final settleAddress = responseJSON['settleAddress']['address'] as String; final settleAddress = responseJSON['settleAddress'] as String;
TradeState? state; TradeState? state;
String? status;
if (deposits?.isNotEmpty ?? false) {
status = deposits![0]['status'] as String?;
}
state = TradeState.deserialize(raw: status ?? 'created'); state = TradeState.deserialize(raw: status ?? 'created');
final isVariable = (responseJSON['type'] as String) == 'variable';
final expiredAtRaw = responseJSON['expiresAtISO'] as String; final expiredAtRaw = responseJSON['expiresAt'] as String;
final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal(); final expiredAt = isVariable ? null : DateTime.tryParse(expiredAtRaw)?.toLocal();
return Trade( return Trade(
id: id, id: id,
@ -261,11 +289,10 @@ class SideShiftExchangeProvider extends ExchangeProvider {
to: to, to: to,
provider: description, provider: description,
inputAddress: inputAddress, inputAddress: inputAddress,
amount: expectedSendAmount, amount: expectedSendAmount ?? '',
state: state, state: state,
expiredAt: expiredAt, expiredAt: expiredAt,
payoutAddress: settleAddress payoutAddress: settleAddress);
);
} }
@override @override
@ -280,28 +307,25 @@ class SideShiftExchangeProvider extends ExchangeProvider {
@override @override
String get title => 'SideShift'; String get title => 'SideShift';
static String _normalizeCryptoCurrency(CryptoCurrency currency) { String _networkFor(CryptoCurrency currency) =>
switch (currency) { currency.tag != null ? _normalizeTag(currency.tag!) : 'mainnet';
case CryptoCurrency.zaddr:
return 'zaddr'; String _normalizeTag(String tag) {
case CryptoCurrency.zec: switch (tag) {
return 'zec'; case 'ETH':
case CryptoCurrency.bnb: return 'ethereum';
return currency.tag!.toLowerCase(); case 'TRX':
case CryptoCurrency.usdterc20: return 'tron';
return 'usdtErc20'; case 'LN':
case CryptoCurrency.usdttrc20: return 'lightning';
return 'usdtTrc20'; case 'POLY':
case CryptoCurrency.usdcpoly:
return 'usdcpolygon';
case CryptoCurrency.usdcsol:
return 'usdcsol';
case CryptoCurrency.maticpoly:
return 'polygon'; return 'polygon';
case CryptoCurrency.btcln: case 'ZEC':
return 'ln'; return 'zcash';
case 'AVAXC':
return 'avax';
default: default:
return currency.title.toLowerCase(); return tag.toLowerCase();
} }
} }
} }

View file

@ -1,10 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/sync_status.dart'; import 'package:cw_core/sync_status.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:connectivity/connectivity.dart';
Timer? _checkConnectionTimer; Timer? _checkConnectionTimer;
void startCheckConnectionReaction( void startCheckConnectionReaction(

View file

@ -175,7 +175,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
fullscreenDialog: true); fullscreenDialog: true);
} else if (isSingleCoin) { } else if (isSingleCoin) {
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<WalletRestorePage>( builder: (_) => getIt.get<WalletRestorePage>(
param1: availableWalletTypes.first param1: availableWalletTypes.first
)); ));
@ -196,7 +195,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.restoreWallet: case Routes.restoreWallet:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<WalletRestorePage>( builder: (_) => getIt.get<WalletRestorePage>(
param1: settings.arguments as WalletType)); param1: settings.arguments as WalletType));
@ -206,6 +204,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.dashboard: case Routes.dashboard:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
settings: settings,
builder: (_) => getIt.get<DashboardPage>()); builder: (_) => getIt.get<DashboardPage>());
case Routes.send: case Routes.send:
@ -468,7 +467,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaCreateAccountPage>()); return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaCreateAccountPage>());
case Routes.ioniaManageCardsPage: case Routes.ioniaManageCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaManageCardsPage>()); return CupertinoPageRoute<void>(
builder: (_) => getIt.get<IoniaManageCardsPage>());
case Routes.ioniaBuyGiftCardPage: case Routes.ioniaBuyGiftCardPage:
final args = settings.arguments as List; final args = settings.arguments as List;
@ -536,7 +536,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.anonPayInvoicePage: case Routes.anonPayInvoicePage:
final args = settings.arguments as List; final args = settings.arguments as List;
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args)); builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args));
case Routes.anonPayReceivePage: case Routes.anonPayReceivePage:

View file

@ -5,7 +5,7 @@ import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cake_wallet/utils/version_comparator.dart'; import 'package:cake_wallet/utils/version_comparator.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
@ -27,8 +27,6 @@ import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart'; import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart';
class DashboardPage extends StatelessWidget { class DashboardPage extends StatelessWidget {
@ -251,7 +249,12 @@ class _DashboardPageView extends BasePage {
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) { if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(Semantics( pages.add(Semantics(
label: 'Marketplace Page', label: 'Marketplace Page',
child: MarketPlacePage(dashboardViewModel: dashboardViewModel))); child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
),
),
);
} }
pages.add(Semantics(label: 'Balance Page', child: balancePage)); pages.add(Semantics(label: 'Balance Page', child: balancePage));
pages.add(Semantics( pages.add(Semantics(

View file

@ -1,7 +1,9 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -70,7 +72,10 @@ class DesktopDashboardActions extends StatelessWidget {
], ],
), ),
Expanded( Expanded(
child: MarketPlacePage(dashboardViewModel: dashboardViewModel), child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
),
), ),
], ],
); );

View file

@ -273,7 +273,7 @@ class AddressPage extends BasePage {
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) { reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
switch (option) { switch (option) {
case ReceivePageOption.anonPayInvoice: case ReceivePageOption.anonPayInvoice:
Navigator.pushReplacementNamed( Navigator.pushNamed(
context, context,
Routes.anonPayInvoicePage, Routes.anonPayInvoicePage,
arguments: [addressListViewModel.address.address, option], arguments: [addressListViewModel.address.address, option],
@ -285,7 +285,7 @@ class AddressPage extends BasePage {
final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink); final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink);
if (clearnetUrl != null && onionUrl != null) { if (clearnetUrl != null && onionUrl != null) {
Navigator.pushReplacementNamed( Navigator.pushNamed(
context, context,
Routes.anonPayReceivePage, Routes.anonPayReceivePage,
arguments: AnonpayDonationLinkInfo( arguments: AnonpayDonationLinkInfo(
@ -295,7 +295,7 @@ class AddressPage extends BasePage {
), ),
); );
} else { } else {
Navigator.pushReplacementNamed( Navigator.pushNamed(
context, context,
Routes.anonPayInvoicePage, Routes.anonPayInvoicePage,
arguments: [addressListViewModel.address.address, option], arguments: [addressListViewModel.address.address, option],

View file

@ -3,16 +3,20 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/market_place_item.dart'; import 'package:cake_wallet/src/widgets/market_place_item.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class MarketPlacePage extends StatelessWidget { class MarketPlacePage extends StatelessWidget {
MarketPlacePage({
MarketPlacePage({required this.dashboardViewModel}); required this.dashboardViewModel,
required this.marketPlaceViewModel,
});
final DashboardViewModel dashboardViewModel; final DashboardViewModel dashboardViewModel;
final MarketPlaceViewModel marketPlaceViewModel;
final _scrollController = ScrollController(); final _scrollController = ScrollController();
@override @override
@ -48,7 +52,7 @@ class MarketPlacePage extends StatelessWidget {
children: <Widget>[ children: <Widget>[
SizedBox(height: 20), SizedBox(height: 20),
MarketPlaceItem( MarketPlaceItem(
onTap: () =>_navigatorToGiftCardsPage(context), onTap: () => _navigatorToGiftCardsPage(context),
title: S.of(context).cake_pay_title, title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle, subTitle: S.of(context).cake_pay_subtitle,
), ),
@ -70,6 +74,7 @@ class MarketPlacePage extends StatelessWidget {
), ),
); );
} }
void _navigatorToGiftCardsPage(BuildContext context) { void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type; final walletType = dashboardViewModel.type;
@ -86,8 +91,13 @@ class MarketPlacePage extends StatelessWidget {
}); });
break; break;
default: default:
marketPlaceViewModel.isIoniaUserAuthenticated().then((value) {
if (value) {
Navigator.pushNamed(context, Routes.ioniaManageCardsPage);
return;
}
Navigator.of(context).pushNamed(Routes.ioniaWelcomePage); Navigator.of(context).pushNamed(Routes.ioniaWelcomePage);
});
} }
} }
} }

View file

@ -25,7 +25,12 @@ class TransactionsPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return GestureDetector(
onLongPress: () => dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
onLongPressUp: () => dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
child: Container(
color: ResponsiveLayoutUtil.instance.isMobile(context) color: ResponsiveLayoutUtil.instance.isMobile(context)
? null ? null
: Theme.of(context).colorScheme.background, : Theme.of(context).colorScheme.background,
@ -51,34 +56,40 @@ class TransactionsPage extends StatelessWidget {
return Observer( return Observer(
builder: (_) => TransactionRow( builder: (_) => TransactionRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context).pushNamed(
.pushNamed(Routes.transactionDetails, arguments: transaction), Routes.transactionDetails,
arguments: transaction),
direction: transaction.direction, direction: transaction.direction,
formattedDate: DateFormat('HH:mm').format(transaction.date), formattedDate: DateFormat('HH:mm')
.format(transaction.date),
formattedAmount: item.formattedCryptoAmount, formattedAmount: item.formattedCryptoAmount,
formattedFiatAmount: formattedFiatAmount: dashboardViewModel
dashboardViewModel.balanceViewModel.isFiatDisabled .balanceViewModel.isFiatDisabled
? '' ? ''
: item.formattedFiatAmount, : item.formattedFiatAmount,
isPending: transaction.isPending, isPending: transaction.isPending,
title: item.formattedTitle + item.formattedStatus)); title: item.formattedTitle +
item.formattedStatus));
} }
if (item is AnonpayTransactionListItem) { if (item is AnonpayTransactionListItem) {
final transactionInfo = item.transaction; final transactionInfo = item.transaction;
return AnonpayTransactionRow( return AnonpayTransactionRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context).pushNamed(
.pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), Routes.anonPayDetailsPage,
arguments: transactionInfo),
currency: transactionInfo.fiatAmount != null currency: transactionInfo.fiatAmount != null
? transactionInfo.fiatEquiv ?? '' ? transactionInfo.fiatEquiv ?? ''
: CryptoCurrency.fromFullName(transactionInfo.coinTo) : CryptoCurrency.fromFullName(
transactionInfo.coinTo)
.name .name
.toUpperCase(), .toUpperCase(),
provider: transactionInfo.provider, provider: transactionInfo.provider,
amount: transactionInfo.fiatAmount?.toString() ?? amount: transactionInfo.fiatAmount?.toString() ??
(transactionInfo.amountTo?.toString() ?? ''), (transactionInfo.amountTo?.toString() ?? ''),
createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt), createdAt: DateFormat('HH:mm')
.format(transactionInfo.createdAt),
); );
} }
@ -87,13 +98,16 @@ class TransactionsPage extends StatelessWidget {
return Observer( return Observer(
builder: (_) => TradeRow( builder: (_) => TradeRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context).pushNamed(
.pushNamed(Routes.tradeDetails, arguments: trade), Routes.tradeDetails,
arguments: trade),
provider: trade.provider, provider: trade.provider,
from: trade.from, from: trade.from,
to: trade.to, to: trade.to,
createdAtFormattedDate: trade.createdAt != null createdAtFormattedDate:
? DateFormat('HH:mm').format(trade.createdAt!) trade.createdAt != null
? DateFormat('HH:mm')
.format(trade.createdAt!)
: null, : null,
formattedAmount: item.tradeFormattedAmount)); formattedAmount: item.tradeFormattedAmount));
} }
@ -104,12 +118,13 @@ class TransactionsPage extends StatelessWidget {
return Observer( return Observer(
builder: (_) => OrderRow( builder: (_) => OrderRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context)
.pushNamed(Routes.orderDetails, arguments: order), .pushNamed(Routes.orderDetails,
arguments: order),
provider: order.provider, provider: order.provider,
from: order.from!, from: order.from!,
to: order.to!, to: order.to!,
createdAtFormattedDate: createdAtFormattedDate: DateFormat('HH:mm')
DateFormat('HH:mm').format(order.createdAt), .format(order.createdAt),
formattedAmount: item.orderFormattedAmount, formattedAmount: item.orderFormattedAmount,
)); ));
} }
@ -121,12 +136,16 @@ class TransactionsPage extends StatelessWidget {
S.of(context).placeholder_transactions, S.of(context).placeholder_transactions,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Theme.of(context).primaryTextTheme!.labelSmall!.decorationColor!), color: Theme.of(context)
.primaryTextTheme
.labelSmall!
.decorationColor!),
), ),
); );
})) }))
], ],
), ),
),
); );
} }
} }

View file

@ -3,14 +3,11 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/typography.dart'; import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:mobx/mobx.dart';
class IoniaWelcomePage extends BasePage { class IoniaWelcomePage extends BasePage {
IoniaWelcomePage(this._cardsListViewModel); IoniaWelcomePage();
@override @override
Widget middle(BuildContext context) { Widget middle(BuildContext context) {
@ -25,15 +22,8 @@ class IoniaWelcomePage extends BasePage {
); );
} }
final IoniaGiftCardsListViewModel _cardsListViewModel;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
reaction((_) => _cardsListViewModel.isLoggedIn, (bool state) {
if (state) {
Navigator.pushReplacementNamed(context, Routes.ioniaManageCardsPage);
}
});
return Padding( return Padding(
padding: const EdgeInsets.all(24.0), padding: const EdgeInsets.all(24.0),
child: Column( child: Column(
@ -41,7 +31,7 @@ class IoniaWelcomePage extends BasePage {
children: [ children: [
Column( Column(
children: [ children: [
SizedBox(height: 100), SizedBox(height: 90),
Text( Text(
S.of(context).about_cake_pay, S.of(context).about_cake_pay,
style: TextStyle( style: TextStyle(

View file

@ -17,7 +17,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
class IoniaManageCardsPage extends BasePage { class IoniaManageCardsPage extends BasePage {
IoniaManageCardsPage(this._cardsListViewModel) { IoniaManageCardsPage(this._cardsListViewModel): searchFocusNode = FocusNode() {
_searchController.addListener(() { _searchController.addListener(() {
if (_searchController.text != _cardsListViewModel.searchString) { if (_searchController.text != _cardsListViewModel.searchString) {
_searchDebounce.run(() { _searchDebounce.run(() {
@ -29,6 +29,7 @@ class IoniaManageCardsPage extends BasePage {
_cardsListViewModel.getMerchants(); _cardsListViewModel.getMerchants();
} }
final FocusNode searchFocusNode;
final IoniaGiftCardsListViewModel _cardsListViewModel; final IoniaGiftCardsListViewModel _cardsListViewModel;
final _searchDebounce = Debounce(Duration(milliseconds: 500)); final _searchDebounce = Debounce(Duration(milliseconds: 500));
@ -86,7 +87,14 @@ class IoniaManageCardsPage extends BasePage {
//highlightColor: Colors.transparent, //highlightColor: Colors.transparent,
//splashColor: Colors.transparent, //splashColor: Colors.transparent,
//padding: EdgeInsets.all(0), //padding: EdgeInsets.all(0),
onPressed: () => Navigator.pop(context), onPressed: (){
if (searchFocusNode.hasFocus) {
searchFocusNode.unfocus();
return;
}
Navigator.of(context).pop();
},
child: _backButton), child: _backButton),
), ),
); );
@ -149,6 +157,7 @@ class IoniaManageCardsPage extends BasePage {
Expanded( Expanded(
child: _SearchWidget( child: _SearchWidget(
controller: _searchController, controller: _searchController,
focusNode: searchFocusNode,
)), )),
SizedBox(width: 10), SizedBox(width: 10),
filterButton filterButton
@ -266,9 +275,10 @@ class _SearchWidget extends StatelessWidget {
const _SearchWidget({ const _SearchWidget({
Key? key, Key? key,
required this.controller, required this.controller,
required this.focusNode,
}) : super(key: key); }) : super(key: key);
final TextEditingController controller; final TextEditingController controller;
final FocusNode focusNode;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final searchIcon = Padding( final searchIcon = Padding(
@ -280,6 +290,7 @@ class _SearchWidget extends StatelessWidget {
); );
return TextField( return TextField(
focusNode: focusNode,
style: TextStyle(color: Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!), style: TextStyle(color: Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!),
controller: controller, controller: controller,
decoration: InputDecoration( decoration: InputDecoration(

View file

@ -27,8 +27,7 @@ class AnonPayInvoicePage extends BasePage {
AnonPayInvoicePage( AnonPayInvoicePage(
this.anonInvoicePageViewModel, this.anonInvoicePageViewModel,
this.receiveOptionViewModel, this.receiveOptionViewModel,
) : _amountFocusNode = FocusNode() { ) : _amountFocusNode = FocusNode() {}
}
final _nameController = TextEditingController(); final _nameController = TextEditingController();
final _emailController = TextEditingController(); final _emailController = TextEditingController();
@ -53,6 +52,11 @@ class AnonPayInvoicePage extends BasePage {
@override @override
AppBarStyle get appBarStyle => AppBarStyle.transparent; AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
void onClose(BuildContext context) {
Navigator.popUntil(context, ModalRoute.withName(Routes.dashboard));
}
@override @override
Widget middle(BuildContext context) => Widget middle(BuildContext context) =>
PresentReceiveOptionPicker(receiveOptionViewModel: receiveOptionViewModel); PresentReceiveOptionPicker(receiveOptionViewModel: receiveOptionViewModel);
@ -65,11 +69,18 @@ class AnonPayInvoicePage extends BasePage {
anonInvoicePageViewModel.reset(); anonInvoicePageViewModel.reset();
}); });
Future<bool> _onNavigateBack(BuildContext context) async {
onClose(context);
return false;
}
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context)); WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context));
return KeyboardActions( return WillPopScope(
onWillPop: () => _onNavigateBack(context),
child: KeyboardActions(
disableScroll: true, disableScroll: true,
config: KeyboardActionsConfig( config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
@ -89,7 +100,8 @@ class AnonPayInvoicePage extends BasePage {
child: ScrollableWithBottomSection( child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24), contentPadding: EdgeInsets.only(bottom: 24),
content: Container( content: Container(
decoration: DeviceInfo.instance.isMobile ? BoxDecoration( decoration: DeviceInfo.instance.isMobile
? BoxDecoration(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient( gradient: LinearGradient(
@ -100,7 +112,8 @@ class AnonPayInvoicePage extends BasePage {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
), ),
) : null, )
: null,
child: Observer(builder: (_) { child: Observer(builder: (_) {
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(24, 120, 24, 0), padding: EdgeInsets.fromLTRB(24, 120, 24, 0),
@ -143,8 +156,9 @@ class AnonPayInvoicePage extends BasePage {
), ),
), ),
LoadingPrimaryButton( LoadingPrimaryButton(
text: text: isInvoice
isInvoice ? S.of(context).create_invoice : S.of(context).create_donation_link, ? S.of(context).create_invoice
: S.of(context).create_donation_link,
onPressed: () { onPressed: () {
anonInvoicePageViewModel.setRequestParams( anonInvoicePageViewModel.setRequestParams(
inputAmount: _amountController.text, inputAmount: _amountController.text,
@ -175,6 +189,7 @@ class AnonPayInvoicePage extends BasePage {
}), }),
), ),
), ),
),
); );
} }

View file

@ -266,6 +266,8 @@ class WalletRestorePage extends BasePage {
} }
void _confirmForm() { void _confirmForm() {
// Dismissing all visible keyboard to provide context for navigation
FocusManager.instance.primaryFocus?.unfocus();
final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey.currentContext ? walletRestoreFromSeedFormKey.currentContext
: walletRestoreFromKeysFormKey.currentContext; : walletRestoreFromKeysFormKey.currentContext;

View file

@ -145,7 +145,12 @@ class SendPage extends BasePage {
Widget body(BuildContext context) { Widget body(BuildContext context) {
_setEffects(context); _setEffects(context);
return Form( return GestureDetector(
onLongPress: () => sendViewModel.balanceViewModel.isReversing =
!sendViewModel.balanceViewModel.isReversing,
onLongPressUp: () => sendViewModel.balanceViewModel.isReversing =
!sendViewModel.balanceViewModel.isReversing,
child: Form(
key: _formKey, key: _formKey,
child: ScrollableWithBottomSection( child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24), contentPadding: EdgeInsets.only(bottom: 24),
@ -174,8 +179,8 @@ class SendPage extends BasePage {
}, },
)), )),
Padding( Padding(
padding: padding: EdgeInsets.only(
EdgeInsets.only(top: 10, left: 24, right: 24, bottom: 10), top: 10, left: 24, right: 24, bottom: 10),
child: Container( child: Container(
height: 10, height: 10,
child: Observer( child: Observer(
@ -192,10 +197,12 @@ class SendPage extends BasePage {
dotWidth: 6.0, dotWidth: 6.0,
dotHeight: 6.0, dotHeight: 6.0,
dotColor: Theme.of(context) dotColor: Theme.of(context)
.primaryTextTheme!.displaySmall! .primaryTextTheme
!.displaySmall!
.backgroundColor!, .backgroundColor!,
activeDotColor: Theme.of(context) activeDotColor: Theme.of(context)
.primaryTextTheme!.displayMedium! .primaryTextTheme
!.displayMedium!
.backgroundColor!), .backgroundColor!),
) )
: Offstage(); : Offstage();
@ -218,7 +225,8 @@ class SendPage extends BasePage {
return Row( return Row(
children: <Widget>[ children: <Widget>[
AddTemplateButton( AddTemplateButton(
onTap: () => Navigator.of(context).pushNamed(Routes.sendTemplate), onTap: () => Navigator.of(context)
.pushNamed(Routes.sendTemplate),
currentTemplatesLength: templates.length, currentTemplatesLength: templates.length,
), ),
ListView.builder( ListView.builder(
@ -231,35 +239,52 @@ class SendPage extends BasePage {
return TemplateTile( return TemplateTile(
key: UniqueKey(), key: UniqueKey(),
to: template.name, to: template.name,
amount: template.isCurrencySelected ? template.amount : template.amountFiat, amount: template.isCurrencySelected
from: template.isCurrencySelected ? template.cryptoCurrency : template.fiatCurrency, ? template.amount
: template.amountFiat,
from: template.isCurrencySelected
? template.cryptoCurrency
: template.fiatCurrency,
onTap: () async { onTap: () async {
final fiatFromTemplate = FiatCurrency.all.singleWhere((element) => element.title == template.fiatCurrency); final fiatFromTemplate = FiatCurrency
.all
.singleWhere((element) =>
element.title ==
template.fiatCurrency);
final output = _defineCurrentOutput(); final output = _defineCurrentOutput();
output.address = template.address; output.address = template.address;
if(template.isCurrencySelected){ if (template.isCurrencySelected) {
output.setCryptoAmount(template.amount); output
}else{ .setCryptoAmount(template.amount);
sendViewModel.setFiatCurrency(fiatFromTemplate); } else {
output.setFiatAmount(template.amountFiat); sendViewModel.setFiatCurrency(
fiatFromTemplate);
output.setFiatAmount(
template.amountFiat);
} }
output.resetParsedAddress(); output.resetParsedAddress();
await output.fetchParsedAddress(context); await output
.fetchParsedAddress(context);
}, },
onRemove: () { onRemove: () {
showPopUp<void>( showPopUp<void>(
context: context, context: context,
builder: (dialogContext) { builder: (dialogContext) {
return AlertWithTwoActions( return AlertWithTwoActions(
alertTitle: S.of(context).template, alertTitle:
S.of(context).template,
alertContent: S alertContent: S
.of(context) .of(context)
.confirm_delete_template, .confirm_delete_template,
rightButtonText: S.of(context).delete, rightButtonText:
leftButtonText: S.of(context).cancel, S.of(context).delete,
leftButtonText:
S.of(context).cancel,
actionRightButton: () { actionRightButton: () {
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext)
sendViewModel.sendTemplateViewModel .pop();
sendViewModel
.sendTemplateViewModel
.removeTemplate( .removeTemplate(
template: template); template: template);
}, },
@ -286,19 +311,19 @@ class SendPage extends BasePage {
bottomSection: Column( bottomSection: Column(
children: [ children: [
if (sendViewModel.hasCurrecyChanger) if (sendViewModel.hasCurrecyChanger)
Observer(builder: (_) => Observer(
Padding( builder: (_) => Padding(
padding: EdgeInsets.only(bottom: 12), padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton( child: PrimaryButton(
onPressed: () => presentCurrencyPicker(context), onPressed: () => presentCurrencyPicker(context),
text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})', text:
'Change your asset (${sendViewModel.selectedCryptoCurrency})',
color: Colors.transparent, color: Colors.transparent,
textColor: Theme.of(context) textColor: Theme.of(context)
.accentTextTheme!.displaySmall! .accentTextTheme
!.displaySmall!
.decorationColor!, .decorationColor!,
) ))),
)
),
if (sendViewModel.hasMultiRecipient) if (sendViewModel.hasMultiRecipient)
Padding( Padding(
padding: EdgeInsets.only(bottom: 12), padding: EdgeInsets.only(bottom: 12),
@ -306,24 +331,28 @@ class SendPage extends BasePage {
onPressed: () { onPressed: () {
sendViewModel.addOutput(); sendViewModel.addOutput();
Future.delayed(const Duration(milliseconds: 250), () { Future.delayed(const Duration(milliseconds: 250), () {
controller.jumpToPage(sendViewModel.outputs.length - 1); controller
.jumpToPage(sendViewModel.outputs.length - 1);
}); });
}, },
text: S.of(context).add_receiver, text: S.of(context).add_receiver,
color: Colors.transparent, color: Colors.transparent,
textColor: Theme.of(context) textColor: Theme.of(context)
.accentTextTheme!.displaySmall! .accentTextTheme
!.displaySmall!
.decorationColor!, .decorationColor!,
isDottedBorder: true, isDottedBorder: true,
borderColor: Theme.of(context) borderColor: Theme.of(context)
.primaryTextTheme!.displaySmall! .primaryTextTheme
!.displaySmall!
.decorationColor!, .decorationColor!,
)), )),
Observer( Observer(
builder: (_) { builder: (_) {
return LoadingPrimaryButton( return LoadingPrimaryButton(
onPressed: () async { onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { if (_formKey.currentState != null &&
!_formKey.currentState!.validate()) {
if (sendViewModel.outputs.length > 1) { if (sendViewModel.outputs.length > 1) {
showErrorValidationAlert(context); showErrorValidationAlert(context);
} }
@ -333,7 +362,8 @@ class SendPage extends BasePage {
final notValidItems = sendViewModel.outputs final notValidItems = sendViewModel.outputs
.where((item) => .where((item) =>
item.address.isEmpty || item.cryptoAmount.isEmpty) item.address.isEmpty ||
item.cryptoAmount.isEmpty)
.toList(); .toList();
if (notValidItems.isNotEmpty ?? false) { if (notValidItems.isNotEmpty ?? false) {
@ -342,10 +372,10 @@ class SendPage extends BasePage {
} }
await sendViewModel.createTransaction(); await sendViewModel.createTransaction();
}, },
text: S.of(context).send, text: S.of(context).send,
color: Theme.of(context).accentTextTheme!.bodyLarge!.color!, color:
Theme.of(context).accentTextTheme!.bodyLarge!.color!,
textColor: Colors.white, textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState || isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting, sendViewModel.state is TransactionCommitting,
@ -355,6 +385,7 @@ class SendPage extends BasePage {
) )
], ],
)), )),
),
); );
} }
@ -389,10 +420,12 @@ class SendPage extends BasePage {
amount: S.of(context).send_amount, amount: S.of(context).send_amount,
amountValue: amountValue:
sendViewModel.pendingTransaction!.amountFormatted, sendViewModel.pendingTransaction!.amountFormatted,
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted, fiatAmountValue:
sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(context).send_fee, fee: S.of(context).send_fee,
feeValue: sendViewModel.pendingTransaction!.feeFormatted, feeValue: sendViewModel.pendingTransaction!.feeFormatted,
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted, feeFiatAmount: sendViewModel
.pendingTransactionFeeFiatAmountFormatted,
outputs: sendViewModel.outputs, outputs: sendViewModel.outputs,
rightButtonText: S.of(context).ok, rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel, leftButtonText: S.of(context).cancel,
@ -413,7 +446,8 @@ class SendPage extends BasePage {
return AlertWithOneAction( return AlertWithOneAction(
alertTitle: '', alertTitle: '',
alertContent: S.of(context).send_success( alertContent: S.of(context).send_success(
sendViewModel.selectedCryptoCurrency.toString()), sendViewModel.selectedCryptoCurrency
.toString()),
buttonText: S.of(context).ok, buttonText: S.of(context).ok,
buttonAction: () { buttonAction: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -466,10 +500,12 @@ class SendPage extends BasePage {
builder: (_) => Picker( builder: (_) => Picker(
items: sendViewModel.currencies, items: sendViewModel.currencies,
displayItem: (Object item) => item.toString(), displayItem: (Object item) => item.toString(),
selectedAtIndex: sendViewModel.currencies.indexOf(sendViewModel.selectedCryptoCurrency), selectedAtIndex: sendViewModel.currencies
.indexOf(sendViewModel.selectedCryptoCurrency),
title: S.of(context).please_select, title: S.of(context).please_select,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (CryptoCurrency cur) => sendViewModel.selectedCryptoCurrency = cur, onItemSelected: (CryptoCurrency cur) =>
sendViewModel.selectedCryptoCurrency = cur,
), ),
context: context); context: context);
} }

View file

@ -43,26 +43,24 @@ class TotpAuthCodePageState extends State<TotpAuthCodePage> {
@override @override
void initState() { void initState() {
if(widget.totpArguments.onTotpAuthenticationFinished != null) {
_reaction ??= reaction((_) => widget.setup2FAViewModel.state, (ExecutionState state) { _reaction ??= reaction((_) => widget.setup2FAViewModel.state, (ExecutionState state) {
if (state is ExecutedSuccessfullyState) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (state is ExecutedSuccessfullyState) {
widget.totpArguments.onTotpAuthenticationFinished!(true, this); widget.totpArguments.onTotpAuthenticationFinished!(true, this);
});
} }
if (state is FailureState) { if (state is FailureState) {
print(state.error); print(state.error);
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.totpArguments.onTotpAuthenticationFinished!(false, this); widget.totpArguments.onTotpAuthenticationFinished!(false, this);
});
} }
if (state is AuthenticationBanned) { if (state is AuthenticationBanned) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.totpArguments.onTotpAuthenticationFinished!(false, this); widget.totpArguments.onTotpAuthenticationFinished!(false, this);
});
} }
}); });
});
}
super.initState(); super.initState();
} }

View file

@ -150,12 +150,26 @@ class WalletListBodyState extends State<WalletListBody> {
return wallet.isCurrent return wallet.isCurrent
? row ? row
: Slidable( : Row(children: [
key: Key('${wallet.key}'), Expanded(child: row),
startActionPane: _actionPane(wallet), GestureDetector(
endActionPane: _actionPane(wallet), onTap: () => _removeWallet(wallet),
child: row, child: Container(
); height: 40,
width: 44,
padding: EdgeInsets.only(right: 20),
child: Center(
child: Image.asset('assets/images/trash.png',
height: 16,
width: 16,
color: Theme.of(context)
.primaryTextTheme
.titleLarge!
.color),
),
),
)
]);
}), }),
), ),
), ),
@ -280,18 +294,4 @@ class WalletListBodyState extends State<WalletListBody> {
_progressBar = null; _progressBar = null;
}); });
} }
ActionPane _actionPane(WalletListItem wallet) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
SlidableAction(
onPressed: (_) => _removeWallet(wallet),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: CupertinoIcons.delete,
label: S.of(context).delete,
),
],
);
} }

View file

@ -0,0 +1,17 @@
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart';
part 'market_place_view_model.g.dart';
class MarketPlaceViewModel = MarketPlaceViewModelBase with _$MarketPlaceViewModel;
abstract class MarketPlaceViewModelBase with Store {
final IoniaService _ioniaService;
MarketPlaceViewModelBase(this._ioniaService);
Future<bool> isIoniaUserAuthenticated() async {
return await _ioniaService.isLogined();
}
}

View file

@ -13,7 +13,6 @@ import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cw_core/keyable.dart'; import 'package:cw_core/keyable.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
class TransactionListItem extends ActionListItem with Keyable { class TransactionListItem extends ActionListItem with Keyable {
TransactionListItem( TransactionListItem(
{required this.transaction, {required this.transaction,
@ -28,7 +27,7 @@ class TransactionListItem extends ActionListItem with Keyable {
FiatCurrency get fiatCurrency => settingsStore.fiatCurrency; FiatCurrency get fiatCurrency => settingsStore.fiatCurrency;
BalanceDisplayMode get displayMode => settingsStore.balanceDisplayMode; BalanceDisplayMode get displayMode => balanceViewModel.displayMode;
@override @override
dynamic get keyIndex => transaction.id; dynamic get keyIndex => transaction.id;

View file

@ -114,6 +114,10 @@ abstract class ExchangeTradeViewModelBase with Store {
updatedTrade.createdAt = trade.createdAt; updatedTrade.createdAt = trade.createdAt;
} }
if (updatedTrade.amount.isEmpty) {
updatedTrade.amount = trade.amount;
}
trade = updatedTrade; trade = updatedTrade;
_updateItems(); _updateItems();
@ -123,7 +127,8 @@ abstract class ExchangeTradeViewModelBase with Store {
} }
void _updateItems() { void _updateItems() {
final tagFrom = tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : ''; final tagFrom =
tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : '';
final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : ''; final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : '';
items.clear(); items.clear();
items.add(ExchangeTradeItem( items.add(ExchangeTradeItem(

View file

@ -443,7 +443,9 @@ abstract class ExchangeViewModelBase with Store {
request = SideShiftRequest( request = SideShiftRequest(
depositMethod: depositCurrency, depositMethod: depositCurrency,
settleMethod: receiveCurrency, settleMethod: receiveCurrency,
depositAmount: depositAmount.replaceAll(',', '.'), depositAmount: isFixedRateMode
? receiveAmount.replaceAll(',', '.')
: depositAmount.replaceAll(',', '.'),
settleAddress: receiveAddress, settleAddress: receiveAddress,
refundAddress: depositAddress, refundAddress: depositAddress,
); );

View file

@ -16,12 +16,10 @@ abstract class IoniaGiftCardsListViewModelBase with Store {
ioniaCategories = IoniaCategory.allCategories, ioniaCategories = IoniaCategory.allCategories,
selectedIndices = ObservableList<IoniaCategory>.of([IoniaCategory.all]), selectedIndices = ObservableList<IoniaCategory>.of([IoniaCategory.all]),
scrollOffsetFromTop = 0.0, scrollOffsetFromTop = 0.0,
isLoggedIn = false,
merchantState = InitialIoniaMerchantLoadingState(), merchantState = InitialIoniaMerchantLoadingState(),
createCardState = IoniaCreateCardState(), createCardState = IoniaCreateCardState(),
searchString = '', searchString = '',
ioniaMerchantList = <IoniaMerchant>[] { ioniaMerchantList = <IoniaMerchant>[] {
_getAuthStatus().then((value) => isLoggedIn = value);
} }
final IoniaService ioniaService; final IoniaService ioniaService;
@ -45,24 +43,17 @@ abstract class IoniaGiftCardsListViewModelBase with Store {
@observable @observable
List<IoniaMerchant> ioniaMerchants; List<IoniaMerchant> ioniaMerchants;
@observable
bool isLoggedIn;
@observable @observable
List<IoniaCategory> ioniaCategories; List<IoniaCategory> ioniaCategories;
@observable @observable
ObservableList<IoniaCategory> selectedIndices; ObservableList<IoniaCategory> selectedIndices;
Future<bool> _getAuthStatus() async {
return await ioniaService.isLogined();
}
@action @action
Future<void> createCard() async { Future<void> createCard() async {
try { try {
createCardState = IoniaCreateCardLoading(); createCardState = IoniaCreateCardLoading();
final card = await ioniaService.createCard(); await ioniaService.createCard();
createCardState = IoniaCreateCardSuccess(); createCardState = IoniaCreateCardSuccess();
} catch (e) { } catch (e) {
createCardState = IoniaCreateCardFailure(error: e.toString()); createCardState = IoniaCreateCardFailure(error: e.toString());

View file

@ -5,7 +5,7 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import connectivity_macos import connectivity_plus_macos
import cw_monero import cw_monero
import device_info_plus import device_info_plus
import devicelocale import devicelocale

View file

@ -56,8 +56,8 @@ dependencies:
# password: ^1.0.0 # password: ^1.0.0
basic_utils: ^4.3.0 basic_utils: ^4.3.0
get_it: ^7.2.0 get_it: ^7.2.0
connectivity: ^3.0.3 # connectivity: ^3.0.3
# connectivity_plus: ^2.3.5 connectivity_plus: ^2.3.5
keyboard_actions: ^4.0.1 keyboard_actions: ^4.0.1
another_flushbar: ^1.12.29 another_flushbar: ^1.12.29
archive: ^3.3.0 archive: ^3.3.0

View file

@ -685,14 +685,14 @@
"arrive_in_this_address" : "${currency} ${tag}arrivera à cette adresse", "arrive_in_this_address" : "${currency} ${tag}arrivera à cette adresse",
"do_not_send": "Ne pas envoyer", "do_not_send": "Ne pas envoyer",
"error_dialog_content": "Oups, nous avons rencontré une erreur.\n\nMerci d'envoyer le rapport d'erreur à notre équipe d'assistance afin de nous permettre d'améliorer l'application.", "error_dialog_content": "Oups, nous avons rencontré une erreur.\n\nMerci d'envoyer le rapport d'erreur à notre équipe d'assistance afin de nous permettre d'améliorer l'application.",
"scan_qr_code": "Scannez le code QR", "scan_qr_code": "Scannez le QR code",
"cold_or_recover_wallet": "Ajoutez un cold wallet ou récupérez un paper wallet", "cold_or_recover_wallet": "Ajoutez un portefeuille froid (cold wallet) ou récupérez un portefeuille papier (paper wallet)",
"please_wait": "S'il vous plaît, attendez", "please_wait": "Merci de patienter",
"sweeping_wallet": "Portefeuille de balayage", "sweeping_wallet": "Portefeuille (wallet) de consolidation",
"sweeping_wallet_alert": "Cela ne devrait pas prendre longtemps. NE QUITTEZ PAS CET ÉCRAN OU LES FONDS BALAYÉS POURRAIENT ÊTRE PERDUS", "sweeping_wallet_alert": "Cette opération ne devrait pas prendre longtemps. NE QUITTEZ PAS CET ÉCRAN OU LES FONDS CONSOLIDÉS POURRAIENT ÊTRE PERDUS",
"decimal_places_error": "Trop de décimales", "decimal_places_error": "Trop de décimales",
"edit_node": "Modifier le nœud", "edit_node": "Modifier le nœud",
"frozen_balance": "Équilibre gelé", "frozen_balance": "Solde gelé",
"invoice_details": "Détails de la facture", "invoice_details": "Détails de la facture",
"donation_link_details": "Détails du lien de don", "donation_link_details": "Détails du lien de don",
"anonpay_description": "Générez ${type}. Le destinataire peut ${method} avec n'importe quelle crypto-monnaie prise en charge, et vous recevrez des fonds dans ce portefeuille (wallet).", "anonpay_description": "Générez ${type}. Le destinataire peut ${method} avec n'importe quelle crypto-monnaie prise en charge, et vous recevrez des fonds dans ce portefeuille (wallet).",
@ -709,22 +709,22 @@
"error_text_input_above_maximum_limit" : "Le montant est supérieur au maximum", "error_text_input_above_maximum_limit" : "Le montant est supérieur au maximum",
"show_market_place" :"Afficher la place de marché", "show_market_place" :"Afficher la place de marché",
"prevent_screenshots": "Empêcher les captures d'écran et l'enregistrement d'écran", "prevent_screenshots": "Empêcher les captures d'écran et l'enregistrement d'écran",
"modify_2fa": "Modifier le gâteau 2FA", "modify_2fa": "Modifier les paramètres Cake 2FA",
"disable_cake_2fa": "Désactiver le gâteau 2FA", "disable_cake_2fa": "Désactiver Cake 2FA",
"question_to_disable_2fa":"Êtes-vous sûr de vouloir désactiver Cake 2FA ? Un code 2FA ne sera plus nécessaire pour accéder au portefeuille et à certaines fonctions.", "question_to_disable_2fa":"Êtes-vous sûr de vouloir désactiver Cake 2FA ? Un code 2FA ne sera plus nécessaire pour accéder au portefeuille (wallet) et à certaines fonctions.",
"disable": "Désactiver", "disable": "Désactiver",
"setup_2fa": "Gâteau d'installation 2FA", "setup_2fa": "Paramétrer Cake 2FA",
"verify_with_2fa": "Vérifier avec Cake 2FA", "verify_with_2fa": "Vérifier avec Cake 2FA",
"totp_code": "Code TOTP", "totp_code": "Code TOTP",
"please_fill_totp": "Veuillez renseigner le code à 8 chiffres présent sur votre autre appareil", "please_fill_totp": "Veuillez renseigner le code à 8 chiffres affiché sur votre autre appareil",
"totp_2fa_success": "Succès! Cake 2FA activé pour ce portefeuille. N'oubliez pas de sauvegarder votre graine mnémonique au cas où vous perdriez l'accès au portefeuille.", "totp_2fa_success": "Succès! Cake 2FA est activé pour ce portefeuille. N'oubliez pas de sauvegarder votre phrase secrète (seed) au cas où vous perdriez l'accès au portefeuille (wallet).",
"totp_verification_success" :"Vérification réussie !", "totp_verification_success" :"Vérification réussie !",
"totp_2fa_failure": "Code incorrect. Veuillez essayer un code différent ou générer une nouvelle clé secrète. Utilisez une application 2FA compatible qui prend en charge les codes à 8 chiffres et SHA512.", "totp_2fa_failure": "Code incorrect. Veuillez essayer un code différent ou générer un nouveau secret TOTP. Utilisez une application 2FA compatible qui prend en charge les codes à 8 chiffres et SHA512.",
"enter_totp_code": "Veuillez entrer le code TOTP.", "enter_totp_code": "Veuillez entrer le code TOTP.",
"add_secret_code":"Ajouter ce code secret à un autre appareil", "add_secret_code":"Configurer un autre appareil à l'aide de ce secret TOTP",
"totp_secret_code":"Code secret TOTP", "totp_secret_code":"Secret TOTP",
"important_note": "Note importante", "important_note": "Note importante",
"setup_2fa_text": "Cake 2FA n'est PAS aussi sûr que le stockage à froid. 2FA protège contre les types d'attaques de base, comme votre ami fournissant votre empreinte digitale pendant que vous dormez.\n\n Cake 2FA ne protège PAS contre un appareil compromis par un attaquant sophistiqué.\n\n Si vous perdez l'accès à vos codes 2FA , VOUS PERDREZ L'ACCÈS À CE PORTEFEUILLE. Vous devrez restaurer votre portefeuille à partir de graines mnémotechniques. VOUS DEVEZ DONC SAUVEGARDER VOS SEMENCES MNEMONIQUES ! De plus, quelqu'un ayant accès à vos graines mnémoniques pourra voler vos fonds, en contournant Cake 2FA.\n\n Le personnel d'assistance de Cake ne pourra pas vous aider si vous perdez l'accès à vos graines mnémoniques, puisque Cake est un portefeuille non dépositaire.", "setup_2fa_text": "Cake 2FA (Authentification à 2 Facteurs) n'est PAS aussi sûr que le stockage à froid. Cake 2FA protège contre les attaques basiques, comme un ami fournissant votre empreinte digitale pendant que vous dormez.\n\n Cake 2FA ne protège PAS contre un appareil compromis par un attaquant sophistiqué.\n\n Si vous perdez l'accès à vos codes 2FA , VOUS PERDREZ L'ACCÈS À CE PORTEFEUILLE (WALLET). Vous devrez restaurer votre portefeuille à partir de sa phrase secrète (seed). VOUS DEVEZ DONC SAUVEGARDER VOTRE PHRASE SECRÈTE ! De plus, quelqu'un ayant accès à votre phrase secrète pourra voler vos fonds, en contournant Cake 2FA.\n\n Le personnel d'assistance de Cake ne pourra pas vous aider si vous perdez l'accès à votre phrase secrète, puisque Cake est un portefeuille non dépositaire (non custodial).",
"setup_totp_recommended": "Configurer TOTP (recommandé)", "setup_totp_recommended": "Configurer TOTP (recommandé)",
"disable_buy": "Désactiver l'action d'achat", "disable_buy": "Désactiver l'action d'achat",
"disable_sell": "Désactiver l'action de vente" "disable_sell": "Désactiver l'action de vente"

View file

@ -20,7 +20,6 @@ class SecretKey {
SecretKey('moonPayApiKey', () => ''), SecretKey('moonPayApiKey', () => ''),
SecretKey('moonPaySecretKey', () => ''), SecretKey('moonPaySecretKey', () => ''),
SecretKey('sideShiftAffiliateId', () => ''), SecretKey('sideShiftAffiliateId', () => ''),
SecretKey('sideShiftApiKey', () => ''),
SecretKey('simpleSwapApiKey', () => ''), SecretKey('simpleSwapApiKey', () => ''),
SecretKey('simpleSwapApiKeyDesktop', () => ''), SecretKey('simpleSwapApiKeyDesktop', () => ''),
SecretKey('anypayToken', () => ''), SecretKey('anypayToken', () => ''),