Merge pull request #59 from cake-tech/CWA-212-update-exchange-screen

Cwa 212 update exchange screen
This commit is contained in:
Oleksandr Sobol 2020-05-15 21:17:07 +03:00 committed by GitHub
commit 9bace0cd3c
35 changed files with 1662 additions and 739 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 895 B

After

Width:  |  Height:  |  Size: 752 B

View file

@ -237,6 +237,7 @@ class S implements WidgetsLocalizations {
String get trade_details_state => "State";
String get trade_details_title => "Trade Details";
String get trade_history_title => "Trade history";
String get trade_id => "Trade ID:";
String get trade_not_created => "Trade not created.";
String get trade_not_found => "Trade not found.";
String get trade_state_btc_sent => "Btc sent";
@ -313,7 +314,6 @@ class S implements WidgetsLocalizations {
String time(String minutes, String seconds) => "${minutes}m ${seconds}s";
String trade_details_copied(String title) => "${title} copied to Clipboard";
String trade_for_not_created(String title) => "Trade for ${title} is not created.";
String trade_id(String id) => "Trade ID:\n${id}";
String trade_id_not_found(String tradeId, String title) => "Trade ${tradeId} of ${title} not found.";
String trade_is_powered_by(String provider) => "This trade is powered by ${provider}";
String transaction_details_copied(String title) => "${title} copied to Clipboard";
@ -357,6 +357,8 @@ class $de extends S {
@override
String get you_will_get => "Sie erhalten";
@override
String get trade_id => "Handel-ID:";
@override
String get restore_description_from_seed => "Stellen Sie Ihr Wallet aus den 25 Wörtern wieder her oder 13-Wort-Kombinationscode";
@override
String get trade_state_underpaid => "Unterbezahlt";
@ -879,8 +881,6 @@ class $de extends S {
@override
String change_current_node(String node) => "Möchten Sie den aktuellen Knoten wirklich auf ändern? ${node}?";
@override
String trade_id(String id) => "Handel-ID:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Wird geladen ${wallet_name} Wallet";
@override
String version(String currentVersion) => "Ausführung ${currentVersion}";
@ -967,6 +967,8 @@ class $hi extends S {
@override
String get you_will_get => "आपको मिल जायेगा";
@override
String get trade_id => "व्यापार ID:";
@override
String get restore_description_from_seed => "या तो 25 शब्द से अपने वॉलेट को पुनर्स्थापित करें या 13 शब्द संयोजन कोड";
@override
String get trade_state_underpaid => "के तहत भुगतान किया";
@ -1489,8 +1491,6 @@ class $hi extends S {
@override
String change_current_node(String node) => "क्या आप वर्तमान नोड को बदलना सुनिश्चित करते हैं ${node}?";
@override
String trade_id(String id) => "व्यापार ID:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "लोड हो रहा है ${wallet_name} बटुआ";
@override
String version(String currentVersion) => "संस्करण ${currentVersion}";
@ -1577,6 +1577,8 @@ class $ru extends S {
@override
String get you_will_get => "Вы получите";
@override
String get trade_id => "ID сделки:";
@override
String get restore_description_from_seed => "Вы можете восстановить кошелёк используя 25-ти значную мнемоническую фразу";
@override
String get trade_state_underpaid => "Недоплаченная";
@ -2099,8 +2101,6 @@ class $ru extends S {
@override
String change_current_node(String node) => "Вы уверены, что хотите изменить текущую ноду на ${node}?";
@override
String trade_id(String id) => "ID сделки:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Загрузка ${wallet_name} кошелька";
@override
String version(String currentVersion) => "Версия ${currentVersion}";
@ -2187,6 +2187,8 @@ class $ko extends S {
@override
String get you_will_get => "당신은 얻을 것이다";
@override
String get trade_id => "무역 ID:";
@override
String get restore_description_from_seed => "25 단어 또는 13 단어 조합 코드에서 지갑을 복원하십시오.";
@override
String get trade_state_underpaid => "미지급";
@ -2709,8 +2711,6 @@ class $ko extends S {
@override
String change_current_node(String node) => "현재 노드를 다음으로 변경 하시겠습니까 ${node}?";
@override
String trade_id(String id) => "무역 ID:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "로딩 ${wallet_name} 지갑";
@override
String version(String currentVersion) => "버전 ${currentVersion}";
@ -2797,6 +2797,8 @@ class $pt extends S {
@override
String get you_will_get => "Você receberá";
@override
String get trade_id => "ID da troca:";
@override
String get restore_description_from_seed => "Restaure sua carteira a partir de semente com 25 palavras ou 13 palavras";
@override
String get trade_state_underpaid => "Parcialmente paga";
@ -3319,8 +3321,6 @@ class $pt extends S {
@override
String change_current_node(String node) => "Você realmente deseja alterar o nó atual para ${node}?";
@override
String trade_id(String id) => "ID da troca:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Abrindo a carteira ${wallet_name}";
@override
String version(String currentVersion) => "Versão ${currentVersion}";
@ -3407,6 +3407,8 @@ class $uk extends S {
@override
String get you_will_get => "Ви отримаєте";
@override
String get trade_id => "ID операції:";
@override
String get restore_description_from_seed => "Ви можете відновити гаманець використовуючи 25-ти слівну мнемонічну фразу";
@override
String get trade_state_underpaid => "Недоплачена";
@ -3929,8 +3931,6 @@ class $uk extends S {
@override
String change_current_node(String node) => "Ви впевнені, що хочете змінити поточний вузол на ${node}?";
@override
String trade_id(String id) => "ID операції:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Завантаження ${wallet_name} гаманця";
@override
String version(String currentVersion) => "Версія ${currentVersion}";
@ -4017,6 +4017,8 @@ class $ja extends S {
@override
String get you_will_get => "あなたが取得します";
@override
String get trade_id => "取引ID:";
@override
String get restore_description_from_seed => "25ワードからウォレットを復元しますまたは13ワードの組み合わせコード";
@override
String get trade_state_underpaid => "支払不足";
@ -4539,8 +4541,6 @@ class $ja extends S {
@override
String change_current_node(String node) => "現在のノードを変更してよろしいですか ${node}?";
@override
String trade_id(String id) => "取引ID:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "読み込み中 ${wallet_name} 財布";
@override
String version(String currentVersion) => "バージョン ${currentVersion}";
@ -4631,6 +4631,8 @@ class $pl extends S {
@override
String get you_will_get => "Dostaniesz";
@override
String get trade_id => "Identyfikator handlu:";
@override
String get restore_description_from_seed => "Przywróć swój portfel z 25 słów lub 13-słowny kod kombinacji";
@override
String get trade_state_underpaid => "Niedopłacone";
@ -5153,8 +5155,6 @@ class $pl extends S {
@override
String change_current_node(String node) => "Czy na pewno chcesz przywrócić ustawienia domyślne? ${node}?";
@override
String trade_id(String id) => "Identyfikator handlu:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Ładuję ${wallet_name} portfel";
@override
String version(String currentVersion) => "Wersja ${currentVersion}";
@ -5241,6 +5241,8 @@ class $es extends S {
@override
String get you_will_get => "Conseguirás";
@override
String get trade_id => "Comercial ID:";
@override
String get restore_description_from_seed => "Restaure su billetera desde el código de combinación de 25 palabras i de 13 palabras";
@override
String get trade_state_underpaid => "Poco pagado";
@ -5763,8 +5765,6 @@ class $es extends S {
@override
String change_current_node(String node) => "¿Está seguro de cambiar el nodo actual a ${node}?";
@override
String trade_id(String id) => "Comercial ID:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Billetera ${wallet_name} de carga";
@override
String version(String currentVersion) => "Versión ${currentVersion}";
@ -5851,6 +5851,8 @@ class $nl extends S {
@override
String get you_will_get => "Je zult krijgen";
@override
String get trade_id => "Trade ID:";
@override
String get restore_description_from_seed => "Herstel uw portemonnee van het 25 woord of 13 woord combinatiecode";
@override
String get trade_state_underpaid => "Slecht betaald";
@ -6373,8 +6375,6 @@ class $nl extends S {
@override
String change_current_node(String node) => "Weet u zeker dat u het huidige knooppunt wilt wijzigen in ${node}?";
@override
String trade_id(String id) => "Trade ID:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "Bezig met laden ${wallet_name} portemonnee";
@override
String version(String currentVersion) => "Versie ${currentVersion}";
@ -6461,6 +6461,8 @@ class $zh extends S {
@override
String get you_will_get => "你会得到";
@override
String get trade_id => "贸易编号:";
@override
String get restore_description_from_seed => "从25个字中恢复您的钱包或13个字的组合码";
@override
String get trade_state_underpaid => "支付不足";
@ -6983,8 +6985,6 @@ class $zh extends S {
@override
String change_current_node(String node) => "您确定将当前节点更改为 ${node}?";
@override
String trade_id(String id) => "贸易编号:\n${id}";
@override
String wallet_list_loading_wallet(String wallet_name) => "载入中 ${wallet_name} 钱包";
@override
String version(String currentVersion) => "${currentVersion}";

View file

@ -22,6 +22,7 @@ import 'package:cake_wallet/src/stores/balance/balance_store.dart';
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/screens/root/root.dart';
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
@ -34,6 +35,7 @@ import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/src/domain/common/template.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/language.dart';
@ -51,6 +53,7 @@ void main() async {
Hive.registerAdapter(WalletInfoAdapter(), 4);
Hive.registerAdapter(WalletTypeAdapter(), 5);
Hive.registerAdapter(TemplateAdapter(), 6);
Hive.registerAdapter(ExchangeTemplateAdapter(), 7);
final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey = await getEncryptionKey(
@ -69,6 +72,7 @@ void main() async {
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates = await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final sharedPreferences = await SharedPreferences.getInstance();
final walletService = WalletService();
@ -106,6 +110,7 @@ void main() async {
sharedPreferences: sharedPreferences, walletsService: walletListService);
final seedLanguageStore = SeedLanguageStore();
final sendTemplateStore = SendTemplateStore(templateSource: templates);
final exchangeTemplateStore = ExchangeTemplateStore(templateSource: exchangeTemplates);
setReactions(
settingsStore: settingsStore,
@ -133,6 +138,7 @@ void main() async {
Provider(create: (_) => trades),
Provider(create: (_) => seedLanguageStore),
Provider(create: (_) => sendTemplateStore),
Provider(create: (_) => exchangeTemplateStore),
], child: CakeWalletApp()));
}

View file

@ -88,6 +88,7 @@ import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
class Router {
static Route<dynamic> generateRoute(
@ -470,6 +471,25 @@ class Router {
}),
], child: ExchangePage()));
case Routes.exchangeTemplate:
return MaterialPageRoute<void>(
builder: (_) => Provider(create: (_) {
final xmrtoprovider = XMRTOExchangeProvider();
return ExchangeStore(
initialProvider: xmrtoprovider,
initialDepositCurrency: CryptoCurrency.xmr,
initialReceiveCurrency: CryptoCurrency.btc,
trades: trades,
providerList: [
xmrtoprovider,
ChangeNowExchangeProvider(),
MorphTokenExchangeProvider(trades: trades)
],
walletStore: walletStore);
}, child: ExchangeTemplatePage(),)
);
case Routes.settings:
return MaterialPageRoute<void>(
builder: (_) => Provider(

View file

@ -45,4 +45,5 @@ class Routes {
static const changeLanguage = '/change_language';
static const newWalletType = '/new_wallet_type';
static const sendTemplate = '/send_template';
static const exchangeTemplate = '/exchange_template';
}

View file

@ -0,0 +1,35 @@
import 'package:hive/hive.dart';
part 'exchange_template.g.dart';
@HiveType()
class ExchangeTemplate extends HiveObject {
ExchangeTemplate({
this.amount,
this.depositCurrency,
this.receiveCurrency,
this.provider,
this.depositAddress,
this.receiveAddress
});
static const boxName = 'ExchangeTemplate';
@HiveField(0)
String amount;
@HiveField(1)
String depositCurrency;
@HiveField(2)
String receiveCurrency;
@HiveField(3)
String provider;
@HiveField(4)
String depositAddress;
@HiveField(5)
String receiveAddress;
}

View file

@ -1,4 +1,9 @@
import 'dart:ui';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
@ -8,7 +13,6 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
@ -17,18 +21,21 @@ import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/top_panel.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/provider_picker.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class ExchangePage extends BasePage {
@override
String get title => S.current.exchange;
@override
bool get isModalBackButton => true;
Color get backgroundColor => PaletteDark.walletCardSubAddressField;
final Image arrowBottomPurple =
Image.asset('assets/images/arrow_bottom_purple_icon.png', height: 8);
final Image arrowBottom =
Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
@override
Widget middle(BuildContext context) {
@ -38,31 +45,34 @@ class ExchangePage extends BasePage {
onPressed: () => _presentProviderPicker(context),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(S.of(context).exchange,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryTextTheme.title.color)),
SizedBox(width: 5),
arrowBottomPurple
]),
Observer(
builder: (_) => Text('${exchangeStore.provider.title}',
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(S.of(context).exchange,
style: TextStyle(
fontSize: 10.0,
fontSize: 16.0,
fontWeight: FontWeight.w400,
color:
Theme.of(context).primaryTextTheme.headline.color)))
color: Colors.white)),
Observer(
builder: (_) => Text('${exchangeStore.provider.title}',
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w400,
color:PaletteDark.walletCardText)))
],
),
SizedBox(width: 5),
Padding(
padding: EdgeInsets.only(top: 8),
child: arrowBottom,
)
],
),
)
);
}
@ -72,14 +82,16 @@ class ExchangePage extends BasePage {
return ButtonTheme(
minWidth: double.minPositive,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: FlatButton(
padding: EdgeInsets.all(0),
child: Text(
S.of(context).clear,
style: TextStyle(
color: Theme.of(context).accentTextTheme.caption.color,
color: PaletteDark.walletCardText,
fontWeight: FontWeight.w500,
fontSize: 16),
fontSize: 14),
),
onPressed: () => exchangeStore.reset()),
);
@ -92,10 +104,26 @@ class ExchangePage extends BasePage {
final exchangeStore = Provider.of<ExchangeStore>(context);
final items = exchangeStore.providersForCurrentPair();
final selectedItem = items.indexOf(exchangeStore.provider);
final images = List<Image>();
for (ExchangeProvider provider in items) {
switch (provider.description) {
case ExchangeProviderDescription.xmrto:
images.add(Image.asset('assets/images/xmr_btc.png'));
break;
case ExchangeProviderDescription.changeNow:
images.add(Image.asset('assets/images/change_now.png'));
break;
case ExchangeProviderDescription.morphToken:
images.add(Image.asset('assets/images/morph_icon.png'));
break;
}
}
showDialog<void>(
builder: (_) => Picker(
builder: (_) => ProviderPicker(
items: items,
images: images,
selectedAtIndex: selectedItem,
title: S.of(context).change_exchange_provider,
onItemSelected: (ExchangeProvider provider) =>
@ -117,10 +145,12 @@ class ExchangeFormState extends State<ExchangeForm> {
final Image arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white,
height: 8,
);
final Image arrowBottomCakeGreen = Image.asset(
'assets/images/arrow_bottom_cake_green.png',
color: Colors.white,
height: 8,
);
@ -128,6 +158,7 @@ class ExchangeFormState extends State<ExchangeForm> {
Widget build(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
final depositWalletName =
exchangeStore.depositCurrency == CryptoCurrency.xmr
@ -141,156 +172,229 @@ class ExchangeFormState extends State<ExchangeForm> {
WidgetsBinding.instance.addPostFrameCallback(
(_) => _setReactions(context, exchangeStore, walletStore));
return Form(
key: _formKey,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 20, right: 20),
content: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
return Container(
color: PaletteDark.historyPanel,
child: Form(
key: _formKey,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
children: <Widget>[
TopPanel(
color: PaletteDark.menuList,
edgeInsets: EdgeInsets.only(bottom: 24),
widget: Column(
children: <Widget>[
TopPanel(
color: PaletteDark.walletCardSubAddressField,
widget: Observer(
builder: (_) => ExchangeCard(
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency: exchangeStore.depositCurrency,
initialWalletName: depositWalletName,
initialAddress:
exchangeStore.depositCurrency == walletStore.type
? walletStore.address
: exchangeStore.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable: true,
isAmountEstimated: false,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeStore.changeDepositCurrency(currency: currency),
imageArrow: arrowBottomPurple,
currencyButtonColor: PaletteDark.walletCardSubAddressField,
addressButtonsColor: PaletteDark.menuList,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.depositCurrency);
return exchangeStore.errorMessage;
},
),
)
),
Padding(
padding: EdgeInsets.only(top: 32, left: 24, right: 24),
child: Observer(
builder: (_) => ExchangeCard(
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency: exchangeStore.receiveCurrency,
initialWalletName: receiveWalletName,
initialAddress:
exchangeStore.receiveCurrency == walletStore.type
? walletStore.address
: exchangeStore.receiveAddress,
initialIsAmountEditable: false,
initialIsAddressEditable: true,
isAmountEstimated: true,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) => exchangeStore
.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyButtonColor: PaletteDark.menuList,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.receiveCurrency);
return exchangeStore.errorMessage;
},
)),
)
],
)
),
Padding(
padding: EdgeInsets.only(top: 10, bottom: 20),
child: Text(
S.of(context).you_will_send,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
height: 1.1,
color: Theme.of(context).primaryTextTheme.title.color),
padding: EdgeInsets.only(
top: 32,
left: 24,
bottom: 24
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).send_templates,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: PaletteDark.walletCardText
),
)
],
),
),
ExchangeCard(
key: depositKey,
initialCurrency: exchangeStore.depositCurrency,
initialWalletName: depositWalletName,
initialAddress:
exchangeStore.depositCurrency == walletStore.type
? walletStore.address
: null,
initialIsAmountEditable: true,
initialIsAddressEditable: true,
isAmountEstimated: false,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeStore.changeDepositCurrency(currency: currency),
imageArrow: arrowBottomPurple,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.depositCurrency);
return exchangeStore.errorMessage;
},
),
SizedBox(height: 35),
Padding(
padding: EdgeInsets.only(bottom: 20),
child: Text(
S.of(context).you_will_get,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
height: 1.1,
color:
Theme.of(context).primaryTextTheme.title.color),
)),
Observer(
builder: (_) => ExchangeCard(
key: receiveKey,
initialCurrency: exchangeStore.receiveCurrency,
initialWalletName: receiveWalletName,
initialAddress:
exchangeStore.receiveCurrency == walletStore.type
? walletStore.address
: null,
initialIsAmountEditable: false,
initialIsAddressEditable: true,
isAmountEstimated: true,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) => exchangeStore
.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.receiveCurrency);
return exchangeStore.errorMessage;
},
)),
Container(
height: 40,
width: double.infinity,
padding: EdgeInsets.only(left: 24),
child: Observer(
builder: (_) {
final itemCount = exchangeTemplateStore.templates.length + 1;
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: itemCount,
itemBuilder: (context, index) {
if (index == 0) {
return GestureDetector(
onTap: () => Navigator.of(context)
.pushNamed(Routes.exchangeTemplate),
child: Container(
padding: EdgeInsets.only(right: 10),
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [8, 4],
color: PaletteDark.menuList,
strokeWidth: 2,
radius: Radius.circular(20),
child: Container(
height: 40,
width: 75,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.transparent,
),
child: Text(
S.of(context).send_new,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: PaletteDark.walletCardText
),
),
)
),
),
);
}
index -= 1;
final template = exchangeTemplateStore.templates[index];
return TemplateTile(
amount: template.amount,
from: template.depositCurrency,
to: template.receiveCurrency,
onTap: () {
applyTemplate(exchangeStore, template);
}
);
}
);
}
),
)
],
),
),
bottomSectionPadding: EdgeInsets.only(top: 35, left: 20, right: 20),
bottomSection: Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 15),
child: Observer(builder: (_) {
final description =
exchangeStore.provider is XMRTOExchangeProvider
? S.of(context).amount_is_guaranteed
: S.of(context).amount_is_estimate;
return Center(
child: Text(
description,
style: TextStyle(color: Palette.blueGrey, fontSize: 12),
),
);
}),
),
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).exchange,
onPressed: () {
if (_formKey.currentState.validate()) {
exchangeStore.createTrade();
}
},
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.button.color,
isLoading: exchangeStore.tradeState is TradeIsCreating,
)),
Observer(builder: (_) {
final title = exchangeStore.provider.description.title;
var imageSrc = '';
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 15),
child: Observer(builder: (_) {
final description =
exchangeStore.provider is XMRTOExchangeProvider
? S.of(context).amount_is_guaranteed
: S.of(context).amount_is_estimate;
return Center(
child: Text(
description,
style: TextStyle(
color: PaletteDark.walletCardText,
fontSize: 12
),
),
);
}),
),
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).exchange,
onPressed: () {
if (_formKey.currentState.validate()) {
exchangeStore.createTrade();
}
},
color: Colors.blue,
textColor: Colors.white,
isLoading: exchangeStore.tradeState is TradeIsCreating,
)),
]),
)),
);
}
switch (exchangeStore.provider.description) {
case ExchangeProviderDescription.xmrto:
imageSrc = 'assets/images/xmr_btc.png';
break;
case ExchangeProviderDescription.changeNow:
imageSrc = 'assets/images/change_now.png';
break;
case ExchangeProviderDescription.morphToken:
imageSrc = 'assets/images/morph_icon.png';
break;
}
void applyTemplate(ExchangeStore store, ExchangeTemplate template) {
store.changeDepositCurrency(currency: CryptoCurrency.fromString(template.depositCurrency));
store.changeReceiveCurrency(currency: CryptoCurrency.fromString(template.receiveCurrency));
return Padding(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(imageSrc),
SizedBox(width: 10),
Text(
S.of(context).powered_by(title),
style: TextStyle(fontSize: 14, color: Palette.powered),
)
],
),
);
})
]),
));
switch (template.provider) {
case 'XMR.TO':
store.changeProvider(provider: store.providerList[0]);
break;
case 'ChangeNOW':
store.changeProvider(provider: store.providerList[1]);
break;
case 'MorphToken':
store.changeProvider(provider: store.providerList[2]);
break;
}
store.changeDepositAmount(amount: template.amount);
store.depositAddress = template.depositAddress;
store.receiveAddress = template.receiveAddress;
}
void _setReactions(
@ -345,6 +449,12 @@ class ExchangeFormState extends State<ExchangeForm> {
}
});
reaction((_) => store.depositAddress, (String address) {
if (depositKey.currentState.addressController.text != address) {
depositKey.currentState.addressController.text = address;
}
});
reaction((_) => store.receiveAmount, (String amount) {
if (receiveKey.currentState.amountController.text !=
store.receiveAmount) {
@ -352,6 +462,12 @@ class ExchangeFormState extends State<ExchangeForm> {
}
});
reaction((_) => store.receiveAddress, (String address) {
if (receiveKey.currentState.addressController.text != address) {
receiveKey.currentState.addressController.text = address;
}
});
reaction((_) => store.provider, (ExchangeProvider provider) {
receiveKey.currentState.isAddressEditable(isEditable: true);
receiveKey.currentState.isAmountEditable(isEditable: false);
@ -367,14 +483,11 @@ class ExchangeFormState extends State<ExchangeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).error),
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});

View file

@ -0,0 +1,448 @@
import 'dart:ui';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/top_panel.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/provider_picker.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
class ExchangeTemplatePage extends BasePage {
@override
String get title => 'New template';
@override
Color get backgroundColor => PaletteDark.walletCardSubAddressField;
final Image arrowBottom =
Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
@override
Widget trailing(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
return FlatButton(
onPressed: () => _presentProviderPicker(context),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(S.of(context).exchange,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: Colors.white)),
Observer(
builder: (_) => Text('${exchangeStore.provider.title}',
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w400,
color:PaletteDark.walletCardText)))
],
),
SizedBox(width: 5),
Padding(
padding: EdgeInsets.only(top: 8),
child: arrowBottom,
)
],
)
);
}
void _presentProviderPicker(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
final items = exchangeStore.providersForCurrentPair();
final selectedItem = items.indexOf(exchangeStore.provider);
final images = List<Image>();
for (ExchangeProvider provider in items) {
switch (provider.description) {
case ExchangeProviderDescription.xmrto:
images.add(Image.asset('assets/images/xmr_btc.png'));
break;
case ExchangeProviderDescription.changeNow:
images.add(Image.asset('assets/images/change_now.png'));
break;
case ExchangeProviderDescription.morphToken:
images.add(Image.asset('assets/images/morph_icon.png'));
break;
}
}
showDialog<void>(
builder: (_) => ProviderPicker(
items: items,
images: images,
selectedAtIndex: selectedItem,
title: S.of(context).change_exchange_provider,
onItemSelected: (ExchangeProvider provider) =>
exchangeStore.changeProvider(provider: provider)),
context: context);
}
@override
Widget body(BuildContext context) => ExchangeTemplateForm();
}
class ExchangeTemplateForm extends StatefulWidget{
@override
ExchangeTemplateFormState createState() => ExchangeTemplateFormState();
}
class ExchangeTemplateFormState extends State<ExchangeTemplateForm> {
final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>();
final _formKey = GlobalKey<FormState>();
var _isReactionsSet = false;
final Image arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white,
height: 8,
);
final Image arrowBottomCakeGreen = Image.asset(
'assets/images/arrow_bottom_cake_green.png',
color: Colors.white,
height: 8,
);
@override
Widget build(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
final depositWalletName =
exchangeStore.depositCurrency == CryptoCurrency.xmr
? walletStore.name
: null;
final receiveWalletName =
exchangeStore.receiveCurrency == CryptoCurrency.xmr
? walletStore.name
: null;
WidgetsBinding.instance.addPostFrameCallback(
(_) => _setReactions(context, exchangeStore, walletStore));
return Container(
color: PaletteDark.historyPanel,
child: Form(
key: _formKey,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
children: <Widget>[
TopPanel(
color: PaletteDark.menuList,
edgeInsets: EdgeInsets.only(bottom: 24),
widget: Column(
children: <Widget>[
TopPanel(
color: PaletteDark.walletCardSubAddressField,
widget: Observer(
builder: (_) => ExchangeCard(
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency: exchangeStore.depositCurrency,
initialWalletName: depositWalletName,
initialAddress:
exchangeStore.depositCurrency == walletStore.type
? walletStore.address
: null,
initialIsAmountEditable: true,
initialIsAddressEditable: true,
isAmountEstimated: false,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeStore.changeDepositCurrency(currency: currency),
imageArrow: arrowBottomPurple,
currencyButtonColor: PaletteDark.walletCardSubAddressField,
addressButtonsColor: PaletteDark.menuList,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.depositCurrency);
return exchangeStore.errorMessage;
},
),
)
),
Padding(
padding: EdgeInsets.only(top: 32, left: 24, right: 24),
child: Observer(
builder: (_) => ExchangeCard(
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency: exchangeStore.receiveCurrency,
initialWalletName: receiveWalletName,
initialAddress:
exchangeStore.receiveCurrency == walletStore.type
? walletStore.address
: null,
initialIsAmountEditable: false,
initialIsAddressEditable: true,
isAmountEstimated: true,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) => exchangeStore
.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyButtonColor: PaletteDark.menuList,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.receiveCurrency);
return exchangeStore.errorMessage;
},
)),
)
],
)
),
],
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 15),
child: Observer(builder: (_) {
final description =
exchangeStore.provider is XMRTOExchangeProvider
? S.of(context).amount_is_guaranteed
: S.of(context).amount_is_estimate;
return Center(
child: Text(
description,
style: TextStyle(
color: PaletteDark.walletCardText,
fontSize: 12
),
),
);
}),
),
PrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
exchangeTemplateStore.addTemplate(
amount: exchangeStore.depositAmount,
depositCurrency: exchangeStore.depositCurrency.toString(),
receiveCurrency: exchangeStore.receiveCurrency.toString(),
provider: exchangeStore.provider.toString(),
depositAddress: exchangeStore.depositAddress,
receiveAddress: exchangeStore.receiveAddress
);
exchangeTemplateStore.update();
Navigator.of(context).pop();
}
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white
),
]),
)),
);
}
void _setReactions(
BuildContext context, ExchangeStore store, WalletStore walletStore) {
if (_isReactionsSet) {
return;
}
final depositAddressController = depositKey.currentState.addressController;
final depositAmountController = depositKey.currentState.amountController;
final receiveAddressController = receiveKey.currentState.addressController;
final receiveAmountController = receiveKey.currentState.amountController;
final limitsState = store.limitsState;
if (limitsState is LimitsLoadedSuccessfully) {
final min = limitsState.limits.min != null
? limitsState.limits.min.toString()
: null;
final max = limitsState.limits.max != null
? limitsState.limits.max.toString()
: null;
final key = depositKey;
key.currentState.changeLimits(min: min, max: max);
}
_onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
_onCurrencyChange(store.depositCurrency, walletStore, depositKey);
reaction(
(_) => walletStore.name,
(String _) => _onWalletNameChange(
walletStore, store.receiveCurrency, receiveKey));
reaction(
(_) => walletStore.name,
(String _) => _onWalletNameChange(
walletStore, store.depositCurrency, depositKey));
reaction(
(_) => store.receiveCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, receiveKey));
reaction(
(_) => store.depositCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, depositKey));
reaction((_) => store.depositAmount, (String amount) {
if (depositKey.currentState.amountController.text != amount) {
depositKey.currentState.amountController.text = amount;
}
});
reaction((_) => store.receiveAmount, (String amount) {
if (receiveKey.currentState.amountController.text !=
store.receiveAmount) {
receiveKey.currentState.amountController.text = amount;
}
});
reaction((_) => store.provider, (ExchangeProvider provider) {
receiveKey.currentState.isAddressEditable(isEditable: true);
receiveKey.currentState.isAmountEditable(isEditable: false);
depositKey.currentState.isAddressEditable(isEditable: true);
depositKey.currentState.isAmountEditable(isEditable: true);
receiveKey.currentState.changeIsAmountEstimated(true);
});
reaction((_) => store.tradeState, (ExchangeTradeState state) {
if (state is TradeIsCreatedFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).error),
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
);
});
});
}
if (state is TradeIsCreatedSuccessfully) {
Navigator.of(context)
.pushNamed(Routes.exchangeConfirm, arguments: state.trade);
}
});
reaction((_) => store.limitsState, (LimitsState state) {
String min;
String max;
if (state is LimitsLoadedSuccessfully) {
min = state.limits.min != null ? state.limits.min.toString() : null;
max = state.limits.max != null ? state.limits.max.toString() : null;
}
if (state is LimitsLoadedFailure) {
min = '0';
max = '0';
}
if (state is LimitsIsLoading) {
min = '...';
max = '...';
}
depositKey.currentState.changeLimits(min: min, max: max);
receiveKey.currentState.changeLimits(min: null, max: null);
});
depositAddressController.addListener(
() => store.depositAddress = depositAddressController.text);
depositAmountController.addListener(() {
if (depositAmountController.text != store.depositAmount) {
store.changeDepositAmount(amount: depositAmountController.text);
}
});
receiveAddressController.addListener(
() => store.receiveAddress = receiveAddressController.text);
receiveAmountController.addListener(() {
if (receiveAmountController.text != store.receiveAmount) {
store.changeReceiveAmount(amount: receiveAmountController.text);
}
});
reaction((_) => walletStore.address, (String address) {
if (store.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address);
}
if (store.receiveCurrency == CryptoCurrency.xmr) {
receiveKey.currentState.changeAddress(address: address);
}
});
_isReactionsSet = true;
}
void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == walletStore.type;
key.currentState.changeSelectedCurrency(currency);
key.currentState
.changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
key.currentState
.changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
key.currentState.changeAmount(amount: '');
}
void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == walletStore.type;
if (isCurrentTypeWallet) {
key.currentState.changeWalletName(walletStore.name);
key.currentState.addressController.text = walletStore.address;
} else if (key.currentState.addressController.text == walletStore.address) {
key.currentState.changeWalletName(null);
key.currentState.addressController.text = null;
}
}
}

View file

@ -0,0 +1,119 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
class CurrencyPicker extends StatelessWidget {
CurrencyPicker({
@required this.selectedAtIndex,
@required this.items,
@required this.title,
@required this.onItemSelected,
});
final int selectedAtIndex;
final List<CryptoCurrency> items;
final String title;
final Function(CryptoCurrency) onItemSelected;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.historyPanel.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 400,
width: 300,
color: PaletteDark.walletCardSubAddressField,
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: 1.25,
physics: const NeverScrollableScrollPhysics(),
crossAxisSpacing: 1,
mainAxisSpacing: 1,
children: List.generate(15, (index) {
if (index == 14) {
return Container(
color: PaletteDark.menuList,
);
}
final item = items[index];
final isItemSelected = index == selectedAtIndex;
final color = isItemSelected
? PaletteDark.historyPanel
: PaletteDark.menuList;
final textColor = isItemSelected
? Colors.blue
: Colors.white;
return GestureDetector(
onTap: () {
if (onItemSelected == null) {
return;
}
Navigator.of(context).pop();
onItemSelected(item);
},
child: Container(
color: color,
child: Center(
child: Text(
item.toString(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: textColor
),
),
),
),
);
})
),
),
),
),
)
],
),
)
),
),
),
);
}
}

View file

@ -3,12 +3,14 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
class ExchangeCard extends StatefulWidget {
ExchangeCard(
{Key key,
this.title = '',
this.initialCurrency,
this.initialAddress,
this.initialWalletName,
@ -18,12 +20,15 @@ class ExchangeCard extends StatefulWidget {
this.currencies,
this.onCurrencySelected,
this.imageArrow,
this.currencyButtonColor = Colors.transparent,
this.addressButtonsColor = PaletteDark.walletCardSubAddressField,
this.currencyValueValidator,
this.addressTextFieldValidator})
: super(key: key);
final List<CryptoCurrency> currencies;
final Function(CryptoCurrency) onCurrencySelected;
final String title;
final CryptoCurrency initialCurrency;
final String initialWalletName;
final String initialAddress;
@ -31,6 +36,8 @@ class ExchangeCard extends StatefulWidget {
final bool initialIsAddressEditable;
final bool isAmountEstimated;
final Image imageArrow;
final Color currencyButtonColor;
final Color addressButtonsColor;
final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator;
@ -42,6 +49,7 @@ class ExchangeCardState extends State<ExchangeCard> {
final addressController = TextEditingController();
final amountController = TextEditingController();
String _title;
String _min;
String _max;
CryptoCurrency _selectedCurrency;
@ -52,6 +60,7 @@ class ExchangeCardState extends State<ExchangeCard> {
@override
void initState() {
_title = widget.title;
_isAmountEditable = widget.initialIsAmountEditable;
_isAddressEditable = widget.initialIsAddressEditable;
_walletName = widget.initialWalletName;
@ -103,157 +112,115 @@ class ExchangeCardState extends State<ExchangeCard> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(22, 15, 22, 30),
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Column(children: <Widget>[
_isAmountEstimated != null && _isAmountEstimated
? Row(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
SizedBox(
height: 30,
child: Container(
padding: EdgeInsets.fromLTRB(10, 5, 10, 5),
decoration: BoxDecoration(
color: Palette.lightGrey,
borderRadius: BorderRadius.circular(10.0)),
child: Text(
S.of(context).estimated,
style: TextStyle(
fontSize: 14,
color: Palette.wildDarkBlue,
fontWeight: FontWeight.bold),
),
),
),
])
: Container(),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 52,
width: 90,
color: Colors.transparent,
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
_title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: PaletteDark.walletCardText
),
)
],
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Stack(
children: <Widget>[
BaseTextFormField(
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.left,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
hintText: '0.0000',
borderColor: PaletteDark.borderCardColor,
validator: widget.currencyValueValidator
),
Positioned(
top: 8,
right: 0,
child: Container(
height: 32,
padding: EdgeInsets.only(left: 10),
color: widget.currencyButtonColor,
child: InkWell(
onTap: () => _presentPicker(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(_selectedCurrency.toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: Theme.of(context)
.primaryTextTheme
.title
.color)),
widget.imageArrow
]),
_walletName != null
? Text(_walletName,
style: TextStyle(
fontSize: 12,
color: Palette.wildDarkBlue))
: SizedBox(),
Text(
_selectedCurrency.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.white)),
Padding(
padding: EdgeInsets.only(left: 5),
child: widget.imageArrow,
)
]),
),
),
SizedBox(width: 10),
Flexible(
child: Column(
children: [
TextFormField(
style: TextStyle(fontSize: 23, height: 1.21),
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.right,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).cardTheme.color,
fontSize: 23,
height: 1.21),
hintText: '0.00000000',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: _isAmountEditable
? Palette.deepPurple
: Theme.of(context).focusColor,
width: 1.0))),
validator: widget.currencyValueValidator),
SizedBox(height: 5),
SizedBox(
height: 15,
width: double.infinity,
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
_min != null
? Text(
S.of(context).min_value(
_min, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
: SizedBox(),
_min != null ? SizedBox(width: 10) : SizedBox(),
_max != null
? Text(
S.of(context).max_value(
_max, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color))
: SizedBox(),
]),
),
),
],
),
),
)
],
)
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_min != null
? Text(
S.of(context).min_value(
_min, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: PaletteDark.walletCardText),
)
: Offstage(),
_min != null ? SizedBox(width: 10) : Offstage(),
_max != null
? Text(
S.of(context).max_value(
_max, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: PaletteDark.walletCardText))
: Offstage(),
]),
),
SizedBox(height: 10),
AddressTextField(
controller: addressController,
isActive: _isAddressEditable,
options: _isAddressEditable
? _walletName != null
? [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
AddressTextFieldOption.subaddressList
]
: [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
]
: [],
validator: widget.addressTextFieldValidator,
Padding(
padding: EdgeInsets.only(top: 10),
child: AddressTextField(
controller: addressController,
isActive: _isAddressEditable,
options: _isAddressEditable
? _walletName != null
? []
: [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
]
: [],
isBorderExist: false,
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
),
)
]),
);
@ -261,14 +228,14 @@ class ExchangeCardState extends State<ExchangeCard> {
void _presentPicker(BuildContext context) {
showDialog<void>(
builder: (_) => Picker(
items: widget.currencies,
builder: (_) => CurrencyPicker(
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
items: widget.currencies,
title: S.of(context).change_currency,
onItemSelected: (CryptoCurrency item) =>
widget.onCurrencySelected != null
? widget.onCurrencySelected(item)
: null),
widget.onCurrencySelected != null
? widget.onCurrencySelected(item)
: null),
context: context);
}
}

View file

@ -0,0 +1,124 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
class ProviderPicker extends StatelessWidget {
ProviderPicker({
@required this.selectedAtIndex,
@required this.items,
@required this.images,
@required this.title,
@required this.onItemSelected,
});
final int selectedAtIndex;
final List<ExchangeProvider> items;
final List<Image> images;
final String title;
final Function(ExchangeProvider) onItemSelected;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.historyPanel.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
),
),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 233,
color: PaletteDark.menuList,
child: ListView.separated(
separatorBuilder: (context, index) => Divider(
color: PaletteDark.mainBackgroundColor,
height: 1,
),
itemCount: items == null ? 0 : items.length,
itemBuilder: (context, index) {
final item = items[index];
final image = images[index];
final isItemSelected = index == selectedAtIndex;
final color = isItemSelected
? PaletteDark.menuHeader
: Colors.transparent;
final textColor = isItemSelected
? Colors.blue
: Colors.white;
return GestureDetector(
onTap: () {
if (onItemSelected == null) {
return;
}
Navigator.of(context).pop();
onItemSelected(item);
},
child: Container(
height: 77,
padding: EdgeInsets.only(left: 24, right: 24),
color: color,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image,
Padding(
padding: EdgeInsets.only(left: 12),
child: Text(
item.toString(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: textColor,
decoration: TextDecoration.none,
),
),
)
],
),
),
);
},
)
),
),
),
)
],
),
)
),
),
),
);
}
}

View file

@ -6,54 +6,56 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/domain/exchange/trade.dart';
import 'package:cake_wallet/palette.dart';
class ExchangeConfirmPage extends BasePage {
ExchangeConfirmPage({@required this.trade});
final Trade trade;
final copyImage = Image.asset('assets/images/copy_content.png', color: Colors.white);
@override
String get title => S.current.copy_id;
@override
Color get backgroundColor => PaletteDark.historyPanel;
@override
Widget body(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 80.0, right: 80.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
S.of(context).exchange_result_write_down_trade_id,
return Container(
color: PaletteDark.historyPanel,
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
S.of(context).exchange_result_write_down_trade_id,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w600,
color: Colors.white),
),
Padding(
padding: EdgeInsets.only(top: 60),
child: Text(
S.of(context).trade_id,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Colors.white),
),
SizedBox(
height: 70.0,
),
Text(
S.of(context).trade_id(trade.id),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
SizedBox(
height: 70.0,
),
PrimaryButton(
onPressed: () {
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Builder(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: trade.id));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
@ -62,31 +64,52 @@ class ExchangeConfirmPage extends BasePage {
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
));
},
text: S.of(context).copy_id,
color: Theme.of(context)
.accentTextTheme
.caption
.backgroundColor,
textColor: Theme.of(context)
.primaryTextTheme
.button
.color)
],
),
))),
Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 40.0),
child: PrimaryButton(
child: Container(
height: 60,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
color: PaletteDark.menuList
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text(
trade.id,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.white
),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
),
)
),
)
],
),
)),
PrimaryButton(
onPressed: () => Navigator.of(context)
.pushReplacementNamed(Routes.exchangeTrade, arguments: trade),
text: S.of(context).saved_the_trade_id,
color: Theme.of(context).primaryTextTheme.button.backgroundColor,
textColor:
Theme.of(context).primaryTextTheme.button.color),
)
],
color: Colors.green,
textColor: Colors.white)
],
),
);
}
}

View file

@ -12,17 +12,21 @@ import 'package:cake_wallet/src/stores/exchange_trade/exchange_trade_store.dart'
import 'package:cake_wallet/src/stores/send/send_store.dart';
import 'package:cake_wallet/src/stores/send/sending_state.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/exchange_trade/widgets/copy_button.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/widgets/timer_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class ExchangeTradePage extends BasePage {
@override
String get title => S.current.exchange;
@override
Color get backgroundColor => PaletteDark.historyPanel;
@override
Widget body(BuildContext context) => ExchangeTradeForm();
}
@ -46,278 +50,273 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
_setEffects(context);
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 20, right: 20, top: 20),
content: Observer(builder: (_) {
final trade = tradeStore.trade;
final walletName = walletStore.name;
return Container(
color: PaletteDark.historyPanel,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, top: 24),
content: Observer(builder: (_) {
final trade = tradeStore.trade;
final walletName = walletStore.name;
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
Text(
'${trade.id ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).amount,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
Text(
'${trade.amount ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
),
trade.extraId != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).payment_id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
Text(
'${trade.extraId ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Colors.white),
),
Text(
'${trade.id ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: PaletteDark.walletCardText),
)
: Container(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).status,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.button
.color,
height: 2),
),
Text(
'${trade.state ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
),
trade.expiredAt != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).offer_expires_in,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
TimerWidget(trade.expiredAt,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color)
],
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).amount,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Colors.white),
),
Text(
'${trade.amount ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: PaletteDark.walletCardText),
)
: Container(),
],
),
SizedBox(width: 10),
Container(
padding: EdgeInsets.all(5),
color: Colors.white,
constraints: BoxConstraints(minWidth: 65, maxWidth: 105),
child: QrImage(
data: trade.inputAddress ?? fetchingLabel,
backgroundColor: Colors.white))
],
),
),
SizedBox(
height: 20.0,
),
Center(
child: Text(
S.of(context).trade_is_powered_by(trade.provider != null
? trade.provider.title
: fetchingLabel),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.headline.color),
),
),
Container(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Center(
child: Text(
trade.inputAddress ?? fetchingLabel,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14.0, color: Palette.lightViolet),
],
),
trade.extraId != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).payment_id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Colors.white),
),
Text(
'${trade.extraId ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: PaletteDark.walletCardText),
)
],
)
: Container(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).status,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Colors.white,
height: 2),
),
Text(
'${trade.state ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: PaletteDark.walletCardText),
)
],
),
trade.expiredAt != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).offer_expires_in,
style: TextStyle(
fontSize: 14.0,
color: Colors.white),
),
TimerWidget(trade.expiredAt,
color: PaletteDark.walletCardText)
],
)
: Container(),
],
),
],
),
),
),
Container(
padding: EdgeInsets.only(left: 50.0, right: 50.0),
child: Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 5.0),
child: CopyButton(
onPressed: () => Clipboard.setData(
ClipboardData(text: trade.inputAddress)),
text: S.of(context).copy_address,
color: Theme.of(context)
.accentTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.button
.decorationColor),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 5.0),
child: CopyButton(
onPressed: () =>
Clipboard.setData(ClipboardData(text: trade.id)),
text: S.of(context).copy_id,
color: Theme.of(context)
.accentTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.button
.decorationColor),
))
],
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
children: <Widget>[
Spacer(
flex: 1,
),
Flexible(
flex: 1,
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: QrImage(
data: trade.inputAddress ?? fetchingLabel,
backgroundColor: Colors.transparent,
foregroundColor: PaletteDark.walletCardText,
),
),
)),
Spacer(
flex: 1,
)
],
),
),
),
Container(
padding: EdgeInsets.only(top: 20),
child: Text(
tradeStore.isSendable
? S.of(context).exchange_result_confirm(
trade.amount ?? fetchingLabel,
trade.from.toString(),
walletName)
: S.of(context).exchange_result_description(
trade.amount ?? fetchingLabel, trade.from.toString()),
SizedBox(
height: 20.0,
),
Center(
child: Text(
S.of(context).trade_is_powered_by(trade.provider != null
? trade.provider.title
: fetchingLabel),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
Container(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Center(
child: Text(
trade.inputAddress ?? fetchingLabel,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14.0, color: PaletteDark.walletCardText),
),
),
),
Container(
child: Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 5.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: trade.inputAddress));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
));
},
text: S.of(context).copy_address,
color: PaletteDark.menuList,
textColor: Colors.white)
),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 5.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: trade.id));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
));
},
text: S.of(context).copy_id,
color: PaletteDark.menuList,
textColor: Colors.white)
),
))
],
),
),
Container(
padding: EdgeInsets.only(top: 20),
child: Text(
tradeStore.isSendable
? S.of(context).exchange_result_confirm(
trade.amount ?? fetchingLabel,
trade.from.toString(),
walletName)
: S.of(context).exchange_result_description(
trade.amount ?? fetchingLabel, trade.from.toString()),
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 13.0,
color: Colors.white),
),
),
Text(
S.of(context).exchange_result_write_down_ID,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 13.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
Text(
S.of(context).exchange_result_write_down_ID,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 13.0,
color: Theme.of(context).accentTextTheme.title.color),
)
],
);
}),
bottomSection: Observer(
builder: (_) => tradeStore.trade.from == CryptoCurrency.xmr &&
!(sendStore.state is TransactionCommitted)
? Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: LoadingPrimaryButton(
isDisabled: tradeStore.trade.inputAddress == null ||
tradeStore.trade.inputAddress.isEmpty,
isLoading: sendStore.state is CreatingTransaction ||
sendStore.state is TransactionCommitted,
onPressed: () => sendStore.createTransaction(
address: tradeStore.trade.inputAddress,
amount: tradeStore.trade.amount),
text: tradeStore.trade.provider ==
ExchangeProviderDescription.xmrto
? S.of(context).confirm
: S.of(context).send_xmr,
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.button.color),
)
: Offstage()),
color: Colors.white),
)
],
);
}),
bottomSectionPadding: EdgeInsets.all(24),
bottomSection: Observer(
builder: (_) => tradeStore.trade.from == CryptoCurrency.xmr &&
!(sendStore.state is TransactionCommitted)
? LoadingPrimaryButton(
isDisabled: tradeStore.trade.inputAddress == null ||
tradeStore.trade.inputAddress.isEmpty,
isLoading: sendStore.state is CreatingTransaction ||
sendStore.state is TransactionCommitted,
onPressed: () => sendStore.createTransaction(
address: tradeStore.trade.inputAddress,
amount: tradeStore.trade.amount),
text: tradeStore.trade.provider ==
ExchangeProviderDescription.xmrto
? S.of(context).confirm
: S.of(context).send_xmr,
color: Colors.blue,
textColor: Colors.white)
: Offstage()),
),
);
}
@ -334,14 +333,11 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).error),
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
@ -352,23 +348,18 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).confirm_sending),
content: Text(S.of(context).commit_transaction_amount_fee(
sendStore.pendingTransaction.amount,
sendStore.pendingTransaction.fee)),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
sendStore.commitTransaction();
}),
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () => Navigator.of(context).pop(),
)
],
return AlertWithTwoActions(
alertTitle: S.of(context).confirm_sending,
alertContent: S.of(context).commit_transaction_amount_fee(
sendStore.pendingTransaction.amount,
sendStore.pendingTransaction.fee),
leftButtonText: S.of(context).ok,
rightButtonText: S.of(context).cancel,
actionLeftButton: () {
Navigator.of(context).pop();
sendStore.commitTransaction();
},
actionRightButton: () => Navigator.of(context).pop()
);
});
});
@ -379,14 +370,11 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).sending),
content: Text(S.of(context).transaction_sent),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).sending,
alertContent: S.of(context).transaction_sent,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});

View file

@ -170,7 +170,7 @@ class ReceiveBodyState extends State<ReceiveBody> {
height: 48,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(27)),
borderRadius: BorderRadius.all(Radius.circular(24)),
color: PaletteDark.walletCardSubAddressField
),
child: Row(

View file

@ -27,7 +27,7 @@ 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/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/template_tile.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
class SendPage extends BasePage {
@ -357,7 +357,7 @@ class SendFormState extends State<SendForm> {
if (index == 0) {
return GestureDetector(
onTap: () => Navigator.of(context)
.pushNamed(Routes.sendTemplate, arguments: sendStore),
.pushNamed(Routes.sendTemplate),
child: Container(
padding: EdgeInsets.only(right: 10),
child: DottedBorder(
@ -394,9 +394,9 @@ class SendFormState extends State<SendForm> {
final template = sendTemplateStore.templates[index];
return TemplateTile(
name: template.name,
to: template.name,
amount: template.amount,
cryptoCurrency: template.cryptoCurrency,
from: template.cryptoCurrency,
onTap: () {
_addressController.text = template.address;
_cryptoAmountController.text = template.amount;

View file

@ -33,6 +33,10 @@ abstract class ExchangeStoreBase with Store {
provider = initialProvider;
depositCurrency = initialDepositCurrency;
receiveCurrency = initialReceiveCurrency;
depositAmount = '';
receiveAmount = '';
depositAddress = '';
receiveAddress = '';
limitsState = LimitsInitialState();
tradeState = ExchangeTradeStateInitial();
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
@ -63,6 +67,12 @@ abstract class ExchangeStoreBase with Store {
@observable
String receiveAmount;
@observable
String depositAddress;
@observable
String receiveAddress;
@observable
bool isValid;
@ -71,10 +81,6 @@ abstract class ExchangeStoreBase with Store {
Box<Trade> trades;
String depositAddress;
String receiveAddress;
WalletStore walletStore;
Limits limits;

View file

@ -0,0 +1,40 @@
import 'dart:async';
import 'package:mobx/mobx.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
part 'exchange_template_store.g.dart';
class ExchangeTemplateStore = ExchangeTemplateBase with _$ExchangeTemplateStore;
abstract class ExchangeTemplateBase with Store {
ExchangeTemplateBase({this.templateSource}) {
templates = ObservableList<ExchangeTemplate>();
update();
}
@observable
ObservableList<ExchangeTemplate> templates;
Box<ExchangeTemplate> templateSource;
@action
void update() =>
templates.replaceRange(0, templates.length, templateSource.values.toList());
@action
Future addTemplate({String amount, String depositCurrency, String receiveCurrency,
String provider, String depositAddress, String receiveAddress}) async {
final template = ExchangeTemplate(
amount: amount,
depositCurrency: depositCurrency,
receiveCurrency: receiveCurrency,
provider: provider,
depositAddress: depositAddress,
receiveAddress: receiveAddress);
await templateSource.add(template);
}
@action
Future remove({ExchangeTemplate template}) async => await template.delete();
}

View file

@ -19,6 +19,8 @@ class AddressTextField extends StatelessWidget {
],
this.onURIScanned,
this.focusNode,
this.isBorderExist = true,
this.buttonColor = PaletteDark.walletCardSubAddressField,
this.validator});
static const prefixIconWidth = 34.0;
@ -31,6 +33,8 @@ class AddressTextField extends StatelessWidget {
final Function(Uri) onURIScanned;
final List<AddressTextFieldOption> options;
final FormFieldValidator<String> validator;
final bool isBorderExist;
final Color buttonColor;
FocusNode focusNode;
@override
@ -62,7 +66,7 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: PaletteDark.walletCardSubAddressField,
color: buttonColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/qr_code_icon.png')),
@ -80,7 +84,7 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: PaletteDark.walletCardSubAddressField,
color: buttonColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
@ -99,7 +103,7 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: PaletteDark.walletCardSubAddressField,
color: buttonColor,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
@ -114,13 +118,17 @@ class AddressTextField extends StatelessWidget {
color: PaletteDark.walletCardText
),
hintText: placeholder ?? S.current.widgets_address,
focusedBorder: UnderlineInputBorder(
focusedBorder: isBorderExist
? UnderlineInputBorder(
borderSide: BorderSide(
color: PaletteDark.walletCardSubAddressField,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
width: 1.0))
: InputBorder.none,
enabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: PaletteDark.walletCardSubAddressField, width: 1.0)),
BorderSide(color: PaletteDark.walletCardSubAddressField, width: 1.0))
: InputBorder.none,
),
validator: validator,
);

View file

@ -15,6 +15,9 @@ class BaseTextFormField extends StatelessWidget {
this.textColor = Colors.white,
this.hintColor = PaletteDark.walletCardText,
this.borderColor = PaletteDark.menuList,
this.prefix,
this.suffix,
this.enabled = true,
this.validator
});
@ -29,6 +32,9 @@ class BaseTextFormField extends StatelessWidget {
final Color textColor;
final Color hintColor;
final Color borderColor;
final Widget prefix;
final Widget suffix;
final bool enabled;
final FormFieldValidator<String> validator;
@override
@ -41,11 +47,14 @@ class BaseTextFormField extends StatelessWidget {
autovalidate: autovalidate,
maxLines: maxLines,
inputFormatters: inputFormatters,
enabled: enabled,
style: TextStyle(
fontSize: 16.0,
color: textColor
),
decoration: InputDecoration(
prefix: prefix,
suffix: suffix,
hintStyle: TextStyle(
color: hintColor,
fontSize: 16

View file

@ -3,15 +3,15 @@ import 'package:cake_wallet/palette.dart';
class TemplateTile extends StatelessWidget {
TemplateTile({
@required this.name,
@required this.to,
@required this.amount,
@required this.cryptoCurrency,
@required this.from,
@required this.onTap
});
final String name;
final String to;
final String amount;
final String cryptoCurrency;
final String from;
final VoidCallback onTap;
final toIcon = Image.asset('assets/images/to_icon.png');
@ -44,7 +44,7 @@ class TemplateTile extends StatelessWidget {
Padding(
padding: EdgeInsets.only(left: 5),
child: Text(
cryptoCurrency,
from,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
@ -59,7 +59,7 @@ class TemplateTile extends StatelessWidget {
Padding(
padding: EdgeInsets.only(left: 5),
child: Text(
name,
to,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,

View file

@ -1,24 +1,40 @@
import 'package:flutter/material.dart';
class TopPanel extends StatelessWidget {
TopPanel({@required this.color, @required this.widget});
class TopPanel extends StatefulWidget {
TopPanel({
@required this.color,
@required this.widget,
this.edgeInsets = const EdgeInsets.all(24)
});
final Color color;
final Widget widget;
final EdgeInsets edgeInsets;
@override
TopPanelState createState() => TopPanelState(color, widget, edgeInsets);
}
class TopPanelState extends State<TopPanel> {
TopPanelState(this._color, this._widget, this._edgeInsets);
final Color _color;
final Widget _widget;
final EdgeInsets _edgeInsets;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: EdgeInsets.all(24),
padding: _edgeInsets,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
),
color: color
color: _color
),
child: widget,
child: _widget,
);
}
}

View file

@ -74,7 +74,7 @@
"copy_id" : "ID kopieren",
"exchange_result_write_down_trade_id" : "Bitte kopieren oder notieren Sie die Handel-ID, um fortzufahren.",
"trade_id" : "Handel-ID:\n${id}",
"trade_id" : "Handel-ID:",
"copied_to_clipboard" : "In die Zwischenablage kopiert",
"saved_the_trade_id" : "Ich habe die Geschäfts-ID gespeichert",
"fetching" : "holen",

View file

@ -74,7 +74,7 @@
"copy_id" : "Copy ID",
"exchange_result_write_down_trade_id" : "Please copy or write down the trade ID to continue.",
"trade_id" : "Trade ID:\n${id}",
"trade_id" : "Trade ID:",
"copied_to_clipboard" : "Copied to Clipboard",
"saved_the_trade_id" : "I've saved the trade ID",
"fetching" : "Fetching",

View file

@ -74,7 +74,7 @@
"copy_id" : "Copiar ID",
"exchange_result_write_down_trade_id" : "Por favor, copia o escribe el ID.",
"trade_id" : "Comercial ID:\n${id}",
"trade_id" : "Comercial ID:",
"copied_to_clipboard" : "Copiado al portapapeles",
"saved_the_trade_id" : "He salvado comercial ID",
"fetching" : "Cargando",

View file

@ -74,7 +74,7 @@
"copy_id" : "प्रतिलिपि ID",
"exchange_result_write_down_trade_id" : "जारी रखने के लिए कृपया ट्रेड ID की प्रतिलिपि बनाएँ या लिखें.",
"trade_id" : "व्यापार ID:\n${id}",
"trade_id" : "व्यापार ID:",
"copied_to_clipboard" : "क्लिपबोर्ड पर नकल",
"saved_the_trade_id" : "मैंने व्यापार बचा लिया है ID",
"fetching" : "ला रहा है",

View file

@ -74,7 +74,7 @@
"copy_id" : "IDをコピー",
"exchange_result_write_down_trade_id" : "続行するには、取引IDをコピーまたは書き留めてください.",
"trade_id" : "取引ID:\n${id}",
"trade_id" : "取引ID:",
"copied_to_clipboard" : "クリップボードにコピーしました",
"saved_the_trade_id" : "取引IDを保存しました",
"fetching" : "フェッチング",

View file

@ -74,7 +74,7 @@
"copy_id" : "부 ID",
"exchange_result_write_down_trade_id" : "계속하려면 거래 ID를 복사하거나 적어 두십시오..",
"trade_id" : "무역 ID:\n${id}",
"trade_id" : "무역 ID:",
"copied_to_clipboard" : "클립 보드에 복사",
"saved_the_trade_id" : "거래 ID를 저장했습니다",
"fetching" : "가져 오는 중",

View file

@ -74,7 +74,7 @@
"copy_id" : "ID kopiëren",
"exchange_result_write_down_trade_id" : "Kopieer of noteer de handels-ID om door te gaan.",
"trade_id" : "Trade ID:\n${id}",
"trade_id" : "Trade ID:",
"copied_to_clipboard" : "Gekopieerd naar het klembord",
"saved_the_trade_id" : "Ik heb de ruil-ID opgeslagen",
"fetching" : "Ophalen",

View file

@ -74,7 +74,7 @@
"copy_id" : "ID kopii",
"exchange_result_write_down_trade_id" : "Skopiuj lub zanotuj identyfikator transakcji, aby kontynuować.",
"trade_id" : "Identyfikator handlu:\n${id}",
"trade_id" : "Identyfikator handlu:",
"copied_to_clipboard" : "Skopiowane do schowka",
"saved_the_trade_id" : "Zapisałem ID",
"fetching" : "Ujmujący",

View file

@ -74,7 +74,7 @@
"copy_id" : "Copiar ID",
"exchange_result_write_down_trade_id" : "Copie ou anote o ID da troca para continuar.",
"trade_id" : "ID da troca:\n${id}",
"trade_id" : "ID da troca:",
"copied_to_clipboard" : "Copiado para a área de transferência",
"saved_the_trade_id" : "ID da troca salvo",
"fetching" : "Buscando",

View file

@ -74,7 +74,7 @@
"copy_id" : "Скопировать ID",
"exchange_result_write_down_trade_id" : "Пожалуйста, скопируйте или запишите ID сделки.",
"trade_id" : "ID сделки:\n${id}",
"trade_id" : "ID сделки:",
"copied_to_clipboard" : "Скопировано в буфер обмена",
"saved_the_trade_id" : "Я сохранил ID сделки",
"fetching" : "Загрузка",

View file

@ -74,7 +74,7 @@
"copy_id" : "Скопіювати ID",
"exchange_result_write_down_trade_id" : "Будь ласка, скопіюйте або запишіть ID операції.",
"trade_id" : "ID операції:\n${id}",
"trade_id" : "ID операції:",
"copied_to_clipboard" : "Скопійовано в буфер обміну",
"saved_the_trade_id" : "Я зберіг ID операції",
"fetching" : "Завантаження",

View file

@ -74,7 +74,7 @@
"copy_id" : "复印ID",
"exchange_result_write_down_trade_id" : "请复制或写下交易编号以继续.",
"trade_id" : "贸易编号:\n${id}",
"trade_id" : "贸易编号:",
"copied_to_clipboard" : "复制到剪贴板",
"saved_the_trade_id" : "我已经保存了交易ID",
"fetching" : "正在取得",