CAKE-20 | updated send and send template pages; added send_template_store and exchange_template_store; created base_send_widget; applied send_template_store to send_view_model; applied base_send_widget to send and send template pages; added properties to address_text_field, base_text_field and template_tile

Oleksandr Sobol committed Jul 29, 2020 at 19:55 UTC fe9deecd03779156f9c816df23f5d5e31f198fae
35 files changed +1267 -1050
assets/fonts/Poppins-SemiBold.ttf
Binary files /dev/null and b/assets/fonts/Poppins-SemiBold.ttf differ
assets/images/2.0x/duplicate.png
Binary files /dev/null and b/assets/images/2.0x/duplicate.png differ
assets/images/3.0x/duplicate.png
Binary files /dev/null and b/assets/images/3.0x/duplicate.png differ
assets/images/duplicate.png
Binary files /dev/null and b/assets/images/duplicate.png differ
lib/core/amount_validator.dart
+2 -2
@@ -13,10 +13,10 @@ class AmountValidator extends TextValidator {
13 static String _pattern(WalletType type) {
14 switch (type) {
15 case WalletType.monero:
16 - return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12})\$';
16 + return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12}|ALL)\$';
17 case WalletType.bitcoin:
18 // FIXME: Incorrect pattern for bitcoin
19 - return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12})\$';
19 + return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12}|ALL)\$';
20 default:
21 return '';
22 }
lib/core/template_validator.dart new
+12
@@ -0,0 +1,12 @@
1 +import 'package:cake_wallet/core/validator.dart';
2 +import 'package:cake_wallet/generated/i18n.dart';
3 +
4 +class TemplateValidator extends TextValidator {
5 + TemplateValidator()
6 + : super(
7 + minLength: 0,
8 + maxLength: 0,
9 + pattern: '''^[^`,'"]{1,20}\$''',
10 + errorMessage: S.current.error_text_template
11 + );
12 +}
\ No newline at end of file
lib/core/validator.dart
+2 -2
@@ -27,7 +27,7 @@ class TextValidator extends Validator<String> {
27 @override
28 bool isValid(String value) {
29 if (value == null || value.isEmpty) {
30 - return true;
30 + return false;
31 }
32
33 return value.length > (minLength ?? 0) &&
@@ -42,4 +42,4 @@ class TextValidator extends Validator<String> {
42 class WalletNameValidator extends TextValidator {
43 WalletNameValidator()
44 : super(minLength: 1, maxLength: 15, pattern: '^[a-zA-Z0-9_]\$');
45 -}
45 +}
\ No newline at end of file
lib/di.dart
+19 -2
@@ -7,6 +7,7 @@ import 'package:cake_wallet/src/screens/contact/contact_page.dart';
7 import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
8 import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
9 import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
10 +import 'package:cake_wallet/src/screens/send/send_template_page.dart';
11 import 'package:cake_wallet/src/screens/settings/settings.dart';
12 import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
13 import 'package:cake_wallet/store/contact_list_store.dart';
@@ -57,6 +58,10 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
58 import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
59 import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
60 import 'package:cake_wallet/store/dashboard/fiat_convertation_store.dart';
61 +import 'package:cake_wallet/store/templates/send_template_store.dart';
62 +import 'package:cake_wallet/store/templates/exchange_template_store.dart';
63 +import 'package:cake_wallet/src/domain/common/template.dart';
64 +import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
65
66 final getIt = GetIt.instance;
67
@@ -83,7 +88,9 @@ Future setup(
88 {Box<WalletInfo> walletInfoSource,
89 Box<Node> nodeSource,
90 Box<Contact> contactSource,
86 - Box<Trade> tradesSource}) async {
91 + Box<Trade> tradesSource,
92 + Box<Template> templates,
93 + Box<ExchangeTemplate> exchangeTemplates}) async {
94 getIt.registerSingletonAsync<SharedPreferences>(
95 () => SharedPreferences.getInstance());
96
@@ -110,6 +117,10 @@ Future setup(
117 TradeFilterStore(wallet: getIt.get<AppStore>().wallet));
118 getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
119 getIt.registerSingleton<FiatConvertationStore>(FiatConvertationStore());
120 + getIt.registerSingleton<SendTemplateStore>(
121 + SendTemplateStore(templateSource: templates));
122 + getIt.registerSingleton<ExchangeTemplateStore>(
123 + ExchangeTemplateStore(templateSource: exchangeTemplates));
124
125 getIt.registerFactory<KeyService>(
126 () => KeyService(getIt.get<FlutterSecureStorage>()));
@@ -198,11 +209,17 @@ Future setup(
209 getIt.get<WalletAddressEditOrCreateViewModel>(param1: item)));
210
211 getIt.registerFactory<SendViewModel>(() => SendViewModel(
201 - getIt.get<AppStore>().wallet, getIt.get<AppStore>().settingsStore));
212 + getIt.get<AppStore>().wallet,
213 + getIt.get<AppStore>().settingsStore,
214 + getIt.get<FiatConvertationStore>(),
215 + getIt.get<SendTemplateStore>()));
216
217 getIt.registerFactory(
218 () => SendPage(sendViewModel: getIt.get<SendViewModel>()));
219
220 + getIt.registerFactory(
221 + () => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
222 +
223 getIt.registerFactory(() => WalletListViewModel(
224 walletInfoSource, getIt.get<AppStore>(), getIt.get<KeyService>()));
225
lib/generated/i18n.dart
+104 -35
@@ -56,6 +56,8 @@ class S implements WidgetsLocalizations {
56 String get choose_wallet_currency => "Please choose wallet currency:";
57 String get clear => "Clear";
58 String get confirm => "Confirm";
59 + String get confirm_delete_template => "This action will delete this template. Do you wish to continue?";
60 + String get confirm_delete_wallet => "This action will delete this wallet. Do you wish to continue?";
61 String get confirm_sending => "Confirm sending";
62 String get contact => "Contact";
63 String get contact_name => "Contact Name";
@@ -183,14 +185,13 @@ class S implements WidgetsLocalizations {
185 String get send_estimated_fee => "Estimated fee:";
186 String get send_fee => "Fee:";
187 String get send_got_it => "Got it";
186 - String get send_monero_address => "Monero address";
188 String get send_name => "Name";
189 String get send_new => "New";
190 String get send_payment_id => "Payment ID (optional)";
191 String get send_sending => "Sending...";
192 String get send_success => "Your Monero was successfully sent";
193 String get send_templates => "Templates";
193 - String get send_title => "Send Monero";
194 + String get send_title => "Send";
195 String get send_xmr => "Send XMR";
196 String get send_your_wallet => "Your wallet";
197 String get sending => "Sending";
@@ -234,6 +235,7 @@ class S implements WidgetsLocalizations {
235 String get sync_status_starting_sync => "STARTING SYNC";
236 String get sync_status_syncronized => "SYNCHRONIZED";
237 String get sync_status_syncronizing => "SYNCHRONIZING";
238 + String get template => "Template";
239 String get today => "Today";
240 String get trade_details_created_at => "Created at";
241 String get trade_details_fetching => "Fetching";
@@ -316,6 +318,7 @@ class S implements WidgetsLocalizations {
318 String openalias_alert_content(String recipient_name) => "You will be sending funds to\n${recipient_name}";
319 String powered_by(String title) => "Powered by ${title}";
320 String router_no_route(String name) => "No route defined for ${name}";
321 + String send_address(String cryptoCurrency) => "${cryptoCurrency} address";
322 String send_priority(String transactionPriority) => "Currently the fee is set at ${transactionPriority} priority.\nTransaction priority can be adjusted in the settings";
323 String time(String minutes, String seconds) => "${minutes}m ${seconds}s";
324 String trade_details_copied(String title) => "${title} copied to Clipboard";
@@ -677,6 +680,8 @@ class $de extends S {
680 @override
681 String get sync_status_syncronized => "SYNCHRONISIERT";
682 @override
683 + String get template => "Vorlage";
684 + @override
685 String get transaction_priority_medium => "Mittel";
686 @override
687 String get transaction_details_transaction_id => "Transaktions-ID";
@@ -725,6 +730,8 @@ class $de extends S {
730 @override
731 String get trade_not_created => "Handel nicht angelegt.";
732 @override
733 + String get confirm_delete_wallet => "Diese Aktion löscht diese Brieftasche. Möchten Sie fortfahren?";
734 + @override
735 String get restore_wallet_name => "Walletname";
736 @override
737 String get widgets_seed => "Seed";
@@ -733,6 +740,8 @@ class $de extends S {
740 @override
741 String get rename => "Umbenennen";
742 @override
743 + String get confirm_delete_template => "Diese Aktion löscht diese Vorlage. Möchten Sie fortfahren?";
744 + @override
745 String get restore_active_seed => "Aktives Seed";
746 @override
747 String get send_name => "Name";
@@ -793,7 +802,7 @@ class $de extends S {
802 @override
803 String get send => "Senden";
804 @override
796 - String get send_title => "Senden Sie Monero";
805 + String get send_title => "Senden Sie";
806 @override
807 String get error_text_keys => "Walletschlüssel können nur 64 hexadezimale Zeichen enthalten";
808 @override
@@ -877,8 +886,6 @@ class $de extends S {
886 @override
887 String get restore_description_from_backup => "Sie können die gesamte Cake Wallet-App von wiederherstellen Ihre Sicherungsdatei";
888 @override
880 - String get send_monero_address => "Monero-Adresse";
881 - @override
889 String get error_text_node_port => "Der Knotenport kann nur Nummern zwischen 0 und 65535 enthalten";
890 @override
891 String get add_new_word => "Neues Wort hinzufügen";
@@ -925,6 +932,8 @@ class $de extends S {
932 @override
933 String error_text_maximum_limit(String provider, String max, String currency) => "Handel für ${provider} wird nicht erstellt. Menge ist mehr als maximal: ${max} ${currency}";
934 @override
935 + String send_address(String cryptoCurrency) => "${cryptoCurrency}-Adresse";
936 + @override
937 String min_value(String value, String currency) => "Mindest: ${value} ${currency}";
938 @override
939 String failed_authentication(String state_error) => "Authentifizierung fehlgeschlagen. ${state_error}";
@@ -1297,6 +1306,8 @@ class $hi extends S {
1306 @override
1307 String get sync_status_syncronized => "सिंक्रनाइज़";
1308 @override
1309 + String get template => "खाका";
1310 + @override
1311 String get transaction_priority_medium => "मध्यम";
1312 @override
1313 String get transaction_details_transaction_id => "लेनदेन आईडी";
@@ -1345,6 +1356,8 @@ class $hi extends S {
1356 @override
1357 String get trade_not_created => "व्यापार नहीं बनाया गया.";
1358 @override
1359 + String get confirm_delete_wallet => "यह क्रिया इस वॉलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?";
1360 + @override
1361 String get restore_wallet_name => "बटुए का नाम";
1362 @override
1363 String get widgets_seed => "बीज";
@@ -1353,6 +1366,8 @@ class $hi extends S {
1366 @override
1367 String get rename => "नाम बदलें";
1368 @override
1369 + String get confirm_delete_template => "यह क्रिया इस टेम्पलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?";
1370 + @override
1371 String get restore_active_seed => "सक्रिय बीज";
1372 @override
1373 String get send_name => "नाम";
@@ -1413,7 +1428,7 @@ class $hi extends S {
1428 @override
1429 String get send => "संदेश";
1430 @override
1416 - String get send_title => "संदेश Monero";
1431 + String get send_title => "संदेश";
1432 @override
1433 String get error_text_keys => "वॉलेट कीज़ में हेक्स में केवल 64 वर्ण हो सकते हैं";
1434 @override
@@ -1497,8 +1512,6 @@ class $hi extends S {
1512 @override
1513 String get restore_description_from_backup => "आप से पूरे केक वॉलेट एप्लिकेशन को पुनर्स्थापित कर सकते हैं आपकी बैक-अप फ़ाइल";
1514 @override
1500 - String get send_monero_address => "मोनरो पता";
1501 - @override
1515 String get error_text_node_port => "नोड पोर्ट में केवल 0 और 65535 के बीच संख्याएँ हो सकती हैं";
1516 @override
1517 String get add_new_word => "नया शब्द जोड़ें";
@@ -1545,6 +1558,8 @@ class $hi extends S {
1558 @override
1559 String error_text_maximum_limit(String provider, String max, String currency) => "व्यापार ${provider} के लिए नहीं बनाया गया है। राशि अधिक है तो अधिकतम: ${max} ${currency}";
1560 @override
1561 + String send_address(String cryptoCurrency) => "${cryptoCurrency} पता";
1562 + @override
1563 String min_value(String value, String currency) => "मिन: ${value} ${currency}";
1564 @override
1565 String failed_authentication(String state_error) => "प्रमाणीकरण विफल. ${state_error}";
@@ -1917,6 +1932,8 @@ class $ru extends S {
1932 @override
1933 String get sync_status_syncronized => "СИНХРОНИЗИРОВАН";
1934 @override
1935 + String get template => "Шаблон";
1936 + @override
1937 String get transaction_priority_medium => "Средний";
1938 @override
1939 String get transaction_details_transaction_id => "ID транзакции";
@@ -1965,6 +1982,8 @@ class $ru extends S {
1982 @override
1983 String get trade_not_created => "Сделка не создана.";
1984 @override
1985 + String get confirm_delete_wallet => "Это действие удалит кошелек. Вы хотите продолжить?";
1986 + @override
1987 String get restore_wallet_name => "Имя кошелька";
1988 @override
1989 String get widgets_seed => "Мнемоническая фраза";
@@ -1973,6 +1992,8 @@ class $ru extends S {
1992 @override
1993 String get rename => "Переименовать";
1994 @override
1995 + String get confirm_delete_template => "Это действие удалит шаблон. Вы хотите продолжить?";
1996 + @override
1997 String get restore_active_seed => "Активная мнемоническая фраза";
1998 @override
1999 String get send_name => "Имя";
@@ -2033,7 +2054,7 @@ class $ru extends S {
2054 @override
2055 String get send => "Отправить";
2056 @override
2036 - String get send_title => "Отправить Monero";
2057 + String get send_title => "Отправить";
2058 @override
2059 String get error_text_keys => "Ключи кошелька могут содержать только 64 символа в hex";
2060 @override
@@ -2117,8 +2138,6 @@ class $ru extends S {
2138 @override
2139 String get restore_description_from_backup => "Вы можете восстановить Cake Wallet из вашего back-up файла";
2140 @override
2120 - String get send_monero_address => "Monero адрес";
2121 - @override
2141 String get error_text_node_port => "Порт ноды может содержать только цифры от 0 до 65535";
2142 @override
2143 String get add_new_word => "Добавить новое слово";
@@ -2165,6 +2184,8 @@ class $ru extends S {
2184 @override
2185 String error_text_maximum_limit(String provider, String max, String currency) => "Сделка для ${provider} не создана. Сумма больше максимальной: ${max} ${currency}";
2186 @override
2187 + String send_address(String cryptoCurrency) => "${cryptoCurrency} адрес";
2188 + @override
2189 String min_value(String value, String currency) => "Мин: ${value} ${currency}";
2190 @override
2191 String failed_authentication(String state_error) => "Ошибка аутентификации. ${state_error}";
@@ -2537,6 +2558,8 @@ class $ko extends S {
2558 @override
2559 String get sync_status_syncronized => "동기화";
2560 @override
2561 + String get template => "주형";
2562 + @override
2563 String get transaction_priority_medium => "매질";
2564 @override
2565 String get transaction_details_transaction_id => "트랜잭션 ID";
@@ -2585,6 +2608,8 @@ class $ko extends S {
2608 @override
2609 String get trade_not_created => "거래가 생성되지 않았습니다.";
2610 @override
2611 + String get confirm_delete_wallet => "이 작업은이 지갑을 삭제합니다. 계속 하시겠습니까?";
2612 + @override
2613 String get restore_wallet_name => "지갑 이름";
2614 @override
2615 String get widgets_seed => "씨";
@@ -2593,6 +2618,8 @@ class $ko extends S {
2618 @override
2619 String get rename => "이름 바꾸기";
2620 @override
2621 + String get confirm_delete_template => "이 작업은이 템플릿을 삭제합니다. 계속 하시겠습니까?";
2622 + @override
2623 String get restore_active_seed => "활성 종자";
2624 @override
2625 String get send_name => "이름";
@@ -2653,7 +2680,7 @@ class $ko extends S {
2680 @override
2681 String get send => "보내다";
2682 @override
2656 - String get send_title => "모네로 보내기";
2683 + String get send_title => "보내다";
2684 @override
2685 String get error_text_keys => "지갑 키는 16 진수로 64 자만 포함 할 수 있습니다";
2686 @override
@@ -2737,8 +2764,6 @@ class $ko extends S {
2764 @override
2765 String get restore_description_from_backup => "백업 파일에서 전체 Cake Wallet 앱을 복원 할 수 있습니다.";
2766 @override
2740 - String get send_monero_address => "모네로 주소";
2741 - @override
2767 String get error_text_node_port => "노드 포트는 0에서 65535 사이의 숫자 만 포함 할 수 있습니다";
2768 @override
2769 String get add_new_word => "새로운 단어 추가";
@@ -2785,6 +2810,8 @@ class $ko extends S {
2810 @override
2811 String error_text_maximum_limit(String provider, String max, String currency) => "거래 ${provider} 가 생성되지 않습니다. 금액이 최대 값보다 많습니다. ${max} ${currency}";
2812 @override
2813 + String send_address(String cryptoCurrency) => "${cryptoCurrency} 주소";
2814 + @override
2815 String min_value(String value, String currency) => "최소: ${value} ${currency}";
2816 @override
2817 String failed_authentication(String state_error) => "인증 실패. ${state_error}";
@@ -3157,6 +3184,8 @@ class $pt extends S {
3184 @override
3185 String get sync_status_syncronized => "SINCRONIZADO";
3186 @override
3187 + String get template => "Modelo";
3188 + @override
3189 String get transaction_priority_medium => "Média";
3190 @override
3191 String get transaction_details_transaction_id => "ID da transação";
@@ -3205,6 +3234,8 @@ class $pt extends S {
3234 @override
3235 String get trade_not_created => "Troca não criada.";
3236 @override
3237 + String get confirm_delete_wallet => "Esta ação excluirá esta carteira. Você deseja continuar?";
3238 + @override
3239 String get restore_wallet_name => "Nome da carteira";
3240 @override
3241 String get widgets_seed => "Semente";
@@ -3213,6 +3244,8 @@ class $pt extends S {
3244 @override
3245 String get rename => "Renomear";
3246 @override
3247 + String get confirm_delete_template => "Esta ação excluirá este modelo. Você deseja continuar?";
3248 + @override
3249 String get restore_active_seed => "Semente ativa";
3250 @override
3251 String get send_name => "Nome";
@@ -3273,7 +3306,7 @@ class $pt extends S {
3306 @override
3307 String get send => "Enviar";
3308 @override
3276 - String get send_title => "Enviar Monero";
3309 + String get send_title => "Enviar";
3310 @override
3311 String get error_text_keys => "As chaves da carteira podem conter apenas 64 caracteres em hexadecimal";
3312 @override
@@ -3357,8 +3390,6 @@ class $pt extends S {
3390 @override
3391 String get restore_description_from_backup => "Você pode restaurar todo o aplicativo Cake Wallet de seu arquivo de backup";
3392 @override
3360 - String get send_monero_address => "Endereço Monero";
3361 - @override
3393 String get error_text_node_port => "A porta do nó deve conter apenas números entre 0 e 65535";
3394 @override
3395 String get add_new_word => "Adicionar nova palavra";
@@ -3405,6 +3436,8 @@ class $pt extends S {
3436 @override
3437 String error_text_maximum_limit(String provider, String max, String currency) => "A troca por ${provider} não é criada. O valor é superior ao máximo: ${max} ${currency}";
3438 @override
3439 + String send_address(String cryptoCurrency) => "Endereço ${cryptoCurrency}";
3440 + @override
3441 String min_value(String value, String currency) => "Mín: ${value} ${currency}";
3442 @override
3443 String failed_authentication(String state_error) => "Falha na autenticação. ${state_error}";
@@ -3777,6 +3810,8 @@ class $uk extends S {
3810 @override
3811 String get sync_status_syncronized => "СИНХРОНІЗОВАНИЙ";
3812 @override
3813 + String get template => "Шаблон";
3814 + @override
3815 String get transaction_priority_medium => "Середній";
3816 @override
3817 String get transaction_details_transaction_id => "ID транзакції";
@@ -3825,6 +3860,8 @@ class $uk extends S {
3860 @override
3861 String get trade_not_created => "Операція не створена.";
3862 @override
3863 + String get confirm_delete_wallet => "Ця дія видалить гаманець. Ви хочете продовжити?";
3864 + @override
3865 String get restore_wallet_name => "Ім'я гаманця";
3866 @override
3867 String get widgets_seed => "Мнемонічна фраза";
@@ -3833,6 +3870,8 @@ class $uk extends S {
3870 @override
3871 String get rename => "Перейменувати";
3872 @override
3873 + String get confirm_delete_template => "Ця дія видалить шаблон. Ви хочете продовжити?";
3874 + @override
3875 String get restore_active_seed => "Активна мнемонічна фраза";
3876 @override
3877 String get send_name => "Ім'я";
@@ -3893,7 +3932,7 @@ class $uk extends S {
3932 @override
3933 String get send => "Відправити";
3934 @override
3896 - String get send_title => "Відправити Monero";
3935 + String get send_title => "Відправити";
3936 @override
3937 String get error_text_keys => "Ключі гаманця можуть містити тільки 64 символів в hex";
3938 @override
@@ -3977,8 +4016,6 @@ class $uk extends S {
4016 @override
4017 String get restore_description_from_backup => "Ви можете відновити Cake Wallet з вашого резервного файлу";
4018 @override
3980 - String get send_monero_address => "Monero адреса";
3981 - @override
4019 String get error_text_node_port => "Порт вузла може містити тільки цифри від 0 до 65535";
4020 @override
4021 String get add_new_word => "Добавити нове слово";
@@ -4025,6 +4062,8 @@ class $uk extends S {
4062 @override
4063 String error_text_maximum_limit(String provider, String max, String currency) => "Операція для ${provider} не створена. Сума більше максимальної: ${max} ${currency}";
4064 @override
4065 + String send_address(String cryptoCurrency) => "${cryptoCurrency} адреса";
4066 + @override
4067 String min_value(String value, String currency) => "Мін: ${value} ${currency}";
4068 @override
4069 String failed_authentication(String state_error) => "Помилка аутентифікації. ${state_error}";
@@ -4397,6 +4436,8 @@ class $ja extends S {
4436 @override
4437 String get sync_status_syncronized => "同期された";
4438 @override
4439 + String get template => "テンプレート";
4440 + @override
4441 String get transaction_priority_medium => "中";
4442 @override
4443 String get transaction_details_transaction_id => "トランザクションID";
@@ -4445,6 +4486,8 @@ class $ja extends S {
4486 @override
4487 String get trade_not_created => "作成されていない取引";
4488 @override
4489 + String get confirm_delete_wallet => "このアクションにより、このウォレットが削除されます。 続行しますか?";
4490 + @override
4491 String get restore_wallet_name => "ウォレット名";
4492 @override
4493 String get widgets_seed => "シード";
@@ -4453,6 +4496,8 @@ class $ja extends S {
4496 @override
4497 String get rename => "リネーム";
4498 @override
4499 + String get confirm_delete_template => "この操作により、このテンプレートが削除されます。 続行しますか?";
4500 + @override
4501 String get restore_active_seed => "アクティブシード";
4502 @override
4503 String get send_name => "名前";
@@ -4513,7 +4558,7 @@ class $ja extends S {
4558 @override
4559 String get send => "送る";
4560 @override
4516 - String get send_title => "Moneroを送信";
4561 + String get send_title => "を送信";
4562 @override
4563 String get error_text_keys => "ウォレットキーには、16進数で64文字しか含めることができません";
4564 @override
@@ -4597,8 +4642,6 @@ class $ja extends S {
4642 @override
4643 String get restore_description_from_backup => "Cake Walletアプリ全体を復元できますバックアップファイル";
4644 @override
4600 - String get send_monero_address => "Monero 住所";
4601 - @override
4645 String get error_text_node_port => "ノードポートには、0〜65535の数字のみを含めることができます";
4646 @override
4647 String get add_new_word => "新しい単語を追加";
@@ -4645,6 +4688,8 @@ class $ja extends S {
4688 @override
4689 String error_text_maximum_limit(String provider, String max, String currency) => "${provider} の取引は作成されません。 金額は最大値を超えています: ${max} ${currency}";
4690 @override
4691 + String send_address(String cryptoCurrency) => "${cryptoCurrency} 住所";
4692 + @override
4693 String min_value(String value, String currency) => "分: ${value} ${currency}";
4694 @override
4695 String failed_authentication(String state_error) => "認証失敗. ${state_error}";
@@ -5021,6 +5066,8 @@ class $pl extends S {
5066 @override
5067 String get sync_status_syncronized => "SYNCHRONIZOWANY";
5068 @override
5069 + String get template => "Szablon";
5070 + @override
5071 String get transaction_priority_medium => "Średni";
5072 @override
5073 String get transaction_details_transaction_id => "Transakcja ID";
@@ -5069,6 +5116,8 @@ class $pl extends S {
5116 @override
5117 String get trade_not_created => "Handel nie utworzony.";
5118 @override
5119 + String get confirm_delete_wallet => "Ta czynność usunie ten portfel. Czy chcesz kontynuować?";
5120 + @override
5121 String get restore_wallet_name => "Nazwa portfela";
5122 @override
5123 String get widgets_seed => "Ziarno";
@@ -5077,6 +5126,8 @@ class $pl extends S {
5126 @override
5127 String get rename => "Przemianować";
5128 @override
5129 + String get confirm_delete_template => "Ta czynność usunie ten szablon. Czy chcesz kontynuować?";
5130 + @override
5131 String get restore_active_seed => "Aktywne nasiona";
5132 @override
5133 String get send_name => "Imię";
@@ -5137,7 +5188,7 @@ class $pl extends S {
5188 @override
5189 String get send => "Wysłać";
5190 @override
5140 - String get send_title => "Wyślij Monero";
5191 + String get send_title => "Wyślij";
5192 @override
5193 String get error_text_keys => "Klucze portfela mogą zawierać tylko 64 znaki w systemie szesnastkowym";
5194 @override
@@ -5221,8 +5272,6 @@ class $pl extends S {
5272 @override
5273 String get restore_description_from_backup => "Możesz przywrócić całą aplikację Cake Wallet z plik kopii zapasowej";
5274 @override
5224 - String get send_monero_address => "Adres Monero";
5225 - @override
5275 String get error_text_node_port => "Port węzła może zawierać tylko liczby od 0 do 65535";
5276 @override
5277 String get add_new_word => "Dodaj nowe słowo";
@@ -5269,6 +5318,8 @@ class $pl extends S {
5318 @override
5319 String error_text_maximum_limit(String provider, String max, String currency) => "Wymiana dla ${provider} nie została utworzona. Kwota jest większa niż maksymalna: ${max} ${currency}";
5320 @override
5321 + String send_address(String cryptoCurrency) => "Adres ${cryptoCurrency}";
5322 + @override
5323 String min_value(String value, String currency) => "Min: ${value} ${currency}";
5324 @override
5325 String failed_authentication(String state_error) => "Nieudane uwierzytelnienie. ${state_error}";
@@ -5641,6 +5692,8 @@ class $es extends S {
5692 @override
5693 String get sync_status_syncronized => "SINCRONIZADO";
5694 @override
5695 + String get template => "Plantilla";
5696 + @override
5697 String get transaction_priority_medium => "Medio";
5698 @override
5699 String get transaction_details_transaction_id => "ID de transacción";
@@ -5689,6 +5742,8 @@ class $es extends S {
5742 @override
5743 String get trade_not_created => "Comercio no se crea.";
5744 @override
5745 + String get confirm_delete_wallet => "Esta acción eliminará esta billetera. ¿Desea continuar?";
5746 + @override
5747 String get restore_wallet_name => "Nombre de la billetera";
5748 @override
5749 String get widgets_seed => "Semilla";
@@ -5697,6 +5752,8 @@ class $es extends S {
5752 @override
5753 String get rename => "Rebautizar";
5754 @override
5755 + String get confirm_delete_template => "Esta acción eliminará esta plantilla. ¿Desea continuar?";
5756 + @override
5757 String get restore_active_seed => "Semilla activa";
5758 @override
5759 String get send_name => "Nombre";
@@ -5757,7 +5814,7 @@ class $es extends S {
5814 @override
5815 String get send => "Enviar";
5816 @override
5760 - String get send_title => "Enviar Monero";
5817 + String get send_title => "Enviar";
5818 @override
5819 String get error_text_keys => "Las llaves de billetera solo pueden contener 64 caracteres en hexadecimal";
5820 @override
@@ -5841,8 +5898,6 @@ class $es extends S {
5898 @override
5899 String get restore_description_from_backup => "Puede restaurar toda la aplicación Cake Wallet desde ysu archivo de respaldo";
5900 @override
5844 - String get send_monero_address => "Dirección de Monero";
5845 - @override
5901 String get error_text_node_port => "El puerto de nodo solo puede contener números entre 0 y 65535";
5902 @override
5903 String get add_new_word => "Agregar palabra nueva";
@@ -5889,6 +5944,8 @@ class $es extends S {
5944 @override
5945 String error_text_maximum_limit(String provider, String max, String currency) => "El comercio por ${provider} no se crea. La cantidad es más que el máximo: ${max} ${currency}";
5946 @override
5947 + String send_address(String cryptoCurrency) => "Dirección de ${cryptoCurrency}";
5948 + @override
5949 String min_value(String value, String currency) => "Min: ${value} ${currency}";
5950 @override
5951 String failed_authentication(String state_error) => "Autenticación fallida. ${state_error}";
@@ -6261,6 +6318,8 @@ class $nl extends S {
6318 @override
6319 String get sync_status_syncronized => "SYNCHRONIZED";
6320 @override
6321 + String get template => "Sjabloon";
6322 + @override
6323 String get transaction_priority_medium => "Medium";
6324 @override
6325 String get transaction_details_transaction_id => "Transactie ID";
@@ -6309,6 +6368,8 @@ class $nl extends S {
6368 @override
6369 String get trade_not_created => "Handel niet gecreëerd.";
6370 @override
6371 + String get confirm_delete_wallet => "Met deze actie wordt deze portemonnee verwijderd. Wilt u doorgaan?";
6372 + @override
6373 String get restore_wallet_name => "Portemonnee naam";
6374 @override
6375 String get widgets_seed => "Zaad";
@@ -6317,6 +6378,8 @@ class $nl extends S {
6378 @override
6379 String get rename => "Hernoemen";
6380 @override
6381 + String get confirm_delete_template => "Met deze actie wordt deze sjabloon verwijderd. Wilt u doorgaan?";
6382 + @override
6383 String get restore_active_seed => "Actief zaad";
6384 @override
6385 String get send_name => "Naam";
@@ -6377,7 +6440,7 @@ class $nl extends S {
6440 @override
6441 String get send => "Sturen";
6442 @override
6380 - String get send_title => "Stuur Monero";
6443 + String get send_title => "Stuur";
6444 @override
6445 String get error_text_keys => "Portefeuillesleutels kunnen maximaal 64 tekens bevatten in hexadecimale volgorde";
6446 @override
@@ -6461,8 +6524,6 @@ class $nl extends S {
6524 @override
6525 String get restore_description_from_backup => "Je kunt de hele Cake Wallet-app herstellen van uw back-upbestand";
6526 @override
6464 - String get send_monero_address => "Monero-adres";
6465 - @override
6527 String get error_text_node_port => "Knooppuntpoort kan alleen nummers tussen 0 en 65535 bevatten";
6528 @override
6529 String get add_new_word => "Nieuw woord toevoegen";
@@ -6509,6 +6570,8 @@ class $nl extends S {
6570 @override
6571 String error_text_maximum_limit(String provider, String max, String currency) => "Ruil voor ${provider} is niet gemaakt. Bedrag is meer dan maximaal: ${max} ${currency}";
6572 @override
6573 + String send_address(String cryptoCurrency) => "${cryptoCurrency}-adres";
6574 + @override
6575 String min_value(String value, String currency) => "Min: ${value} ${currency}";
6576 @override
6577 String failed_authentication(String state_error) => "Mislukte authenticatie. ${state_error}";
@@ -6881,6 +6944,8 @@ class $zh extends S {
6944 @override
6945 String get sync_status_syncronized => "已同步";
6946 @override
6947 + String get template => "模板";
6948 + @override
6949 String get transaction_priority_medium => "介质";
6950 @override
6951 String get transaction_details_transaction_id => "交易编号";
@@ -6929,6 +6994,8 @@ class $zh extends S {
6994 @override
6995 String get trade_not_created => "未建立交易.";
6996 @override
6997 + String get confirm_delete_wallet => "此操作將刪除此錢包。 你想繼續嗎?";
6998 + @override
6999 String get restore_wallet_name => "钱包名称";
7000 @override
7001 String get widgets_seed => "种子";
@@ -6937,6 +7004,8 @@ class $zh extends S {
7004 @override
7005 String get rename => "改名";
7006 @override
7007 + String get confirm_delete_template => "此操作將刪除此模板。 你想繼續嗎?";
7008 + @override
7009 String get restore_active_seed => "活性種子";
7010 @override
7011 String get send_name => "名稱";
@@ -6997,7 +7066,7 @@ class $zh extends S {
7066 @override
7067 String get send => "发送";
7068 @override
7000 - String get send_title => "发送门罗币";
7069 + String get send_title => "發送";
7070 @override
7071 String get error_text_keys => "钱包密钥只能包含16个字符的十六进制字符";
7072 @override
@@ -7081,8 +7150,6 @@ class $zh extends S {
7150 @override
7151 String get restore_description_from_backup => "您可以从还原整个Cake Wallet应用您的备份文件";
7152 @override
7084 - String get send_monero_address => "门罗地址";
7085 - @override
7153 String get error_text_node_port => "节点端口只能包含0到65535之间的数字";
7154 @override
7155 String get add_new_word => "添加新词";
@@ -7129,6 +7196,8 @@ class $zh extends S {
7196 @override
7197 String error_text_maximum_limit(String provider, String max, String currency) => "未創建 ${provider} 交易。 金額大於最大值:${max} ${currency}";
7198 @override
7199 + String send_address(String cryptoCurrency) => "${cryptoCurrency} 地址";
7200 + @override
7201 String min_value(String value, String currency) => "敏: ${value} ${currency}";
7202 @override
7203 String failed_authentication(String state_error) => "身份验证失败. ${state_error}";
lib/main.dart
+7 -1
@@ -127,6 +127,8 @@ void main() async {
127 contactSource: contacts,
128 tradesSource: trades,
129 fiatConvertationService: fiatConvertationService,
130 + templates: templates,
131 + exchangeTemplates: exchangeTemplates,
132 initialMigrationVersion: 3);
133
134 setReactions(
@@ -169,6 +171,8 @@ Future<void> initialSetup(
171 @required Box<Contact> contactSource,
172 @required Box<Trade> tradesSource,
173 @required FiatConvertationService fiatConvertationService,
174 + @required Box<Template> templates,
175 + @required Box<ExchangeTemplate> exchangeTemplates,
176 int initialMigrationVersion = 3}) async {
177 await defaultSettingsMigration(
178 version: initialMigrationVersion,
@@ -178,7 +182,9 @@ Future<void> initialSetup(
182 walletInfoSource: walletInfoSource,
183 nodeSource: nodes,
184 contactSource: contactSource,
181 - tradesSource: tradesSource);
185 + tradesSource: tradesSource,
186 + templates: templates,
187 + exchangeTemplates: exchangeTemplates);
188 await bootstrap(fiatConvertationService: fiatConvertationService);
189 monero_wallet.onStartup();
190 }
lib/palette.dart
+5
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
3 class Palette {
4 static const Color green = Color.fromRGBO(39, 206, 80, 1.0);
5 static const Color red = Color.fromRGBO(255, 51, 51, 1.0);
6 + static const Color darkRed = Color.fromRGBO(204, 38, 38, 1.0);
7 static const Color blueAlice = Color.fromRGBO(231, 240, 253, 1.0);
8 static const Color lightBlue = Color.fromRGBO(172, 203, 238, 1.0);
9 static const Color lavender = Color.fromRGBO(237, 245, 252, 1.0);
@@ -41,6 +42,10 @@ class PaletteDark {
42 static const Color oceanBlue = Color.fromRGBO(27, 39, 71, 1.0);
43 static const Color lightNightBlue = Color.fromRGBO(39, 52, 89, 1.0);
44 static const Color wildBlue = Color.fromRGBO(165, 176, 205, 1.0);
45 + static const Color buttonNightBlue = Color.fromRGBO(46, 57, 96, 1.0);
46 + static const Color lightBlueGrey = Color.fromRGBO(125, 141, 183, 1.0);
47 + static const Color lightVioletBlue = Color.fromRGBO(56, 71, 109, 1.0);
48 + static const Color darkVioletBlue = Color.fromRGBO(49, 60, 96, 1.0);
49
50 // FIXME: Rename.
51 static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
lib/router.dart
+1 -6
@@ -258,12 +258,7 @@ class Router {
258
259 case Routes.sendTemplate:
260 return CupertinoPageRoute<void>(
261 - builder: (_) => Provider(
262 - create: (_) => SendStore(
263 - walletService: walletService,
264 - priceStore: priceStore,
265 - transactionDescriptions: transactionDescriptions),
266 - child: SendTemplatePage()));
261 + fullscreenDialog: true, builder: (_) => getIt.get<SendTemplatePage>());
262
263 case Routes.receive:
264 return CupertinoPageRoute<void>(
lib/src/screens/base_page.dart
+5 -3
@@ -23,9 +23,11 @@ abstract class BasePage extends StatelessWidget {
23
24 Widget Function(BuildContext, Widget) get rootWrapper => null;
25
26 - final _backArrowImage = Image.asset('assets/images/back_arrow.png');
26 + final _backArrowImage = Image.asset('assets/images/back_arrow.png',
27 + color: Colors.white);
28 final _backArrowImageDarkTheme =
28 - Image.asset('assets/images/back_arrow_dark_theme.png');
29 + Image.asset('assets/images/back_arrow_dark_theme.png',
30 + color: Colors.white);
31 final _closeButtonImage = Image.asset('assets/images/close_button.png');
32 final _closeButtonImageDarkTheme =
33 Image.asset('assets/images/close_button_dark_theme.png');
@@ -71,7 +73,7 @@ abstract class BasePage extends StatelessWidget {
73 style: TextStyle(
74 fontSize: 18.0,
75 fontWeight: FontWeight.bold,
74 - color: Theme.of(context).primaryTextTheme.title.color),
76 + color: Colors.white),
77 );
78 }
79
lib/src/screens/send/send_page.dart
+6 -557
@@ -1,39 +1,9 @@
1 -import 'package:cake_wallet/core/address_validator.dart';
2 -import 'package:cake_wallet/core/amount_validator.dart';
3 -import 'package:cake_wallet/src/screens/auth/auth_page.dart';
4 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
1 import 'package:cake_wallet/view_model/send_view_model.dart';
2 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart';
8 -import 'package:flutter/services.dart';
9 -import 'package:flutter_mobx/flutter_mobx.dart';
10 -import 'package:mobx/mobx.dart';
11 -import 'package:provider/provider.dart';
4 import 'package:cake_wallet/palette.dart';
13 -import 'package:cake_wallet/routes.dart';
14 -import 'package:cake_wallet/src/widgets/address_text_field.dart';
15 -import 'package:cake_wallet/src/widgets/primary_button.dart';
16 -import 'package:cake_wallet/src/stores/settings/settings_store.dart';
17 -import 'package:cake_wallet/src/stores/balance/balance_store.dart';
18 -import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
19 -import 'package:cake_wallet/src/stores/send/send_store.dart';
20 -
21 -//import 'package:cake_wallet/src/stores/send/sending_state.dart';
5 import 'package:cake_wallet/src/screens/base_page.dart';
23 -import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
24 -import 'package:cake_wallet/src/domain/common/calculate_estimated_fee.dart';
25 -import 'package:cake_wallet/generated/i18n.dart';
26 -import 'package:cake_wallet/src/domain/common/sync_status.dart';
27 -import 'package:cake_wallet/src/stores/sync/sync_store.dart';
28 -import 'package:cake_wallet/src/widgets/top_panel.dart';
29 -import 'package:dotted_border/dotted_border.dart';
30 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
31 -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
32 -import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
33 -import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
34 -import 'package:cake_wallet/src/widgets/template_tile.dart';
35 -import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
36 -import 'package:cake_wallet/src/widgets/trail_button.dart';
6 +import 'package:cake_wallet/src/screens/send/widgets/base_send_widget.dart';
7
8 class SendPage extends BasePage {
9 SendPage({@required this.sendViewModel});
@@ -41,539 +11,18 @@ class SendPage extends BasePage {
11 final SendViewModel sendViewModel;
12
13 @override
44 - String get title => S.current.send_title;
14 + String get title => sendViewModel.pageTitle;
15
16 @override
47 - Color get backgroundLightColor => Palette.lavender;
17 + Color get backgroundLightColor => PaletteDark.nightBlue;
18
19 @override
50 - Color get backgroundDarkColor => PaletteDark.lightNightBlue;
20 + Color get backgroundDarkColor => PaletteDark.nightBlue;
21
22 @override
23 bool get resizeToAvoidBottomPadding => false;
24
25 @override
56 - Widget trailing(context) {
57 -// final sendStore = Provider.of<SendStore>(context);
58 -
59 - return TrailButton(caption: S.of(context).clear, onPressed: () => null);
60 - }
61 -
62 - @override
63 - Widget body(BuildContext context) => SendForm(sendViewModel: sendViewModel);
64 -}
65 -
66 -class SendForm extends StatefulWidget {
67 - SendForm({this.sendViewModel});
68 -
69 - final SendViewModel sendViewModel;
70 -
71 - @override
72 - State<StatefulWidget> createState() => SendFormState();
73 -}
74 -
75 -class SendFormState extends State<SendForm> {
76 - final _addressController = TextEditingController();
77 - final _cryptoAmountController = TextEditingController();
78 - final _fiatAmountController = TextEditingController();
79 -
80 - final _focusNode = FocusNode();
81 -
82 - bool _effectsInstalled = false;
83 -
84 - final _formKey = GlobalKey<FormState>();
85 -
86 - @override
87 - void initState() {
88 - _focusNode.addListener(() {
89 - if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
90 - getOpenaliasRecord(context);
91 - }
92 - });
93 -
94 - super.initState();
95 - }
96 -
97 - Future<void> getOpenaliasRecord(BuildContext context) async {
98 - final sendStore = Provider.of<SendStore>(context);
99 - final isOpenalias =
100 - await sendStore.isOpenaliasRecord(_addressController.text);
101 -
102 - if (isOpenalias) {
103 - _addressController.text = sendStore.recordAddress;
104 -
105 - await showDialog<void>(
106 - context: context,
107 - builder: (BuildContext context) {
108 - return AlertWithOneAction(
109 - alertTitle: S.of(context).openalias_alert_title,
110 - alertContent:
111 - S.of(context).openalias_alert_content(sendStore.recordName),
112 - buttonText: S.of(context).ok,
113 - buttonAction: () => Navigator.of(context).pop());
114 - });
115 - }
116 - }
117 -
118 - @override
119 - Widget build(BuildContext context) {
120 -// final settingsStore = Provider.of<SettingsStore>(context);
121 -// final sendStore = Provider.of<SendStore>(context);
122 -// sendStore.settingsStore = settingsStore;
123 -// final balanceStore = Provider.of<BalanceStore>(context);
124 -// final walletStore = Provider.of<WalletStore>(context);
125 -// final syncStore = Provider.of<SyncStore>(context);
126 -// final sendTemplateStore = Provider.of<SendTemplateStore>(context);
127 -
128 - _setEffects(context);
129 -
130 - return Container(
131 - color: Theme.of(context).backgroundColor,
132 - child: ScrollableWithBottomSection(
133 - contentPadding: EdgeInsets.only(bottom: 24),
134 - content: Column(
135 - children: <Widget>[
136 - TopPanel(
137 - color: Theme.of(context).accentTextTheme.title.backgroundColor,
138 - widget: Form(
139 - key: _formKey,
140 - child: Column(children: <Widget>[
141 - AddressTextField(
142 - controller: _addressController,
143 - placeholder: S.of(context).send_monero_address,
144 - focusNode: _focusNode,
145 - onURIScanned: (uri) {
146 - var address = '';
147 - var amount = '';
148 -
149 - if (uri != null) {
150 - address = uri.path;
151 - amount = uri.queryParameters['tx_amount'];
152 - } else {
153 - address = uri.toString();
154 - }
155 -
156 - _addressController.text = address;
157 - _cryptoAmountController.text = amount;
158 - },
159 - options: [
160 - AddressTextFieldOption.qrCode,
161 - AddressTextFieldOption.addressBook
162 - ],
163 - buttonColor: Theme.of(context).accentTextTheme.title.color,
164 - validator: widget.sendViewModel.addressValidator,
165 - ),
166 - Observer(builder: (_) {
167 - return Padding(
168 - padding: const EdgeInsets.only(top: 20),
169 - child: TextFormField(
170 - style: TextStyle(
171 - fontSize: 16.0,
172 - color: Theme.of(context)
173 - .primaryTextTheme
174 - .title
175 - .color),
176 - controller: _cryptoAmountController,
177 - keyboardType: TextInputType.numberWithOptions(
178 - signed: false, decimal: true),
179 - inputFormatters: [
180 - BlacklistingTextInputFormatter(
181 - RegExp('[\\-|\\ |\\,]'))
182 - ],
183 - decoration: InputDecoration(
184 - prefixIcon: Padding(
185 - padding: EdgeInsets.only(top: 12),
186 - child: Text('XMR:',
187 - style: TextStyle(
188 - fontSize: 16,
189 - fontWeight: FontWeight.w500,
190 - color: Theme.of(context)
191 - .primaryTextTheme
192 - .title
193 - .color,
194 - )),
195 - ),
196 - suffixIcon: Padding(
197 - padding: EdgeInsets.only(bottom: 5),
198 - child: Row(
199 - mainAxisSize: MainAxisSize.min,
200 - mainAxisAlignment:
201 - MainAxisAlignment.spaceBetween,
202 - children: <Widget>[
203 - Container(
204 - width:
205 - MediaQuery.of(context).size.width / 2,
206 - alignment: Alignment.centerLeft,
207 - child: Text(
208 - ' / ' + widget.sendViewModel.balance,
209 - maxLines: 1,
210 - overflow: TextOverflow.ellipsis,
211 - style: TextStyle(
212 - fontSize: 16,
213 - color: Theme.of(context)
214 - .primaryTextTheme
215 - .caption
216 - .color)),
217 - ),
218 - Container(
219 - height: 32,
220 - width: 32,
221 - margin: EdgeInsets.only(
222 - left: 12, bottom: 7, top: 4),
223 - decoration: BoxDecoration(
224 - color: Theme.of(context)
225 - .accentTextTheme
226 - .title
227 - .color,
228 - borderRadius: BorderRadius.all(
229 - Radius.circular(6))),
230 - child: InkWell(
231 - onTap: () => null,
232 - // widget.sendViewModel,
233 - child: Center(
234 - child: Text(S.of(context).all,
235 - textAlign: TextAlign.center,
236 - style: TextStyle(
237 - fontSize: 9,
238 - fontWeight: FontWeight.bold,
239 - color: Theme.of(context)
240 - .primaryTextTheme
241 - .caption
242 - .color)),
243 - ),
244 - ),
245 - )
246 - ],
247 - ),
248 - ),
249 - hintStyle: TextStyle(
250 - fontSize: 16.0,
251 - color: Theme.of(context)
252 - .primaryTextTheme
253 - .title
254 - .color),
255 - hintText: '0.0000',
256 - focusedBorder: UnderlineInputBorder(
257 - borderSide: BorderSide(
258 - color: Theme.of(context).dividerColor,
259 - width: 1.0)),
260 - enabledBorder: UnderlineInputBorder(
261 - borderSide: BorderSide(
262 - color: Theme.of(context).dividerColor,
263 - width: 1.0))),
264 - validator: widget.sendViewModel.amountValidator),
265 - );
266 - }),
267 - Padding(
268 - padding: const EdgeInsets.only(top: 20),
269 - child: TextFormField(
270 - style: TextStyle(
271 - fontSize: 16.0,
272 - color:
273 - Theme.of(context).primaryTextTheme.title.color),
274 - controller: _fiatAmountController,
275 - keyboardType: TextInputType.numberWithOptions(
276 - signed: false, decimal: true),
277 - inputFormatters: [
278 - BlacklistingTextInputFormatter(
279 - RegExp('[\\-|\\ |\\,]'))
280 - ],
281 - decoration: InputDecoration(
282 - prefixIcon: Padding(
283 - padding: EdgeInsets.only(top: 12),
284 - child: Text(
285 - '${widget.sendViewModel.fiat.toString()}:',
286 - style: TextStyle(
287 - fontSize: 16,
288 - fontWeight: FontWeight.w500,
289 - color: Theme.of(context)
290 - .primaryTextTheme
291 - .title
292 - .color,
293 - )),
294 - ),
295 - hintStyle: TextStyle(
296 - fontSize: 16.0,
297 - color: Theme.of(context)
298 - .primaryTextTheme
299 - .caption
300 - .color),
301 - hintText: '0.00',
302 - focusedBorder: UnderlineInputBorder(
303 - borderSide: BorderSide(
304 - color: Theme.of(context).dividerColor,
305 - width: 1.0)),
306 - enabledBorder: UnderlineInputBorder(
307 - borderSide: BorderSide(
308 - color: Theme.of(context).dividerColor,
309 - width: 1.0)))),
310 - ),
311 - Padding(
312 - padding: const EdgeInsets.only(top: 20),
313 - child: Row(
314 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
315 - children: <Widget>[
316 - Text(S.of(context).send_estimated_fee,
317 - style: TextStyle(
318 - fontSize: 12,
319 - fontWeight: FontWeight.w600,
320 - color: Theme.of(context)
321 - .primaryTextTheme
322 - .title
323 - .color,
324 - )),
325 - Text(
326 - '${widget.sendViewModel.estimatedFee} ${widget.sendViewModel.currency.toString()}',
327 - style: TextStyle(
328 - fontSize: 12,
329 - fontWeight: FontWeight.w600,
330 - color: Theme.of(context)
331 - .primaryTextTheme
332 - .title
333 - .color,
334 - ))
335 - ],
336 - ),
337 - )
338 - ]),
339 - ),
340 - ),
341 -// Padding(
342 -// padding: EdgeInsets.only(top: 32, left: 24, bottom: 24),
343 -// child: Row(
344 -// mainAxisAlignment: MainAxisAlignment.start,
345 -// children: <Widget>[
346 -// Text(
347 -// S.of(context).send_templates,
348 -// style: TextStyle(
349 -// fontSize: 18,
350 -// fontWeight: FontWeight.w600,
351 -// color:
352 -// Theme.of(context).primaryTextTheme.caption.color),
353 -// )
354 -// ],
355 -// ),
356 -// ),
357 -// Container(
358 -// height: 40,
359 -// width: double.infinity,
360 -// padding: EdgeInsets.only(left: 24),
361 -// child: Observer(builder: (_) {
362 -// final itemCount = sendTemplateStore.templates.length + 1;
363 -//
364 -// return ListView.builder(
365 -// scrollDirection: Axis.horizontal,
366 -// itemCount: itemCount,
367 -// itemBuilder: (context, index) {
368 -// if (index == 0) {
369 -// return GestureDetector(
370 -// onTap: () => Navigator.of(context)
371 -// .pushNamed(Routes.sendTemplate),
372 -// child: Container(
373 -// padding: EdgeInsets.only(right: 10),
374 -// child: DottedBorder(
375 -// borderType: BorderType.RRect,
376 -// dashPattern: [8, 4],
377 -// color: Theme.of(context)
378 -// .accentTextTheme
379 -// .title
380 -// .backgroundColor,
381 -// strokeWidth: 2,
382 -// radius: Radius.circular(20),
383 -// child: Container(
384 -// height: 40,
385 -// width: 75,
386 -// padding: EdgeInsets.only(left: 10, right: 10),
387 -// alignment: Alignment.center,
388 -// decoration: BoxDecoration(
389 -// borderRadius:
390 -// BorderRadius.all(Radius.circular(20)),
391 -// color: Colors.transparent,
392 -// ),
393 -// child: Text(
394 -// S.of(context).send_new,
395 -// style: TextStyle(
396 -// fontSize: 14,
397 -// fontWeight: FontWeight.w600,
398 -// color: Theme.of(context)
399 -// .primaryTextTheme
400 -// .caption
401 -// .color),
402 -// ),
403 -// )),
404 -// ),
405 -// );
406 -// }
407 -//
408 -// index -= 1;
409 -//
410 -// final template = sendTemplateStore.templates[index];
411 -//
412 -// return TemplateTile(
413 -// to: template.name,
414 -// amount: template.amount,
415 -// from: template.cryptoCurrency,
416 -// onTap: () {
417 -// _addressController.text = template.address;
418 -// _cryptoAmountController.text = template.amount;
419 -// getOpenaliasRecord(context);
420 -// });
421 -// });
422 -// }),
423 -// )
424 - ],
425 - ),
426 - bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
427 - bottomSection: Observer(builder: (_) {
428 - return LoadingPrimaryButton(
429 - onPressed: () => null,
430 -// syncStore.status is SyncedSyncStatus
431 -// ? () async {
432 -// // Hack. Don't ask me.
433 -// FocusScope.of(context).requestFocus(FocusNode());
434 -//
435 -// if (_formKey.currentState.validate()) {
436 -// await showDialog<void>(
437 -// context: context,
438 -// builder: (dialogContext) {
439 -// return AlertWithTwoActions(
440 -// alertTitle:
441 -// S.of(context).send_creating_transaction,
442 -// alertContent: S.of(context).confirm_sending,
443 -// leftButtonText: S.of(context).send,
444 -// rightButtonText: S.of(context).cancel,
445 -// actionLeftButton: () async {
446 -// await Navigator.of(dialogContext)
447 -// .popAndPushNamed(Routes.auth, arguments:
448 -// (bool isAuthenticatedSuccessfully,
449 -// AuthPageState auth) {
450 -// if (!isAuthenticatedSuccessfully) {
451 -// return;
452 -// }
453 -//
454 -// Navigator.of(auth.context).pop();
455 -//
456 -// sendStore.createTransaction(
457 -// address: _addressController.text,
458 -// paymentId: '');
459 -// });
460 -// },
461 -// actionRightButton: () =>
462 -// Navigator.of(context).pop());
463 -// });
464 -// }
465 -// }
466 -// : null,
467 - text: S.of(context).send,
468 - color: Colors.blue,
469 - textColor: Colors.white,
470 - isLoading: widget.sendViewModel.state is TransactionIsCreating ||
471 - widget.sendViewModel.state is TransactionCommitting,
472 - isDisabled:
473 - false // FIXME !(syncStore.status is SyncedSyncStatus),
474 - );
475 - }),
476 - ),
477 - );
478 - }
479 -
480 - void _setEffects(BuildContext context) {
481 - if (_effectsInstalled) {
482 - return;
483 - }
484 -
485 -// reaction((_) => widget.sendViewModel.fiatAmount, (String amount) {
486 -// if (amount != _fiatAmountController.text) {
487 -// _fiatAmountController.text = amount;
488 -// }
489 -// });
490 -//
491 -// reaction((_) => widget.sendViewModel.cryptoAmount, (String amount) {
492 -// if (amount != _cryptoAmountController.text) {
493 -// _cryptoAmountController.text = amount;
494 -// }
495 -// });
496 -//
497 -// reaction((_) => widget.sendViewModel.address, (String address) {
498 -// if (address != _addressController.text) {
499 -// _addressController.text = address;
500 -// }
501 -// });
502 -//
503 -// _addressController.addListener(() {
504 -// final address = _addressController.text;
505 -//
506 -// if (widget.sendViewModel.address != address) {
507 -// widget.sendViewModel.changeAddress(address);
508 -// }
509 -// });
510 -
511 -// _fiatAmountController.addListener(() {
512 -// final fiatAmount = _fiatAmountController.text;
513 -//
514 -// if (sendStore.fiatAmount != fiatAmount) {
515 -// sendStore.changeFiatAmount(fiatAmount);
516 -// }
517 -// });
518 -
519 -// _cryptoAmountController.addListener(() {
520 -// final cryptoAmount = _cryptoAmountController.text;
521 -//
522 -// if (sendStore.cryptoAmount != cryptoAmount) {
523 -// sendStore.changeCryptoAmount(cryptoAmount);
524 -// }
525 -// });
526 -
527 - reaction((_) => widget.sendViewModel.state, (SendViewModelState state) {
528 - if (state is SendingFailed) {
529 - WidgetsBinding.instance.addPostFrameCallback((_) {
530 - showDialog<void>(
531 - context: context,
532 - builder: (BuildContext context) {
533 - return AlertWithOneAction(
534 - alertTitle: S.of(context).error,
535 - alertContent: state.error,
536 - buttonText: S.of(context).ok,
537 - buttonAction: () => Navigator.of(context).pop());
538 - });
539 - });
540 - }
541 -
542 - if (state is TransactionCreatedSuccessfully) {
543 -// WidgetsBinding.instance.addPostFrameCallback((_) {
544 -// showDialog<void>(
545 -// context: context,
546 -// builder: (BuildContext context) {
547 -// return ConfirmSendingAlert(
548 -// alertTitle: S.of(context).confirm_sending,
549 -// amount: S.of(context).send_amount,
550 -// amountValue: sendStore.pendingTransaction.amount,
551 -// fee: S.of(context).send_fee,
552 -// feeValue: sendStore.pendingTransaction.fee,
553 -// leftButtonText: S.of(context).ok,
554 -// rightButtonText: S.of(context).cancel,
555 -// actionLeftButton: () {
556 -// Navigator.of(context).pop();
557 -// sendStore.commitTransaction();
558 -// showDialog<void>(
559 -// context: context,
560 -// builder: (BuildContext context) {
561 -// return SendingAlert(sendStore: sendStore);
562 -// });
563 -// },
564 -// actionRightButton: () => Navigator.of(context).pop());
565 -// });
566 -// });
567 - }
568 -
569 - if (state is TransactionCommitted) {
570 - WidgetsBinding.instance.addPostFrameCallback((_) {
571 - _addressController.text = '';
572 - _cryptoAmountController.text = '';
573 - });
574 - }
575 - });
576 -
577 - _effectsInstalled = true;
578 - }
26 + Widget body(BuildContext context) =>
27 + BaseSendWidget(sendViewModel: sendViewModel);
28 }
lib/src/screens/send/send_template_page.dart
+10 -258
@@ -1,277 +1,29 @@
1 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
1 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/material.dart';
4 -import 'package:flutter/services.dart';
5 -import 'package:flutter_mobx/flutter_mobx.dart';
6 -import 'package:mobx/mobx.dart';
7 -import 'package:provider/provider.dart';
3 import 'package:cake_wallet/palette.dart';
9 -import 'package:cake_wallet/src/widgets/address_text_field.dart';
10 -import 'package:cake_wallet/src/widgets/primary_button.dart';
11 -import 'package:cake_wallet/src/stores/settings/settings_store.dart';
12 -import 'package:cake_wallet/src/stores/balance/balance_store.dart';
13 -import 'package:cake_wallet/src/stores/send/send_store.dart';
4 import 'package:cake_wallet/src/screens/base_page.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
16 -import 'package:cake_wallet/src/widgets/top_panel.dart';
17 -import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
6 +import 'package:cake_wallet/view_model/send_view_model.dart';
7 +import 'package:cake_wallet/src/screens/send/widgets/base_send_widget.dart';
8
9 class SendTemplatePage extends BasePage {
20 - @override
21 - String get title => S.current.send_title;
10 + SendTemplatePage({@required this.sendViewModel});
11
23 - @override
24 - Color get backgroundLightColor => Palette.lavender;
12 + final SendViewModel sendViewModel;
13
14 @override
27 - Color get backgroundDarkColor => PaletteDark.lightNightBlue;
15 + String get title => S.current.exchange_new_template;
16
17 @override
30 - bool get resizeToAvoidBottomPadding => false;
18 + Color get backgroundLightColor => PaletteDark.nightBlue;
19
20 @override
33 - Widget body(BuildContext context) => SendTemplateForm();
34 -}
21 + Color get backgroundDarkColor => PaletteDark.nightBlue;
22
36 -class SendTemplateForm extends StatefulWidget {
23 @override
38 - SendTemplateFormState createState() => SendTemplateFormState();
39 -}
40 -
41 -class SendTemplateFormState extends State<SendTemplateForm> {
42 - final _nameController = TextEditingController();
43 - final _addressController = TextEditingController();
44 - final _cryptoAmountController = TextEditingController();
45 - final _fiatAmountController = TextEditingController();
46 -
47 - final _formKey = GlobalKey<FormState>();
48 -
49 - bool _effectsInstalled = false;
50 -
51 - @override
52 - void dispose() {
53 - _nameController.dispose();
54 - _addressController.dispose();
55 - _cryptoAmountController.dispose();
56 - _fiatAmountController.dispose();
57 - super.dispose();
58 - }
24 + bool get resizeToAvoidBottomPadding => false;
25
26 @override
61 - Widget build(BuildContext context) {
62 - final settingsStore = Provider.of<SettingsStore>(context);
63 - final balanceStore = Provider.of<BalanceStore>(context);
64 - final sendStore = Provider.of<SendStore>(context);
65 - sendStore.settingsStore = settingsStore;
66 - final sendTemplateStore = Provider.of<SendTemplateStore>(context);
67 -
68 - _setEffects(context);
69 -
70 - return Container(
71 - color: Theme.of(context).backgroundColor,
72 - child: ScrollableWithBottomSection(
73 - contentPadding: EdgeInsets.only(bottom: 24),
74 - content: Column(
75 - children: <Widget>[
76 - TopPanel(
77 - color: Theme.of(context).accentTextTheme.title.backgroundColor,
78 - widget: Form(
79 - key: _formKey,
80 - child: Column(children: <Widget>[
81 - TextFormField(
82 - style: TextStyle(
83 - fontSize: 16.0,
84 - color: Theme.of(context).primaryTextTheme.title.color),
85 - controller: _nameController,
86 - decoration: InputDecoration(
87 - hintStyle: TextStyle(
88 - fontSize: 16.0,
89 - color: Theme.of(context).primaryTextTheme.caption.color),
90 - hintText: S.of(context).send_name,
91 - focusedBorder: UnderlineInputBorder(
92 - borderSide: BorderSide(
93 - color: Theme.of(context).dividerColor,
94 - width: 1.0)),
95 - enabledBorder: UnderlineInputBorder(
96 - borderSide: BorderSide(
97 - color: Theme.of(context).dividerColor,
98 - width: 1.0))),
99 - validator: (value) {
100 - sendTemplateStore.validateTemplate(value);
101 - return sendTemplateStore.errorMessage;
102 - },
103 - ),
104 - Padding(
105 - padding: EdgeInsets.only(top: 20),
106 - child: AddressTextField(
107 - controller: _addressController,
108 - placeholder: S.of(context).send_monero_address,
109 - onURIScanned: (uri) {
110 - var address = '';
111 - var amount = '';
112 -
113 - if (uri != null) {
114 - address = uri.path;
115 - amount = uri.queryParameters['tx_amount'];
116 - } else {
117 - address = uri.toString();
118 - }
119 -
120 - _addressController.text = address;
121 - _cryptoAmountController.text = amount;
122 - },
123 - options: [
124 - AddressTextFieldOption.qrCode,
125 - AddressTextFieldOption.addressBook
126 - ],
127 - buttonColor: Theme.of(context).accentTextTheme.title.color,
128 - validator: (value) {
129 - sendTemplateStore.validateTemplate(value);
130 - return sendTemplateStore.errorMessage;
131 - },
132 - ),
133 - ),
134 - Observer(
135 - builder: (_) {
136 - return Padding(
137 - padding: const EdgeInsets.only(top: 20),
138 - child: TextFormField(
139 - style: TextStyle(
140 - fontSize: 16.0,
141 - color: Theme.of(context).primaryTextTheme.title.color
142 - ),
143 - controller: _cryptoAmountController,
144 - keyboardType: TextInputType.numberWithOptions(
145 - signed: false, decimal: true),
146 - inputFormatters: [
147 - BlacklistingTextInputFormatter(
148 - RegExp('[\\-|\\ |\\,]'))
149 - ],
150 - decoration: InputDecoration(
151 - prefixIcon: Padding(
152 - padding: EdgeInsets.only(top: 12),
153 - child: Text('XMR:',
154 - style: TextStyle(
155 - fontSize: 16,
156 - fontWeight: FontWeight.bold,
157 - color: Theme.of(context).primaryTextTheme.title.color,
158 - )),
159 - ),
160 - hintStyle: TextStyle(
161 - fontSize: 16.0,
162 - color: Theme.of(context).primaryTextTheme.title.color),
163 - hintText: '0.0000',
164 - focusedBorder: UnderlineInputBorder(
165 - borderSide: BorderSide(
166 - color: Theme.of(context).dividerColor,
167 - width: 1.0)),
168 - enabledBorder: UnderlineInputBorder(
169 - borderSide: BorderSide(
170 - color: Theme.of(context).dividerColor,
171 - width: 1.0))),
172 - ),
173 - );
174 - }
175 - ),
176 - Padding(
177 - padding: const EdgeInsets.only(top: 20),
178 - child: TextFormField(
179 - style: TextStyle(
180 - fontSize: 16.0,
181 - color: Theme.of(context).primaryTextTheme.title.color),
182 - controller: _fiatAmountController,
183 - keyboardType: TextInputType.numberWithOptions(
184 - signed: false, decimal: true),
185 - inputFormatters: [
186 - BlacklistingTextInputFormatter(
187 - RegExp('[\\-|\\ |\\,]'))
188 - ],
189 - decoration: InputDecoration(
190 - prefixIcon: Padding(
191 - padding: EdgeInsets.only(top: 12),
192 - child: Text(
193 - '${settingsStore.fiatCurrency.toString()}:',
194 - style: TextStyle(
195 - fontSize: 16,
196 - fontWeight: FontWeight.bold,
197 - color: Theme.of(context).primaryTextTheme.title.color,
198 - )),
199 - ),
200 - hintStyle: TextStyle(
201 - fontSize: 16.0,
202 - color: Theme.of(context).primaryTextTheme.caption.color),
203 - hintText: '0.00',
204 - focusedBorder: UnderlineInputBorder(
205 - borderSide: BorderSide(
206 - color: Theme.of(context).dividerColor,
207 - width: 1.0)),
208 - enabledBorder: UnderlineInputBorder(
209 - borderSide: BorderSide(
210 - color: Theme.of(context).dividerColor,
211 - width: 1.0)))),
212 - ),
213 - ]),
214 - ),
215 - ),
216 - ],
217 - ),
218 - bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
219 - bottomSection: PrimaryButton(
220 - onPressed: () {
221 - if (_formKey.currentState.validate()) {
222 - sendTemplateStore.addTemplate(
223 - name: _nameController.text,
224 - address: _addressController.text,
225 - cryptoCurrency: 'XMR',
226 - amount: _cryptoAmountController.text
227 - );
228 - sendTemplateStore.update();
229 - Navigator.of(context).pop();
230 - }
231 - },
232 - text: S.of(context).save,
233 - color: Colors.blue,
234 - textColor: Colors.white
235 - ),
236 - ),
237 - );
238 - }
239 -
240 - void _setEffects(BuildContext context) {
241 - if (_effectsInstalled) {
242 - return;
243 - }
244 -
245 - final sendStore = Provider.of<SendStore>(context);
246 -
247 - reaction((_) => sendStore.fiatAmount, (String amount) {
248 - if (amount != _fiatAmountController.text) {
249 - _fiatAmountController.text = amount;
250 - }
251 - });
252 -
253 - reaction((_) => sendStore.cryptoAmount, (String amount) {
254 - if (amount != _cryptoAmountController.text) {
255 - _cryptoAmountController.text = amount;
256 - }
257 - });
258 -
259 - _fiatAmountController.addListener(() {
260 - final fiatAmount = _fiatAmountController.text;
261 -
262 - if (sendStore.fiatAmount != fiatAmount) {
263 - sendStore.changeFiatAmount(fiatAmount);
264 - }
265 - });
266 -
267 - _cryptoAmountController.addListener(() {
268 - final cryptoAmount = _cryptoAmountController.text;
269 -
270 - if (sendStore.cryptoAmount != cryptoAmount) {
271 - sendStore.changeCryptoAmount(cryptoAmount);
272 - }
273 - });
274 -
275 - _effectsInstalled = true;
276 - }
27 + Widget body(BuildContext context) =>
28 + BaseSendWidget(sendViewModel: sendViewModel, isTemplate: true);
29 }
\ No newline at end of file
lib/src/screens/send/widgets/base_send_widget.dart new
+541
@@ -0,0 +1,541 @@
1 +import 'package:cake_wallet/src/widgets/primary_button.dart';
2 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
3 +import 'package:flutter/cupertino.dart';
4 +import 'package:flutter/material.dart';
5 +import 'package:cake_wallet/view_model/send_view_model.dart';
6 +import 'package:flutter/services.dart';
7 +import 'package:flutter_mobx/flutter_mobx.dart';
8 +import 'package:mobx/mobx.dart';
9 +import 'package:cake_wallet/palette.dart';
10 +import 'package:cake_wallet/src/widgets/address_text_field.dart';
11 +import 'package:cake_wallet/generated/i18n.dart';
12 +import 'package:cake_wallet/src/widgets/top_panel.dart';
13 +import 'package:dotted_border/dotted_border.dart';
14 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
15 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
16 +import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
17 +import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
18 +import 'package:cake_wallet/src/widgets/template_tile.dart';
19 +import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
20 +import 'package:cake_wallet/routes.dart';
21 +
22 +class BaseSendWidget extends StatelessWidget {
23 + BaseSendWidget({
24 + @required this.sendViewModel,
25 + this.isTemplate = false
26 + });
27 +
28 + final SendViewModel sendViewModel;
29 + final bool isTemplate;
30 +
31 + final _addressController = TextEditingController();
32 + final _cryptoAmountController = TextEditingController();
33 + final _fiatAmountController = TextEditingController();
34 + final _nameController = TextEditingController();
35 + final _focusNode = FocusNode();
36 + final _formKey = GlobalKey<FormState>();
37 +
38 + bool _effectsInstalled = false;
39 +
40 + @override
41 + Widget build(BuildContext context) {
42 +
43 + _setEffects(context);
44 +
45 + return Container(
46 + color: PaletteDark.backgroundColor,
47 + child: ScrollableWithBottomSection(
48 + contentPadding: EdgeInsets.only(bottom: 24),
49 + content: Column(
50 + children: <Widget>[
51 + TopPanel(
52 + color: PaletteDark.nightBlue,
53 + edgeInsets: EdgeInsets.fromLTRB(24, 24, 24, 32),
54 + widget: Form(
55 + key: _formKey,
56 + child: Column(children: <Widget>[
57 + isTemplate
58 + ? BaseTextFormField(
59 + controller: _nameController,
60 + hintText: S.of(context).send_name,
61 + borderColor: PaletteDark.lightVioletBlue,
62 + textStyle: TextStyle(
63 + fontSize: 14,
64 + fontWeight: FontWeight.w500,
65 + color: Colors.white
66 + ),
67 + placeholderTextStyle: TextStyle(
68 + color: PaletteDark.darkCyanBlue,
69 + fontWeight: FontWeight.w500,
70 + fontSize: 14),
71 + validator: sendViewModel.templateValidator,
72 + )
73 + : Offstage(),
74 + Padding(
75 + padding: EdgeInsets.only(top: isTemplate ? 20 : 0),
76 + child: AddressTextField(
77 + controller: _addressController,
78 + placeholder: S.of(context).send_address(
79 + sendViewModel.cryptoCurrencyTitle),
80 + focusNode: _focusNode,
81 + onURIScanned: (uri) {
82 + var address = '';
83 + var amount = '';
84 +
85 + if (uri != null) {
86 + address = uri.path;
87 + amount = uri.queryParameters['tx_amount'];
88 + } else {
89 + address = uri.toString();
90 + }
91 +
92 + _addressController.text = address;
93 + _cryptoAmountController.text = amount;
94 + },
95 + options: [
96 + AddressTextFieldOption.paste,
97 + AddressTextFieldOption.qrCode,
98 + AddressTextFieldOption.addressBook
99 + ],
100 + buttonColor: PaletteDark.buttonNightBlue,
101 + borderColor: PaletteDark.lightVioletBlue,
102 + textStyle: TextStyle(
103 + fontSize: 14,
104 + fontWeight: FontWeight.w500,
105 + color: Colors.white
106 + ),
107 + hintStyle: TextStyle(
108 + fontSize: 14,
109 + fontWeight: FontWeight.w500,
110 + color: PaletteDark.darkCyanBlue
111 + ),
112 + validator: sendViewModel.addressValidator,
113 + ),
114 + ),
115 + Observer(
116 + builder: (_) {
117 + return Padding(
118 + padding: const EdgeInsets.only(top: 20),
119 + child: BaseTextFormField(
120 + controller: _cryptoAmountController,
121 + keyboardType: TextInputType.numberWithOptions(
122 + signed: false, decimal: true),
123 + inputFormatters: [
124 + BlacklistingTextInputFormatter(
125 + RegExp('[\\-|\\ |\\,]'))
126 + ],
127 + prefixIcon: Padding(
128 + padding: EdgeInsets.only(top: 9),
129 + child: Text(sendViewModel.currency.title + ':',
130 + style: TextStyle(
131 + fontSize: 16,
132 + fontWeight: FontWeight.w600,
133 + color: Colors.white,
134 + )),
135 + ),
136 + suffixIcon: isTemplate
137 + ? Offstage()
138 + : Padding(
139 + padding: EdgeInsets.only(bottom: 2),
140 + child: Row(
141 + mainAxisSize: MainAxisSize.min,
142 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
143 + children: <Widget>[
144 + Container(
145 + width: MediaQuery.of(context).size.width/2,
146 + alignment: Alignment.centerLeft,
147 + child: Text(
148 + ' / ' + sendViewModel.balance,
149 + maxLines: 1,
150 + overflow: TextOverflow.ellipsis,
151 + style: TextStyle(
152 + fontSize: 14,
153 + color: PaletteDark.darkCyanBlue
154 + )
155 + ),
156 + ),
157 + Container(
158 + height: 34,
159 + width: 34,
160 + margin: EdgeInsets.only(left: 12, bottom: 8),
161 + decoration: BoxDecoration(
162 + color: PaletteDark.buttonNightBlue,
163 + borderRadius: BorderRadius.all(Radius.circular(6))
164 + ),
165 + child: InkWell(
166 + onTap: () => sendViewModel.setSendAll(),
167 + child: Center(
168 + child: Text(S.of(context).all,
169 + textAlign: TextAlign.center,
170 + style: TextStyle(
171 + fontSize: 12,
172 + fontWeight: FontWeight.bold,
173 + color: PaletteDark.lightBlueGrey
174 + )
175 + ),
176 + ),
177 + ),
178 + )
179 + ],
180 + ),
181 + ),
182 + hintText: '0.0000',
183 + borderColor: PaletteDark.lightVioletBlue,
184 + textStyle: TextStyle(
185 + fontSize: 14,
186 + fontWeight: FontWeight.w500,
187 + color: Colors.white
188 + ),
189 + placeholderTextStyle: TextStyle(
190 + color: PaletteDark.darkCyanBlue,
191 + fontWeight: FontWeight.w500,
192 + fontSize: 14),
193 + validator: sendViewModel.amountValidator
194 + )
195 + );
196 + }
197 + ),
198 + Padding(
199 + padding: const EdgeInsets.only(top: 20),
200 + child: BaseTextFormField(
201 + controller: _fiatAmountController,
202 + keyboardType: TextInputType.numberWithOptions(
203 + signed: false, decimal: true),
204 + inputFormatters: [
205 + BlacklistingTextInputFormatter(
206 + RegExp('[\\-|\\ |\\,]'))
207 + ],
208 + prefixIcon: Padding(
209 + padding: EdgeInsets.only(top: 9),
210 + child: Text(
211 + sendViewModel.fiat.title + ':',
212 + style: TextStyle(
213 + fontSize: 16,
214 + fontWeight: FontWeight.w600,
215 + color: Colors.white,
216 + )),
217 + ),
218 + hintText: '0.00',
219 + borderColor: PaletteDark.lightVioletBlue,
220 + textStyle: TextStyle(
221 + fontSize: 14,
222 + fontWeight: FontWeight.w500,
223 + color: Colors.white
224 + ),
225 + placeholderTextStyle: TextStyle(
226 + color: PaletteDark.darkCyanBlue,
227 + fontWeight: FontWeight.w500,
228 + fontSize: 14),
229 + )
230 + ),
231 + isTemplate
232 + ? Offstage()
233 + : Padding(
234 + padding: const EdgeInsets.only(top: 24),
235 + child: Row(
236 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
237 + children: <Widget>[
238 + Text(S.of(context).send_estimated_fee,
239 + style: TextStyle(
240 + fontSize: 12,
241 + fontWeight: FontWeight.w500,
242 + color: Colors.white,
243 + )),
244 + Text(
245 + sendViewModel.estimatedFee.toString() + ' '
246 + + sendViewModel.currency.title,
247 + style: TextStyle(
248 + fontSize: 12,
249 + fontWeight: FontWeight.w600,
250 + color: Colors.white,
251 + ))
252 + ],
253 + ),
254 + )
255 + ]),
256 + ),
257 + ),
258 + isTemplate
259 + ? Offstage()
260 + : Padding(
261 + padding: EdgeInsets.only(
262 + top: 30,
263 + left: 24,
264 + bottom: 24
265 + ),
266 + child: Row(
267 + mainAxisAlignment: MainAxisAlignment.start,
268 + children: <Widget>[
269 + Text(
270 + S.of(context).send_templates,
271 + style: TextStyle(
272 + fontSize: 18,
273 + fontWeight: FontWeight.w600,
274 + color: PaletteDark.darkCyanBlue
275 + ),
276 + )
277 + ],
278 + ),
279 + ),
280 + isTemplate
281 + ? Offstage()
282 + : Container(
283 + height: 40,
284 + width: double.infinity,
285 + padding: EdgeInsets.only(left: 24),
286 + child: SingleChildScrollView(
287 + scrollDirection: Axis.horizontal,
288 + child: Row(
289 + children: <Widget>[
290 + GestureDetector(
291 + onTap: () => Navigator.of(context)
292 + .pushNamed(Routes.sendTemplate),
293 + child: Container(
294 + padding: EdgeInsets.only(left: 1, right: 10),
295 + child: DottedBorder(
296 + borderType: BorderType.RRect,
297 + dashPattern: [6, 4],
298 + color: PaletteDark.darkCyanBlue,
299 + strokeWidth: 2,
300 + radius: Radius.circular(20),
301 + child: Container(
302 + height: 34,
303 + width: 75,
304 + padding: EdgeInsets.only(left: 10, right: 10),
305 + alignment: Alignment.center,
306 + decoration: BoxDecoration(
307 + borderRadius: BorderRadius.all(Radius.circular(20)),
308 + color: Colors.transparent,
309 + ),
310 + child: Text(
311 + S.of(context).send_new,
312 + style: TextStyle(
313 + fontSize: 14,
314 + fontWeight: FontWeight.w600,
315 + color: PaletteDark.darkCyanBlue
316 + ),
317 + ),
318 + )
319 + ),
320 + ),
321 + ),
322 + Observer(
323 + builder: (_) {
324 + final templates = sendViewModel.templates;
325 + final itemCount = templates.length;
326 +
327 + return ListView.builder(
328 + scrollDirection: Axis.horizontal,
329 + shrinkWrap: true,
330 + physics: NeverScrollableScrollPhysics(),
331 + itemCount: itemCount,
332 + itemBuilder: (context, index) {
333 + final template = templates[index];
334 +
335 + return TemplateTile(
336 + key: UniqueKey(),
337 + to: template.name,
338 + amount: template.amount,
339 + from: template.cryptoCurrency,
340 + onTap: () {
341 + _addressController.text = template.address;
342 + _cryptoAmountController.text = template.amount;
343 + getOpenaliasRecord(context);
344 + },
345 + onRemove: () {
346 + showDialog<void>(
347 + context: context,
348 + builder: (dialogContext) {
349 + return AlertWithTwoActions(
350 + alertTitle: S.of(context).template,
351 + alertContent: S.of(context).confirm_delete_template,
352 + leftButtonText: S.of(context).delete,
353 + rightButtonText: S.of(context).cancel,
354 + actionLeftButton: () {
355 + Navigator.of(dialogContext).pop();
356 + sendViewModel.sendTemplateStore.remove(template: template);
357 + sendViewModel.sendTemplateStore.update();
358 + },
359 + actionRightButton: () => Navigator.of(dialogContext).pop()
360 + );
361 + }
362 + );
363 + },
364 + );
365 + }
366 + );
367 + }
368 + )
369 + ],
370 + ),
371 + ),
372 + )
373 + ],
374 + ),
375 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
376 + bottomSection: isTemplate
377 + ? PrimaryButton(
378 + onPressed: () {
379 + if (_formKey.currentState.validate()) {
380 + sendViewModel.sendTemplateStore.addTemplate(
381 + name: _nameController.text,
382 + address: _addressController.text,
383 + cryptoCurrency: sendViewModel.currency.title,
384 + amount: _cryptoAmountController.text
385 + );
386 + sendViewModel.sendTemplateStore.update();
387 + Navigator.of(context).pop();
388 + }
389 + },
390 + text: S.of(context).save,
391 + color: Colors.green,
392 + textColor: Colors.white)
393 + : Observer(builder: (_) {
394 + return LoadingPrimaryButton(
395 + onPressed: () {
396 + if (_formKey.currentState.validate()) {
397 + print('SENT!!!');
398 + }
399 + },
400 + text: S.of(context).send,
401 + color: Colors.blue,
402 + textColor: Colors.white,
403 + isLoading: sendViewModel.state is TransactionIsCreating ||
404 + sendViewModel.state is TransactionCommitting,
405 + isDisabled:
406 + false // FIXME !(syncStore.status is SyncedSyncStatus),
407 + );
408 + }),
409 + ),
410 + );
411 + }
412 +
413 + void _setEffects(BuildContext context) {
414 + if (_effectsInstalled) {
415 + return;
416 + }
417 +
418 + reaction((_) => sendViewModel.fiatAmount, (String amount) {
419 + if (amount != _fiatAmountController.text) {
420 + _fiatAmountController.text = amount;
421 + }
422 + });
423 +
424 + reaction((_) => sendViewModel.cryptoAmount, (String amount) {
425 + if (amount != _cryptoAmountController.text) {
426 + _cryptoAmountController.text = amount;
427 + }
428 + });
429 +
430 + reaction((_) => sendViewModel.address, (String address) {
431 + if (address != _addressController.text) {
432 + _addressController.text = address;
433 + }
434 + });
435 +
436 + _addressController.addListener(() {
437 + final address = _addressController.text;
438 +
439 + if (sendViewModel.address != address) {
440 + sendViewModel.changeAddress(address);
441 + }
442 + });
443 +
444 + _fiatAmountController.addListener(() {
445 + final fiatAmount = _fiatAmountController.text;
446 +
447 + if (sendViewModel.fiatAmount != fiatAmount) {
448 + sendViewModel.changeFiatAmount(fiatAmount);
449 + }
450 + });
451 +
452 + _cryptoAmountController.addListener(() {
453 + final cryptoAmount = _cryptoAmountController.text;
454 +
455 + if (sendViewModel.cryptoAmount != cryptoAmount) {
456 + sendViewModel.changeCryptoAmount(cryptoAmount);
457 + }
458 + });
459 +
460 + _focusNode.addListener(() {
461 + if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
462 + getOpenaliasRecord(context);
463 + }
464 + });
465 +
466 + reaction((_) => sendViewModel.state, (SendViewModelState state) {
467 + if (state is SendingFailed) {
468 + WidgetsBinding.instance.addPostFrameCallback((_) {
469 + showDialog<void>(
470 + context: context,
471 + builder: (BuildContext context) {
472 + return AlertDialog(
473 + title: Text(S.of(context).error),
474 + content: Text(state.error),
475 + actions: <Widget>[
476 + FlatButton(
477 + child: Text(S.of(context).ok),
478 + onPressed: () => Navigator.of(context).pop())
479 + ],
480 + );
481 + });
482 + });
483 + }
484 +
485 + if (state is TransactionCreatedSuccessfully) {
486 +// WidgetsBinding.instance.addPostFrameCallback((_) {
487 +// showDialog<void>(
488 +// context: context,
489 +// builder: (BuildContext context) {
490 +// return ConfirmSendingAlert(
491 +// alertTitle: S.of(context).confirm_sending,
492 +// amount: S.of(context).send_amount,
493 +// amountValue: sendStore.pendingTransaction.amount,
494 +// fee: S.of(context).send_fee,
495 +// feeValue: sendStore.pendingTransaction.fee,
496 +// leftButtonText: S.of(context).ok,
497 +// rightButtonText: S.of(context).cancel,
498 +// actionLeftButton: () {
499 +// Navigator.of(context).pop();
500 +// sendStore.commitTransaction();
501 +// showDialog<void>(
502 +// context: context,
503 +// builder: (BuildContext context) {
504 +// return SendingAlert(sendStore: sendStore);
505 +// });
506 +// },
507 +// actionRightButton: () => Navigator.of(context).pop());
508 +// });
509 +// });
510 + }
511 +
512 + if (state is TransactionCommitted) {
513 + WidgetsBinding.instance.addPostFrameCallback((_) {
514 + _addressController.text = '';
515 + _cryptoAmountController.text = '';
516 + });
517 + }
518 + });
519 +
520 + _effectsInstalled = true;
521 + }
522 +
523 + Future<void> getOpenaliasRecord(BuildContext context) async {
524 + final isOpenalias = await sendViewModel.isOpenaliasRecord(_addressController.text);
525 +
526 + if (isOpenalias) {
527 + _addressController.text = sendViewModel.recordAddress;
528 +
529 + await showDialog<void>(
530 + context: context,
531 + builder: (BuildContext context) {
532 + return AlertWithOneAction(
533 + alertTitle: S.of(context).openalias_alert_title,
534 + alertContent: S.of(context).openalias_alert_content(sendViewModel.recordName),
535 + buttonText: S.of(context).ok,
536 + buttonAction: () => Navigator.of(context).pop()
537 + );
538 + });
539 + }
540 + }
541 +}
\ No newline at end of file
lib/src/widgets/address_text_field.dart
+122 -85
@@ -1,26 +1,31 @@
1 +import 'package:cake_wallet/palette.dart';
2 import 'package:cake_wallet/routes.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/src/domain/common/contact.dart';
6 import 'package:cake_wallet/src/domain/monero/subaddress.dart';
7 import 'package:cake_wallet/src/domain/common/qr_scanner.dart';
8 +import 'package:flutter/services.dart';
9
8 -enum AddressTextFieldOption { qrCode, addressBook, subaddressList }
10 +enum AddressTextFieldOption { paste, qrCode, addressBook, subaddressList }
11
12 class AddressTextField extends StatelessWidget {
13 AddressTextField(
14 {@required this.controller,
13 - this.isActive = true,
14 - this.placeholder,
15 - this.options = const [
16 - AddressTextFieldOption.qrCode,
17 - AddressTextFieldOption.addressBook
18 - ],
19 - this.onURIScanned,
20 - this.focusNode,
21 - this.isBorderExist = true,
22 - this.buttonColor,
23 - this.validator});
15 + this.isActive = true,
16 + this.placeholder,
17 + this.options = const [
18 + AddressTextFieldOption.qrCode,
19 + AddressTextFieldOption.addressBook
20 + ],
21 + this.onURIScanned,
22 + this.focusNode,
23 + this.isBorderExist = true,
24 + this.buttonColor,
25 + this.borderColor,
26 + this.textStyle,
27 + this.hintStyle,
28 + this.validator});
29
30 static const prefixIconWidth = 34.0;
31 static const prefixIconHeight = 34.0;
@@ -34,6 +39,9 @@ class AddressTextField extends StatelessWidget {
39 final FormFieldValidator<String> validator;
40 final bool isBorderExist;
41 final Color buttonColor;
42 + final Color borderColor;
43 + final TextStyle textStyle;
44 + final TextStyle hintStyle;
45 FocusNode focusNode;
46
47 @override
@@ -45,106 +53,125 @@ class AddressTextField extends StatelessWidget {
53 enabled: isActive,
54 controller: controller,
55 focusNode: focusNode,
48 - style: TextStyle(
56 + style: textStyle ?? TextStyle(
57 fontSize: 16,
50 - color: Theme.of(context).primaryTextTheme.title.color
58 + color: Colors.white
59 ),
60 decoration: InputDecoration(
61 suffixIcon: SizedBox(
62 width: prefixIconWidth * options.length +
63 (spaceBetweenPrefixIcons * options.length),
64 ),
57 - hintStyle: TextStyle(
65 + hintStyle: hintStyle ?? TextStyle(
66 fontSize: 16,
59 - color: Theme.of(context).primaryTextTheme.caption.color
67 + color: PaletteDark.darkCyanBlue
68 ),
69 hintText: placeholder ?? S.current.widgets_address,
70 focusedBorder: isBorderExist
71 ? UnderlineInputBorder(
72 borderSide: BorderSide(
65 - color: Theme.of(context).dividerColor,
73 + color: borderColor ?? Theme.of(context).dividerColor,
74 width: 1.0))
75 : InputBorder.none,
76 disabledBorder: isBorderExist
77 ? UnderlineInputBorder(
78 borderSide:
71 - BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
79 + BorderSide(color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
80 : InputBorder.none,
81 enabledBorder: isBorderExist
82 ? UnderlineInputBorder(
83 borderSide:
76 - BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
84 + BorderSide(color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
85 : InputBorder.none,
86 ),
87 validator: validator,
88 ),
89 Positioned(
82 - bottom: 10,
83 - right: 0,
84 - child: SizedBox(
85 - width: prefixIconWidth * options.length +
86 - (spaceBetweenPrefixIcons * options.length),
87 - child: Row(
88 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
89 - children: [
90 - SizedBox(width: 5),
91 - if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
92 - Container(
93 - width: prefixIconWidth,
94 - height: prefixIconHeight,
95 - padding: EdgeInsets.only(top: 0),
96 - child: InkWell(
97 - onTap: () async => _presentQRScanner(context),
98 - child: Container(
99 - padding: EdgeInsets.all(8),
100 - decoration: BoxDecoration(
101 - color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
102 - borderRadius:
103 - BorderRadius.all(Radius.circular(6))),
104 - child: Image.asset('assets/images/qr_code_icon.png')),
105 - ))
106 - ],
107 - if (this
108 - .options
109 - .contains(AddressTextFieldOption.addressBook)) ...[
110 - Container(
111 - width: prefixIconWidth,
112 - height: prefixIconHeight,
113 - padding: EdgeInsets.only(top: 0),
114 - child: InkWell(
115 - onTap: () async => _presetAddressBookPicker(context),
116 - child: Container(
117 - padding: EdgeInsets.all(8),
118 - decoration: BoxDecoration(
119 - color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
120 - borderRadius:
121 - BorderRadius.all(Radius.circular(6))),
122 - child: Image.asset(
123 - 'assets/images/open_book.png')),
124 - ))
125 - ],
126 - if (this
127 - .options
128 - .contains(AddressTextFieldOption.subaddressList)) ...[
129 - Container(
130 - width: prefixIconWidth,
131 - height: prefixIconHeight,
132 - padding: EdgeInsets.only(top: 0),
133 - child: InkWell(
134 - onTap: () async => _presetSubaddressListPicker(context),
135 - child: Container(
136 - padding: EdgeInsets.all(8),
137 - decoration: BoxDecoration(
138 - color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
139 - borderRadius:
140 - BorderRadius.all(Radius.circular(6))),
141 - child: Image.asset(
142 - 'assets/images/receive_icon_raw.png')),
143 - ))
90 + top: 2,
91 + right: 0,
92 + child: SizedBox(
93 + width: prefixIconWidth * options.length +
94 + (spaceBetweenPrefixIcons * options.length),
95 + child: Row(
96 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
97 + children: [
98 + SizedBox(width: 5),
99 + if (this
100 + .options
101 + .contains(AddressTextFieldOption.paste)) ...[
102 + Container(
103 + width: prefixIconWidth,
104 + height: prefixIconHeight,
105 + padding: EdgeInsets.only(top: 0),
106 + child: InkWell(
107 + onTap: () async => _pasteAddress(context),
108 + child: Container(
109 + padding: EdgeInsets.all(8),
110 + decoration: BoxDecoration(
111 + color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
112 + borderRadius:
113 + BorderRadius.all(Radius.circular(6))),
114 + child: Image.asset(
115 + 'assets/images/duplicate.png')),
116 + )),
117 + ],
118 + if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
119 + Container(
120 + width: prefixIconWidth,
121 + height: prefixIconHeight,
122 + padding: EdgeInsets.only(top: 0),
123 + child: InkWell(
124 + onTap: () async => _presentQRScanner(context),
125 + child: Container(
126 + padding: EdgeInsets.all(8),
127 + decoration: BoxDecoration(
128 + color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
129 + borderRadius:
130 + BorderRadius.all(Radius.circular(6))),
131 + child: Image.asset('assets/images/qr_code_icon.png')),
132 + ))
133 + ],
134 + if (this
135 + .options
136 + .contains(AddressTextFieldOption.addressBook)) ...[
137 + Container(
138 + width: prefixIconWidth,
139 + height: prefixIconHeight,
140 + padding: EdgeInsets.only(top: 0),
141 + child: InkWell(
142 + onTap: () async => _presetAddressBookPicker(context),
143 + child: Container(
144 + padding: EdgeInsets.all(8),
145 + decoration: BoxDecoration(
146 + color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
147 + borderRadius:
148 + BorderRadius.all(Radius.circular(6))),
149 + child: Image.asset(
150 + 'assets/images/open_book.png')),
151 + ))
152 + ],
153 + if (this
154 + .options
155 + .contains(AddressTextFieldOption.subaddressList)) ...[
156 + Container(
157 + width: prefixIconWidth,
158 + height: prefixIconHeight,
159 + padding: EdgeInsets.only(top: 0),
160 + child: InkWell(
161 + onTap: () async => _presetSubaddressListPicker(context),
162 + child: Container(
163 + padding: EdgeInsets.all(8),
164 + decoration: BoxDecoration(
165 + color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
166 + borderRadius:
167 + BorderRadius.all(Radius.circular(6))),
168 + child: Image.asset(
169 + 'assets/images/receive_icon_raw.png')),
170 + )),
171 + ],
172 ],
145 - ],
146 - ),
147 - )
173 + ),
174 + )
175 )
176 ],
177 );
@@ -189,4 +216,14 @@ class AddressTextField extends StatelessWidget {
216 controller.text = subaddress.address;
217 }
218 }
192 -}
219 +
220 + Future<void> _pasteAddress(BuildContext context) async {
221 + String address;
222 +
223 + await Clipboard.getData('text/plain').then((value) => address = value.text);
224 +
225 + if (address.isNotEmpty) {
226 + controller.text = address;
227 + }
228 + }
229 +}
\ No newline at end of file
lib/src/widgets/base_text_form_field.dart
+6 -1
@@ -15,10 +15,12 @@ class BaseTextFormField extends StatelessWidget {
15 this.hintColor,
16 this.borderColor,
17 this.prefix,
18 + this.prefixIcon,
19 this.suffix,
20 this.suffixIcon,
21 this.enabled = true,
22 this.validator,
23 + this.textStyle,
24 this.placeholderTextStyle});
25
26 final TextEditingController controller;
@@ -33,11 +35,13 @@ class BaseTextFormField extends StatelessWidget {
35 final Color hintColor;
36 final Color borderColor;
37 final Widget prefix;
38 + final Widget prefixIcon;
39 final Widget suffix;
40 final Widget suffixIcon;
41 final bool enabled;
42 final FormFieldValidator<String> validator;
43 final TextStyle placeholderTextStyle;
44 + final TextStyle textStyle;
45
46 @override
47 Widget build(BuildContext context) {
@@ -50,11 +54,12 @@ class BaseTextFormField extends StatelessWidget {
54 maxLines: maxLines,
55 inputFormatters: inputFormatters,
56 enabled: enabled,
53 - style: TextStyle(
57 + style: textStyle ?? TextStyle(
58 fontSize: 16.0,
59 color: textColor ?? Theme.of(context).primaryTextTheme.title.color),
60 decoration: InputDecoration(
61 prefix: prefix,
62 + prefixIcon: prefixIcon,
63 suffix: suffix,
64 suffixIcon: suffixIcon,
65 hintStyle: placeholderTextStyle ??
lib/src/widgets/template_tile.dart
+131 -54
@@ -1,77 +1,154 @@
1 import 'package:flutter/material.dart';
2 +import 'package:cake_wallet/palette.dart';
3
3 -class TemplateTile extends StatelessWidget {
4 +class TemplateTile extends StatefulWidget {
5 TemplateTile({
6 + Key key,
7 @required this.to,
8 @required this.amount,
9 @required this.from,
8 - @required this.onTap
9 - });
10 + @required this.onTap,
11 + @required this.onRemove
12 + }) : super(key: key);
13
14 final String to;
15 final String amount;
16 final String from;
17 final VoidCallback onTap;
18 + final VoidCallback onRemove;
19 +
20 + @override
21 + TemplateTileState createState() => TemplateTileState(
22 + to,
23 + amount,
24 + from,
25 + onTap,
26 + onRemove
27 + );
28 +}
29 +
30 +class TemplateTileState extends State<TemplateTile> {
31 + TemplateTileState(
32 + this.to,
33 + this.amount,
34 + this.from,
35 + this.onTap,
36 + this.onRemove
37 + );
38 +
39 + final String to;
40 + final String amount;
41 + final String from;
42 + final VoidCallback onTap;
43 + final VoidCallback onRemove;
44 + final trash = Image.asset('assets/images/trash.png', height: 16, width: 16, color: Colors.white);
45 +
46 + bool isRemovable = false;
47
48 @override
49 Widget build(BuildContext context) {
18 - final toIcon = Image.asset('assets/images/to_icon.png',
19 - color: Theme.of(context).primaryTextTheme.title.color,
50 + //final color = isRemovable ? Colors.white : Theme.of(context).primaryTextTheme.title.color;
51 + final color = Colors.white;
52 + final toIcon = Image.asset('assets/images/to_icon.png', color: color);
53 +
54 + final content = Row(
55 + mainAxisAlignment: MainAxisAlignment.start,
56 + mainAxisSize: MainAxisSize.min,
57 + children: <Widget>[
58 + Text(
59 + amount,
60 + style: TextStyle(
61 + fontSize: 16,
62 + fontWeight: FontWeight.w600,
63 + color: color
64 + ),
65 + ),
66 + Padding(
67 + padding: EdgeInsets.only(left: 5),
68 + child: Text(
69 + from,
70 + style: TextStyle(
71 + fontSize: 16,
72 + fontWeight: FontWeight.w600,
73 + color: color
74 + ),
75 + ),
76 + ),
77 + Padding(
78 + padding: EdgeInsets.only(left: 5),
79 + child: toIcon,
80 + ),
81 + Padding(
82 + padding: EdgeInsets.only(left: 5),
83 + child: Text(
84 + to,
85 + style: TextStyle(
86 + fontSize: 16,
87 + fontWeight: FontWeight.w600,
88 + color: color
89 + ),
90 + ),
91 + ),
92 + ],
93 );
94
22 - return Container(
23 - padding: EdgeInsets.only(right: 10),
24 - child: GestureDetector(
25 - onTap: onTap,
26 - child: Container(
27 - height: 40,
28 - padding: EdgeInsets.only(left: 24, right: 24),
29 - decoration: BoxDecoration(
30 - borderRadius: BorderRadius.all(Radius.circular(20)),
31 - color: Theme.of(context).accentTextTheme.title.backgroundColor
95 + final tile = Container(
96 + padding: EdgeInsets.only(right: 10),
97 + child: ClipRRect(
98 + borderRadius: BorderRadius.all(Radius.circular(20)),
99 + child: GestureDetector(
100 + onTap: onTap,
101 + onLongPress: () {
102 + setState(() {
103 + isRemovable = true;
104 + });
105 + },
106 + child: Container(
107 + height: 40,
108 + padding: EdgeInsets.only(left: 24, right: 24),
109 + color: PaletteDark.darkVioletBlue,
110 + child: content,
111 + ),
112 ),
33 - child: Row(
34 - mainAxisAlignment: MainAxisAlignment.start,
35 - mainAxisSize: MainAxisSize.min,
36 - children: <Widget>[
37 - Text(
38 - amount,
39 - style: TextStyle(
40 - fontSize: 16,
41 - fontWeight: FontWeight.w600,
42 - color: Theme.of(context).primaryTextTheme.title.color
43 - ),
44 - ),
45 - Padding(
46 - padding: EdgeInsets.only(left: 5),
47 - child: Text(
48 - from,
49 - style: TextStyle(
50 - fontSize: 16,
51 - fontWeight: FontWeight.w600,
52 - color: Theme.of(context).primaryTextTheme.title.color
113 + )
114 + );
115 +
116 + final removableTile = Container(
117 + padding: EdgeInsets.only(right: 10),
118 + child: ClipRRect(
119 + borderRadius: BorderRadius.all(Radius.circular(20)),
120 + child: Row(
121 + mainAxisSize: MainAxisSize.min,
122 + children: <Widget>[
123 + GestureDetector(
124 + onTap: () {
125 + setState(() {
126 + isRemovable = false;
127 + });
128 + },
129 + child: Container(
130 + height: 40,
131 + padding: EdgeInsets.only(left: 24, right: 10),
132 + color: Colors.red,
133 + child: content,
134 ),
135 ),
55 - ),
56 - Padding(
57 - padding: EdgeInsets.only(left: 5),
58 - child: toIcon,
59 - ),
60 - Padding(
61 - padding: EdgeInsets.only(left: 5),
62 - child: Text(
63 - to,
64 - style: TextStyle(
65 - fontSize: 16,
66 - fontWeight: FontWeight.w600,
67 - color: Theme.of(context).primaryTextTheme.title.color
136 + GestureDetector(
137 + onTap: onRemove,
138 + child: Container(
139 + height: 40,
140 + width: 44,
141 + color: Palette.darkRed,
142 + child: Center(
143 + child: trash,
144 + ),
145 ),
69 - ),
70 - ),
71 - ],
72 - ),
73 - ),
74 - ),
146 + )
147 + ],
148 + )
149 + )
150 );
151 +
152 + return isRemovable ? removableTile : tile;
153 }
154 }
\ No newline at end of file
lib/store/templates/exchange_template_store.dart new
+40
@@ -0,0 +1,40 @@
1 +import 'dart:async';
2 +import 'package:mobx/mobx.dart';
3 +import 'package:hive/hive.dart';
4 +import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
5 +
6 +part 'exchange_template_store.g.dart';
7 +
8 +class ExchangeTemplateStore = ExchangeTemplateBase with _$ExchangeTemplateStore;
9 +
10 +abstract class ExchangeTemplateBase with Store {
11 + ExchangeTemplateBase({this.templateSource}) {
12 + templates = ObservableList<ExchangeTemplate>();
13 + update();
14 + }
15 +
16 + @observable
17 + ObservableList<ExchangeTemplate> templates;
18 +
19 + Box<ExchangeTemplate> templateSource;
20 +
21 + @action
22 + void update() =>
23 + templates.replaceRange(0, templates.length, templateSource.values.toList());
24 +
25 + @action
26 + Future addTemplate({String amount, String depositCurrency, String receiveCurrency,
27 + String provider, String depositAddress, String receiveAddress}) async {
28 + final template = ExchangeTemplate(
29 + amount: amount,
30 + depositCurrency: depositCurrency,
31 + receiveCurrency: receiveCurrency,
32 + provider: provider,
33 + depositAddress: depositAddress,
34 + receiveAddress: receiveAddress);
35 + await templateSource.add(template);
36 + }
37 +
38 + @action
39 + Future remove({ExchangeTemplate template}) async => await template.delete();
40 +}
\ No newline at end of file
lib/store/templates/send_template_store.dart new
+34
@@ -0,0 +1,34 @@
1 +import 'dart:async';
2 +import 'package:mobx/mobx.dart';
3 +import 'package:hive/hive.dart';
4 +import 'package:cake_wallet/src/domain/common/template.dart';
5 +
6 +part 'send_template_store.g.dart';
7 +
8 +class SendTemplateStore = SendTemplateBase with _$SendTemplateStore;
9 +
10 +abstract class SendTemplateBase with Store {
11 + SendTemplateBase({this.templateSource}) {
12 + templates = ObservableList<Template>();
13 + update();
14 + }
15 +
16 + @observable
17 + ObservableList<Template> templates;
18 +
19 + Box<Template> templateSource;
20 +
21 + @action
22 + void update() =>
23 + templates.replaceRange(0, templates.length, templateSource.values.toList());
24 +
25 + @action
26 + Future addTemplate({String name, String address, String cryptoCurrency, String amount}) async {
27 + final template = Template(name: name, address: address,
28 + cryptoCurrency: cryptoCurrency, amount: amount);
29 + await templateSource.add(template);
30 + }
31 +
32 + @action
33 + Future remove({Template template}) async => await template.delete();
34 +}
\ No newline at end of file
lib/view_model/send_view_model.dart
+135 -8
@@ -1,16 +1,21 @@
1 import 'package:cake_wallet/core/address_validator.dart';
2 import 'package:cake_wallet/core/amount_validator.dart';
3 +import 'package:cake_wallet/core/template_validator.dart';
4 import 'package:cake_wallet/core/validator.dart';
5 import 'package:cake_wallet/core/wallet_base.dart';
6 import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
7 import 'package:cake_wallet/monero/monero_wallet.dart';
8 import 'package:cake_wallet/src/domain/common/balance.dart';
9 +import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
10 import 'package:cake_wallet/src/domain/common/calculate_estimated_fee.dart';
11 import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
12 import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
13 +import 'package:cake_wallet/src/domain/common/sync_status.dart';
14 import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
15 import 'package:cake_wallet/store/settings_store.dart';
16 +import 'package:cake_wallet/store/templates/send_template_store.dart';
17 import 'package:flutter/foundation.dart';
18 +import 'package:intl/intl.dart';
19 import 'package:mobx/mobx.dart';
20 import 'package:cake_wallet/monero/monero_wallet_service.dart';
21 import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
@@ -18,6 +23,10 @@ import 'package:cake_wallet/core/wallet_creation_service.dart';
23 import 'package:cake_wallet/core/wallet_credentials.dart';
24 import 'package:cake_wallet/src/domain/common/wallet_type.dart';
25 import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
26 +import 'package:cake_wallet/generated/i18n.dart';
27 +import 'package:cake_wallet/src/domain/common/openalias_record.dart';
28 +import 'package:cake_wallet/store/dashboard/fiat_convertation_store.dart';
29 +import 'package:cake_wallet/src/domain/common/template.dart';
30
31 part 'send_view_model.g.dart';
32
@@ -42,8 +51,20 @@ class SendingFailed extends SendViewModelState {
51 class SendViewModel = SendViewModelBase with _$SendViewModel;
52
53 abstract class SendViewModelBase with Store {
45 - SendViewModelBase(this._wallet, this._settingsStore)
46 - : state = InitialSendViewModelState();
54 + SendViewModelBase(
55 + this._wallet,
56 + this._settingsStore,
57 + this._fiatConvertationStore,
58 + this.sendTemplateStore) {
59 +
60 + state = InitialSendViewModelState();
61 +
62 + _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
63 + _fiatNumberFormat = NumberFormat()..maximumFractionDigits = 2;
64 + }
65 +
66 + NumberFormat _cryptoNumberFormat;
67 + NumberFormat _fiatNumberFormat;
68
69 @observable
70 SendViewModelState state;
@@ -57,6 +78,22 @@ abstract class SendViewModelBase with Store {
78 @observable
79 String address;
80
81 + String get cryptoCurrencyTitle {
82 + var _currencyTitle = '';
83 +
84 + if (_wallet is MoneroWallet) {
85 + _currencyTitle = 'Monero';
86 + }
87 +
88 + if (_wallet is BitcoinWallet) {
89 + _currencyTitle = 'Bitcoin';
90 + }
91 +
92 + return _currencyTitle;
93 + }
94 +
95 + String get pageTitle => S.current.send_title + ' ' + cryptoCurrencyTitle;
96 +
97 FiatCurrency get fiat => _settingsStore.fiatCurrency;
98
99 TransactionPriority get transactionPriority =>
@@ -65,30 +102,120 @@ abstract class SendViewModelBase with Store {
102 double get estimatedFee =>
103 calculateEstimatedFee(priority: transactionPriority);
104
105 + String get name => _wallet.name;
106 +
107 CryptoCurrency get currency => _wallet.currency;
108
109 Validator get amountValidator => AmountValidator(type: _wallet.type);
110
111 Validator get addressValidator => AddressValidator(type: _wallet.currency);
112
113 + Validator get templateValidator => TemplateValidator();
114 +
115 + @computed
116 + double get price => _fiatConvertationStore.price;
117 +
118 + @computed
119 + ObservableList<Template> get templates => ObservableList.of(
120 + sendTemplateStore.templates.where((item)
121 + => item.cryptoCurrency == _wallet.currency.title).toList());
122 +
123 @computed
124 String get balance {
125 + var _balance = '0.0';
126 +
127 if (_wallet is MoneroWallet) {
77 - _wallet.balance.formattedUnlockedBalance;
128 + _balance = _wallet.balance.formattedUnlockedBalance.toString();
129 }
130
131 if (_wallet is BitcoinWallet) {
81 - _wallet.balance.confirmedFormatted;
132 + _balance = _wallet.balance.confirmedFormatted.toString();
133 + }
134 +
135 + return _settingsStore.balanceDisplayMode == BalanceDisplayMode.hiddenBalance
136 + ? '---'
137 + : _balance;
138 + }
139 +
140 + @computed
141 + SyncStatus get status => _wallet.syncStatus;
142 +
143 + @action
144 + void changeCryptoAmount(String amount) {
145 + cryptoAmount = amount;
146 +
147 + if (cryptoAmount != null && cryptoAmount.isNotEmpty) {
148 + _calculateFiatAmount();
149 + } else {
150 + fiatAmount = '';
151 }
152 + }
153
84 - return '0.0';
154 + @action
155 + void changeFiatAmount(String amount) {
156 + fiatAmount = amount;
157 +
158 + if (fiatAmount != null && fiatAmount.isNotEmpty) {
159 + _calculateCryptoAmount();
160 + } else {
161 + cryptoAmount = '';
162 + }
163 }
164
87 - WalletBase _wallet;
165 + @action
166 + Future _calculateFiatAmount() async {
167 + try {
168 + final amount = double.parse(cryptoAmount) * price;
169 + fiatAmount = _fiatNumberFormat.format(amount);
170 + } catch (e) {
171 + fiatAmount = '0.00';
172 + }
173 + }
174 +
175 + @action
176 + Future _calculateCryptoAmount() async {
177 + try {
178 + final amount = double.parse(fiatAmount) / price;
179 + cryptoAmount = _cryptoNumberFormat.format(amount);
180 + } catch (e) {
181 + cryptoAmount = '0.00';
182 + }
183 + }
184
89 - SettingsStore _settingsStore;
185 + @action
186 + void changeAddress(String address) {
187 + this.address = address;
188 + }
189 +
190 + @action
191 + void setSendAll() {
192 + cryptoAmount = 'ALL';
193 + fiatAmount = '';
194 + }
195 +
196 + final WalletBase _wallet;
197 +
198 + final SettingsStore _settingsStore;
199 +
200 + final FiatConvertationStore _fiatConvertationStore;
201 +
202 + final SendTemplateStore sendTemplateStore;
203 +
204 + String recordName;
205 +
206 + String recordAddress;
207 +
208 + Future<bool> isOpenaliasRecord(String name) async {
209 + final _openaliasRecord = await OpenaliasRecord
210 + .fetchAddressAndName(OpenaliasRecord.formatDomainName(name));
211 +
212 + recordAddress = _openaliasRecord.address;
213 + recordName = _openaliasRecord.name;
214 +
215 + return recordAddress != name;
216 + }
217
218 Future<void> createTransaction() async {}
219
220 Future<void> commitTransaction() async {}
94 -}
221 +}
\ No newline at end of file
pubspec.yaml
+1
@@ -112,6 +112,7 @@ flutter:
112 fonts:
113 - asset: assets/fonts/Poppins-Regular.ttf
114 - asset: assets/fonts/Poppins-Medium.ttf
115 + - asset: assets/fonts/Poppins-SemiBold.ttf
116 - asset: assets/fonts/Poppins-Bold.ttf
117
118
res/values/strings_de.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Spanisch",
183
184
185 - "send_title" : "Senden Sie Monero",
185 + "send_title" : "Senden Sie",
186 "send_your_wallet" : "Deine Geldbörse",
187 - "send_monero_address" : "Monero-Adresse",
187 + "send_address" : "${cryptoCurrency}-Adresse",
188 "send_payment_id" : "Zahlungs ID (wahlweise)",
189 "all" : "ALLE",
190 "send_error_minimum_value" : "Der Mindestbetrag beträgt 0,01",
@@ -377,5 +377,9 @@
377 "buy" : "Kaufen",
378
379 "placeholder_transactions" : "Ihre Transaktionen werden hier angezeigt",
380 - "placeholder_contacts" : "Ihre Kontakte werden hier angezeigt"
380 + "placeholder_contacts" : "Ihre Kontakte werden hier angezeigt",
381 +
382 + "template" : "Vorlage",
383 + "confirm_delete_template" : "Diese Aktion löscht diese Vorlage. Möchten Sie fortfahren?",
384 + "confirm_delete_wallet" : "Diese Aktion löscht diese Brieftasche. Möchten Sie fortfahren?"
385 }
\ No newline at end of file
res/values/strings_en.arb
+7 -3
@@ -183,9 +183,9 @@
183 "seed_language_spanish" : "Spanish",
184
185
186 - "send_title" : "Send Monero",
186 + "send_title" : "Send",
187 "send_your_wallet" : "Your wallet",
188 - "send_monero_address" : "Monero address",
188 + "send_address" : "${cryptoCurrency} address",
189 "send_payment_id" : "Payment ID (optional)",
190 "all" : "ALL",
191 "send_error_minimum_value" : "Minimum value of amount is 0.01",
@@ -378,5 +378,9 @@
378 "buy" : "Buy",
379
380 "placeholder_transactions" : "Your transactions will be displayed here",
381 - "placeholder_contacts" : "Your contacts will be displayed here"
381 + "placeholder_contacts" : "Your contacts will be displayed here",
382 +
383 + "template" : "Template",
384 + "confirm_delete_template" : "This action will delete this template. Do you wish to continue?",
385 + "confirm_delete_wallet" : "This action will delete this wallet. Do you wish to continue?"
386 }
\ No newline at end of file
res/values/strings_es.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Español",
183
184
185 - "send_title" : "Enviar Monero",
185 + "send_title" : "Enviar",
186 "send_your_wallet" : "Tu billetera",
187 - "send_monero_address" : "Dirección de Monero",
187 + "send_address" : "Dirección de ${cryptoCurrency}",
188 "send_payment_id" : "ID de pago (opcional)",
189 "all" : "TODOS",
190 "send_error_minimum_value" : "El valor mínimo de la cantidad es 0.01",
@@ -377,5 +377,9 @@
377 "buy" : "Comprar",
378
379 "placeholder_transactions" : "Sus transacciones se mostrarán aquí",
380 - "placeholder_contacts" : "Tus contactos se mostrarán aquí"
380 + "placeholder_contacts" : "Tus contactos se mostrarán aquí",
381 +
382 + "template" : "Plantilla",
383 + "confirm_delete_template" : "Esta acción eliminará esta plantilla. ¿Desea continuar?",
384 + "confirm_delete_wallet" : "Esta acción eliminará esta billetera. ¿Desea continuar?"
385 }
\ No newline at end of file
res/values/strings_hi.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "स्पेनिश",
183
184
185 - "send_title" : "संदेश Monero",
185 + "send_title" : "संदेश",
186 "send_your_wallet" : "आपका बटुआ",
187 - "send_monero_address" : "मोनरो पता",
187 + "send_address" : "${cryptoCurrency} पता",
188 "send_payment_id" : "भुगतान ID (ऐच्छिक)",
189 "all" : "सब",
190 "send_error_minimum_value" : "राशि का न्यूनतम मूल्य 0.01 है",
@@ -377,5 +377,9 @@
377 "buy" : "खरीदें",
378
379 "placeholder_transactions" : "आपके लेनदेन यहां प्रदर्शित होंगे",
380 - "placeholder_contacts" : "आपके संपर्क यहां प्रदर्शित होंगे"
380 + "placeholder_contacts" : "आपके संपर्क यहां प्रदर्शित होंगे",
381 +
382 + "template" : "खाका",
383 + "confirm_delete_template" : "यह क्रिया इस टेम्पलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?",
384 + "confirm_delete_wallet" : "यह क्रिया इस वॉलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?"
385 }
\ No newline at end of file
res/values/strings_ja.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "スペイン語",
183
184
185 - "send_title" : "Moneroを送信",
185 + "send_title" : "を送信",
186 "send_your_wallet" : "あなたの財布",
187 - "send_monero_address" : "Monero 住所",
187 + "send_address" : "${cryptoCurrency} 住所",
188 "send_payment_id" : "支払いID (オプショナル)",
189 "all" : "すべて",
190 "send_error_minimum_value" : "金額の最小値は0.01です",
@@ -377,5 +377,9 @@
377 "buy" : "購入",
378
379 "placeholder_transactions" : "あなたの取引はここに表示されます",
380 - "placeholder_contacts" : "連絡先はここに表示されます"
380 + "placeholder_contacts" : "連絡先はここに表示されます",
381 +
382 + "template" : "テンプレート",
383 + "confirm_delete_template" : "この操作により、このテンプレートが削除されます。 続行しますか?",
384 + "confirm_delete_wallet" : "このアクションにより、このウォレットが削除されます。 続行しますか?"
385 }
\ No newline at end of file
res/values/strings_ko.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "스페인의",
183
184
185 - "send_title" : "모네로 보내기",
185 + "send_title" : "보내다",
186 "send_your_wallet" : "지갑",
187 - "send_monero_address" : "모네로 주소",
187 + "send_address" : "${cryptoCurrency} 주소",
188 "send_payment_id" : "지불 ID (optional)",
189 "all" : "모든",
190 "send_error_minimum_value" : "금액의 최소값은 0.01입니다",
@@ -377,5 +377,9 @@
377 "buy" : "구입",
378
379 "placeholder_transactions" : "거래가 여기에 표시됩니다",
380 - "placeholder_contacts" : "연락처가 여기에 표시됩니다"
380 + "placeholder_contacts" : "연락처가 여기에 표시됩니다",
381 +
382 + "template" : "주형",
383 + "confirm_delete_template" : "이 작업은이 템플릿을 삭제합니다. 계속 하시겠습니까?",
384 + "confirm_delete_wallet" : "이 작업은이 지갑을 삭제합니다. 계속 하시겠습니까?"
385 }
\ No newline at end of file
res/values/strings_nl.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Spaans",
183
184
185 - "send_title" : "Stuur Monero",
185 + "send_title" : "Stuur",
186 "send_your_wallet" : "Uw portemonnee",
187 - "send_monero_address" : "Monero-adres",
187 + "send_address" : "${cryptoCurrency}-adres",
188 "send_payment_id" : "Betaling ID (facultatief)",
189 "all" : "ALLE",
190 "send_error_minimum_value" : "Minimale waarde van bedrag is 0,01",
@@ -377,5 +377,9 @@
377 "buy" : "Kopen",
378
379 "placeholder_transactions" : "Uw transacties worden hier weergegeven",
380 - "placeholder_contacts" : "Je contacten worden hier weergegeven"
380 + "placeholder_contacts" : "Je contacten worden hier weergegeven",
381 +
382 + "template" : "Sjabloon",
383 + "confirm_delete_template" : "Met deze actie wordt deze sjabloon verwijderd. Wilt u doorgaan?",
384 + "confirm_delete_wallet" : "Met deze actie wordt deze portemonnee verwijderd. Wilt u doorgaan?"
385 }
\ No newline at end of file
res/values/strings_pl.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Hiszpański",
183
184
185 - "send_title" : "Wyślij Monero",
185 + "send_title" : "Wyślij",
186 "send_your_wallet" : "Twój portfel",
187 - "send_monero_address" : "Adres Monero",
187 + "send_address" : "Adres ${cryptoCurrency}",
188 "send_payment_id" : "Identyfikator płatności (opcjonalny)",
189 "all" : "WSZYSTKO",
190 "send_error_minimum_value" : "Minimalna wartość kwoty to 0,01",
@@ -377,5 +377,9 @@
377 "buy" : "Kup",
378
379 "placeholder_transactions" : "Twoje transakcje zostaną wyświetlone tutaj",
380 - "placeholder_contacts" : "Twoje kontakty zostaną wyświetlone tutaj"
380 + "placeholder_contacts" : "Twoje kontakty zostaną wyświetlone tutaj",
381 +
382 + "template" : "Szablon",
383 + "confirm_delete_template" : "Ta czynność usunie ten szablon. Czy chcesz kontynuować?",
384 + "confirm_delete_wallet" : "Ta czynność usunie ten portfel. Czy chcesz kontynuować?"
385 }
\ No newline at end of file
res/values/strings_pt.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Espanhola",
183
184
185 - "send_title" : "Enviar Monero",
185 + "send_title" : "Enviar",
186 "send_your_wallet" : "Sua carteira",
187 - "send_monero_address" : "Endereço Monero",
187 + "send_address" : "Endereço ${cryptoCurrency}",
188 "send_payment_id" : "ID de pagamento (opcional)",
189 "all" : "TUDO",
190 "send_error_minimum_value" : "O valor mínimo da quantia é 0,01",
@@ -377,5 +377,9 @@
377 "buy" : "Comprar",
378
379 "placeholder_transactions" : "Suas transações serão exibidas aqui",
380 - "placeholder_contacts" : "Seus contatos serão exibidos aqui"
380 + "placeholder_contacts" : "Seus contatos serão exibidos aqui",
381 +
382 + "template" : "Modelo",
383 + "confirm_delete_template" : "Esta ação excluirá este modelo. Você deseja continuar?",
384 + "confirm_delete_wallet" : "Esta ação excluirá esta carteira. Você deseja continuar?"
385 }
res/values/strings_ru.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Испанский",
183
184
185 - "send_title" : "Отправить Monero",
185 + "send_title" : "Отправить",
186 "send_your_wallet" : "Ваш кошелёк",
187 - "send_monero_address" : "Monero адрес",
187 + "send_address" : "${cryptoCurrency} адрес",
188 "send_payment_id" : "ID платежа (опционально)",
189 "all" : "ВСЕ",
190 "send_error_minimum_value" : "Mинимальная сумма 0.01",
@@ -377,5 +377,9 @@
377 "buy" : "Купить",
378
379 "placeholder_transactions" : "Ваши транзакции будут отображаться здесь",
380 - "placeholder_contacts" : "Ваши контакты будут отображаться здесь"
380 + "placeholder_contacts" : "Ваши контакты будут отображаться здесь",
381 +
382 + "template" : "Шаблон",
383 + "confirm_delete_template" : "Это действие удалит шаблон. Вы хотите продолжить?",
384 + "confirm_delete_wallet" : "Это действие удалит кошелек. Вы хотите продолжить?"
385 }
\ No newline at end of file
res/values/strings_uk.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "Іспанська",
183
184
185 - "send_title" : "Відправити Monero",
185 + "send_title" : "Відправити",
186 "send_your_wallet" : "Ваш гаманець",
187 - "send_monero_address" : "Monero адреса",
187 + "send_address" : "${cryptoCurrency} адреса",
188 "send_payment_id" : "ID платежу (опційно)",
189 "all" : "ВСЕ",
190 "send_error_minimum_value" : "Мінімальна сума 0.01",
@@ -377,5 +377,9 @@
377 "buy" : "Купити",
378
379 "placeholder_transactions" : "Тут відображатимуться ваші транзакції",
380 - "placeholder_contacts" : "Тут будуть показані ваші контакти"
380 + "placeholder_contacts" : "Тут будуть показані ваші контакти",
381 +
382 + "template" : "Шаблон",
383 + "confirm_delete_template" : "Ця дія видалить шаблон. Ви хочете продовжити?",
384 + "confirm_delete_wallet" : "Ця дія видалить гаманець. Ви хочете продовжити?"
385 }
\ No newline at end of file
res/values/strings_zh.arb
+7 -3
@@ -182,9 +182,9 @@
182 "seed_language_spanish" : "西班牙文",
183
184
185 - "send_title" : "发送门罗币",
185 + "send_title" : "發送",
186 "send_your_wallet" : "你的钱包",
187 - "send_monero_address" : "门罗地址",
187 + "send_address" : "${cryptoCurrency} 地址",
188 "send_payment_id" : "付款编号 (可选的)",
189 "all" : "所有",
190 "send_error_minimum_value" : "最小金额为0.01",
@@ -377,5 +377,9 @@
377 "buy" : "購買",
378
379 "placeholder_transactions" : "您的交易將顯示在這裡",
380 - "placeholder_contacts" : "您的聯繫人將顯示在這裡"
380 + "placeholder_contacts" : "您的聯繫人將顯示在這裡",
381 +
382 + "template" : "模板",
383 + "confirm_delete_template" : "此操作將刪除此模板。 你想繼續嗎?",
384 + "confirm_delete_wallet" : "此操作將刪除此錢包。 你想繼續嗎?"
385 }
\ No newline at end of file