diff --git a/assets/images/2.0x/duplicate.png b/assets/images/2.0x/duplicate.png deleted file mode 100644 index b377d0a9d..000000000 Binary files a/assets/images/2.0x/duplicate.png and /dev/null differ diff --git a/assets/images/2.0x/paste_ios.png b/assets/images/2.0x/paste_ios.png new file mode 100644 index 000000000..7b5d76d50 Binary files /dev/null and b/assets/images/2.0x/paste_ios.png differ diff --git a/assets/images/3.0x/duplicate.png b/assets/images/3.0x/duplicate.png deleted file mode 100644 index 4fbef4bf2..000000000 Binary files a/assets/images/3.0x/duplicate.png and /dev/null differ diff --git a/assets/images/3.0x/paste_ios.png b/assets/images/3.0x/paste_ios.png new file mode 100644 index 000000000..7b5d76d50 Binary files /dev/null and b/assets/images/3.0x/paste_ios.png differ diff --git a/assets/images/duplicate.png b/assets/images/duplicate.png deleted file mode 100644 index 99cfd4ca2..000000000 Binary files a/assets/images/duplicate.png and /dev/null differ diff --git a/assets/images/paste_ios.png b/assets/images/paste_ios.png new file mode 100644 index 000000000..7b5d76d50 Binary files /dev/null and b/assets/images/paste_ios.png differ diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 95a7598ff..fae18ebca 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -493,7 +493,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -526,7 +526,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/lib/di.dart b/lib/di.dart index aac1bfa24..bac4eeff3 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -15,6 +15,7 @@ import 'package:cake_wallet/src/screens/faq/faq_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart'; import 'package:cake_wallet/src/screens/rescan/rescan_page.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/send/send_template_page.dart'; import 'package:cake_wallet/src/screens/settings/change_language.dart'; @@ -56,6 +57,7 @@ import 'package:cake_wallet/view_model/send/send_view_model.dart'; import 'package:cake_wallet/view_model/settings/settings_view_model.dart'; import 'package:cake_wallet/view_model/wallet_keys_view_model.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; import 'package:flutter/widgets.dart'; @@ -222,10 +224,10 @@ Future setup( addressEditOrCreateViewModel: getIt.get(param1: item))); - // getIt.get() getIt.registerFactory(() => SendViewModel( getIt.get().wallet, getIt.get().settingsStore, + getIt.get(), getIt.get())); getIt.registerFactory( @@ -318,10 +320,10 @@ Future setup( () => NodeCreateOrEditPage(getIt.get())); getIt.registerFactory(() => ExchangeViewModel( - wallet: getIt.get().wallet, - exchangeTemplateStore: getIt.get(), - trades: tradesSource, - tradesStore: getIt.get())); + getIt.get().wallet, + tradesSource, + getIt.get(), + getIt.get())); getIt.registerFactory(() => ExchangeTradeViewModel( wallet: getIt.get().wallet, @@ -371,4 +373,12 @@ Future setup( getIt.registerFactory(() => FaqPage(getIt.get())); getIt.registerFactory(() => LanguageListPage(getIt.get())); + + getIt.registerFactoryParam( + (type, _) => WalletRestoreViewModel(getIt.get(), + getIt.get(param1: type), walletInfoSource, + type: type)); + + getIt.registerFactoryParam((type, _) => + WalletRestorePage(getIt.get(param1: type))); } diff --git a/lib/entities/fs_migration.dart b/lib/entities/fs_migration.dart index 8225eae39..2cdc24952 100644 --- a/lib/entities/fs_migration.dart +++ b/lib/entities/fs_migration.dart @@ -29,7 +29,8 @@ Future migrate_android_v1() async { await android_migrate_wallets(appDocDir: appDocDir); } -Future ios_migrate_v1(Box walletInfoSource, Box tradeSource, Box contactSource) async { +Future ios_migrate_v1(Box walletInfoSource, + Box tradeSource, Box contactSource) async { final prefs = await SharedPreferences.getInstance(); if (prefs.getBool('ios_migration_v1_completed') ?? false) { @@ -390,7 +391,7 @@ Future ios_migrate_trades_list(Box tradeSource) async { Future ios_migrate_address_book(Box contactSource) async { final prefs = await SharedPreferences.getInstance(); - + if (prefs.getBool('ios_migration_address_book_completed') ?? false) { return; } diff --git a/lib/generated/i18n.dart b/lib/generated/i18n.dart index 7a86289b7..3cf819d59 100644 --- a/lib/generated/i18n.dart +++ b/lib/generated/i18n.dart @@ -123,6 +123,7 @@ class S implements WidgetsLocalizations { String get paste => "Paste"; String get payment_id => "Payment ID: "; String get pending => " (pending)"; + String get picker_description => "To choose ChangeNOW or MorphToken, please change your trading pair first"; String get pin_is_incorrect => "PIN is incorrect"; String get placeholder_contacts => "Your contacts will be displayed here"; String get placeholder_transactions => "Your transactions will be displayed here"; @@ -711,6 +712,8 @@ class $de extends S { @override String get wallet_list_load_wallet => "Wallet einlegen"; @override + String get picker_description => "Um ChangeNOW oder MorphToken zu wählen, ändern Sie bitte zuerst Ihr Handelspaar"; + @override String get sending => "Senden"; @override String get restore_restore_wallet => "Wallet wiederherstellen"; @@ -1353,6 +1356,8 @@ class $hi extends S { @override String get wallet_list_load_wallet => "वॉलेट लोड करें"; @override + String get picker_description => "ChangeNOW या MorphToken चुनने के लिए, कृपया अपनी ट्रेडिंग जोड़ी को पहले बदलें"; + @override String get sending => "भेजना"; @override String get restore_restore_wallet => "वॉलेट को पुनर्स्थापित करें"; @@ -1995,6 +2000,8 @@ class $ru extends S { @override String get wallet_list_load_wallet => "Загрузка кошелька"; @override + String get picker_description => "Чтобы выбрать ChangeNOW или MorphToken, сначала смените пару для обмена"; + @override String get sending => "Отправка"; @override String get restore_restore_wallet => "Восстановить кошелёк"; @@ -2637,6 +2644,8 @@ class $ko extends S { @override String get wallet_list_load_wallet => "지갑로드"; @override + String get picker_description => "ChangeNOW 또는 MorphToken을 선택하려면 먼저 거래 쌍을 변경하십시오."; + @override String get sending => "배상"; @override String get restore_restore_wallet => "월렛 복원"; @@ -3279,6 +3288,8 @@ class $pt extends S { @override String get wallet_list_load_wallet => "Abrir carteira"; @override + String get picker_description => "Para escolher ChangeNOW ou MorphToken, altere primeiro o seu par de negociação"; + @override String get sending => "Enviando"; @override String get restore_restore_wallet => "Restaurar carteira"; @@ -3921,6 +3932,8 @@ class $uk extends S { @override String get wallet_list_load_wallet => "Завантаження гаманця"; @override + String get picker_description => "Щоб вибрати ChangeNOW або MorphToken, спочатку змініть пару для обміну"; + @override String get sending => "Відправлення"; @override String get restore_restore_wallet => "Відновити гаманець"; @@ -4563,6 +4576,8 @@ class $ja extends S { @override String get wallet_list_load_wallet => "ウォレットをロード"; @override + String get picker_description => "ChangeNOWまたはMorphTokenを選択するには、最初にトレーディングペアを変更してください"; + @override String get sending => "送信"; @override String get restore_restore_wallet => "ウォレットを復元"; @@ -5209,6 +5224,8 @@ class $pl extends S { @override String get wallet_list_load_wallet => "Załaduj portfel"; @override + String get picker_description => "Aby wybrać ChangeNOW lub MorphToken, najpierw zmień swoją parę handlową"; + @override String get sending => "Wysyłanie"; @override String get restore_restore_wallet => "Przywróć portfel"; @@ -5851,6 +5868,8 @@ class $es extends S { @override String get wallet_list_load_wallet => "Billetera de carga"; @override + String get picker_description => "Para elegir ChangeNOW o MorphToken, primero cambie su par comercial"; + @override String get sending => "Enviando"; @override String get restore_restore_wallet => "Recuperar Cartera"; @@ -6493,6 +6512,8 @@ class $nl extends S { @override String get wallet_list_load_wallet => "Portemonnee laden"; @override + String get picker_description => "Om ChangeNOW of MorphToken te kiezen, moet u eerst uw handelspaar wijzigen"; + @override String get sending => "Bezig met verzenden"; @override String get restore_restore_wallet => "Portemonnee herstellen"; @@ -7135,6 +7156,8 @@ class $zh extends S { @override String get wallet_list_load_wallet => "装入钱包"; @override + String get picker_description => "要選擇ChangeNOW或MorphToken,請先更改您的交易對"; + @override String get sending => "正在发送"; @override String get restore_restore_wallet => "恢复钱包"; diff --git a/lib/main.dart b/lib/main.dart index e9145ddc3..809600174 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -120,7 +120,7 @@ class App extends StatelessWidget { settingsStore.isDarkTheme ? Brightness.light : Brightness.dark; final authenticationStore = getIt.get(); final initialRoute = authenticationStore.state == AuthenticationState.denied - ? Routes.welcome + ? Routes.disclaimer : Routes.login; SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( diff --git a/lib/router.dart b/lib/router.dart index 466e60866..d98c9c7cf 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/entities/contact_record.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -128,7 +129,7 @@ class Router { return CupertinoPageRoute( builder: (_) => getIt.get( param1: (BuildContext context, dynamic _) => - Navigator.pushNamed(context, Routes.restoreWalletFromSeed)), + Navigator.pushNamed(context, Routes.restoreWallet)), fullscreenDialog: true); case Routes.seed: @@ -136,6 +137,11 @@ class Router { builder: (_) => getIt.get(param1: settings.arguments as bool)); + case Routes.restoreWallet: + return MaterialPageRoute( + builder: (_) => + getIt.get(param1: WalletType.monero)); + case Routes.restoreWalletFromSeed: // final args = settings.arguments as List; final type = WalletType.monero; //args.first as WalletType; @@ -144,8 +150,7 @@ class Router { // : LanguageList.english; return CupertinoPageRoute( - builder: (_) => - RestoreWalletFromSeedPage(type: type)); + builder: (_) => RestoreWalletFromSeedPage(type: type)); case Routes.restoreWalletFromKeys: final args = settings.arguments as List; diff --git a/lib/routes.dart b/lib/routes.dart index 61af6b26e..13a365990 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -46,4 +46,5 @@ class Routes { static const sendTemplate = '/send_template'; static const exchangeTemplate = '/exchange_template'; static const restoreWalletType = '/restore_wallet_type'; + static const restoreWallet = '/restore_wallet'; } \ No newline at end of file diff --git a/lib/src/screens/disclaimer/disclaimer_page.dart b/lib/src/screens/disclaimer/disclaimer_page.dart index b80a7a1fe..935de9612 100644 --- a/lib/src/screens/disclaimer/disclaimer_page.dart +++ b/lib/src/screens/disclaimer/disclaimer_page.dart @@ -1,5 +1,6 @@ import 'dart:ui'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -19,26 +20,28 @@ class DisclaimerPage extends BasePage { String get title => 'Terms of Use'; @override - Widget body(BuildContext context) => DisclaimerPageBody(isReadOnly: true); + Widget leading(BuildContext context) => + isReadOnly ? super.leading(context) : null; + + @override + Widget body(BuildContext context) => DisclaimerPageBody(isReadOnly: isReadOnly); } class DisclaimerPageBody extends StatefulWidget { - DisclaimerPageBody({this.isReadOnly = true}); + DisclaimerPageBody({this.isReadOnly}); final bool isReadOnly; @override - DisclaimerBodyState createState() => DisclaimerBodyState(false); + DisclaimerBodyState createState() => DisclaimerBodyState(); } class DisclaimerBodyState extends State { - DisclaimerBodyState(this._isAccepted); static const xmrtoUrl = 'https://xmr.to/terms-of-service'; static const changenowUrl = 'https://changenow.io/terms-of-use'; static const morphUrl = 'http://morphtoken.com/terms'; - final bool _isAccepted; bool _checked = false; String _fileText = ''; @@ -51,26 +54,10 @@ class DisclaimerBodyState extends State { setState(() {}); } - Future _showAlertDialog(BuildContext context) async { - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: 'Terms and conditions', - alertContent: 'By using this app, you agree to the Terms of Agreement set forth to below', - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop() - ); - }); - } - - void _afterLayout(Duration _) => _showAlertDialog(context); - @override void initState() { super.initState(); getFileLines(); - if (_isAccepted) WidgetsBinding.instance.addPostFrameCallback(_afterLayout); } @override @@ -87,8 +74,7 @@ class DisclaimerBodyState extends State { padding: EdgeInsets.only(left: 24.0, right: 24.0), child: Column( children: [ - !_isAccepted - ? Row( + Row( children: [ Expanded( child: Text( @@ -102,13 +88,10 @@ class DisclaimerBodyState extends State { ), ) ], - ) - : Offstage(), - !_isAccepted - ? SizedBox( + ), + SizedBox( height: 20.0, - ) - : Offstage(), + ), Row( children: [ Expanded( @@ -254,8 +237,7 @@ class DisclaimerBodyState extends State { ], )), if (!widget.isReadOnly) ...[ - !_isAccepted - ? Row( + Row( children: [ Expanded( child: Container( @@ -303,25 +285,19 @@ class DisclaimerBodyState extends State { )), ), ], - ) - : Offstage(), - !_isAccepted - ? Container( + ), + Container( padding: EdgeInsets.only(left: 24.0, right: 24.0, bottom: 24.0), child: PrimaryButton( - onPressed: _checked ? () {} : null, + onPressed: _checked ? () => + Navigator.of(context).popAndPushNamed(Routes.welcome) + : null, text: 'Accept', color: Colors.green, textColor: Colors.white, ), - ) - : Offstage(), - _isAccepted - ? SizedBox( - height: 24.0, - ) - : Offstage() + ), ], ], ), diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 694f74129..bf587776f 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -173,6 +173,7 @@ class ExchangePage extends BasePage { initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled, isAmountEstimated: false, + hasRefundAddress: true, currencies: CryptoCurrency.all, onCurrencySelected: (currency) => exchangeViewModel.changeDepositCurrency( @@ -306,8 +307,7 @@ class ExchangePage extends BasePage { ), Observer(builder: (_) { final templates = exchangeViewModel.templates; - final itemCount = - exchangeViewModel.templates.length; + final itemCount = templates.length; return ListView.builder( scrollDirection: Axis.horizontal, @@ -337,24 +337,22 @@ class ExchangePage extends BasePage { alertContent: S .of(context) .confirm_delete_template, - leftButtonText: - S.of(context).delete, rightButtonText: + S.of(context).delete, + leftButtonText: S.of(context).cancel, - actionLeftButton: () { + actionRightButton: () { Navigator.of( dialogContext) .pop(); exchangeViewModel - .exchangeTemplateStore - .remove( + .removeTemplate( template: template); exchangeViewModel - .exchangeTemplateStore - .update(); + .updateTemplate(); }, - actionRightButton: () => + actionLeftButton: () => Navigator.of( dialogContext) .pop()); diff --git a/lib/src/screens/exchange/exchange_template_page.dart b/lib/src/screens/exchange/exchange_template_page.dart index 85ea1f280..c68efc473 100644 --- a/lib/src/screens/exchange/exchange_template_page.dart +++ b/lib/src/screens/exchange/exchange_template_page.dart @@ -134,6 +134,7 @@ class ExchangeTemplatePage extends BasePage { initialIsAddressEditable: exchangeViewModel .isDepositAddressEnabled, isAmountEstimated: false, + hasRefundAddress: true, currencies: CryptoCurrency.all, onCurrencySelected: (currency) => exchangeViewModel.changeDepositCurrency( @@ -220,7 +221,7 @@ class ExchangeTemplatePage extends BasePage { PrimaryButton( onPressed: () { if (_formKey.currentState.validate()) { - exchangeViewModel.exchangeTemplateStore.addTemplate( + exchangeViewModel.addTemplate( amount: exchangeViewModel.depositAmount, depositCurrency: exchangeViewModel.depositCurrency.toString(), @@ -229,7 +230,7 @@ class ExchangeTemplatePage extends BasePage { provider: exchangeViewModel.provider.toString(), depositAddress: exchangeViewModel.depositAddress, receiveAddress: exchangeViewModel.receiveAddress); - exchangeViewModel.exchangeTemplateStore.update(); + exchangeViewModel.updateTemplate(); Navigator.of(context).pop(); } }, diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 7706e7271..d74f7bcca 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -18,6 +18,7 @@ class ExchangeCard extends StatefulWidget { this.initialIsAmountEditable, this.initialIsAddressEditable, this.isAmountEstimated, + this.hasRefundAddress = false, this.currencies, this.onCurrencySelected, this.imageArrow, @@ -38,6 +39,7 @@ class ExchangeCard extends StatefulWidget { final bool initialIsAmountEditable; final bool initialIsAddressEditable; final bool isAmountEstimated; + final bool hasRefundAddress; final Image imageArrow; final Color currencyButtonColor; final Color addressButtonsColor; @@ -228,23 +230,25 @@ class ExchangeCardState extends State { : Offstage(), ]), ), - _isAddressEditable - ? Offstage() - : Padding( - padding: EdgeInsets.only(top: 20), - child: Text( - S.of(context).refund_address, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: - Theme.of(context).textTheme.subhead.decorationColor), - )), + !_isAddressEditable && widget.hasRefundAddress + ? Padding( + padding: EdgeInsets.only(top: 20), + child: Text( + S.of(context).refund_address, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: + Theme.of(context).textTheme.subhead.decorationColor), + )) + : Offstage(), _isAddressEditable ? Padding( padding: EdgeInsets.only(top: 20), child: AddressTextField( controller: addressController, + placeholder: widget.hasRefundAddress + ? S.of(context).refund_address : null, options: [ AddressTextFieldOption.paste, AddressTextFieldOption.qrCode, diff --git a/lib/src/screens/exchange/widgets/present_provider_picker.dart b/lib/src/screens/exchange/widgets/present_provider_picker.dart index f4cc7abf1..664dd90ca 100644 --- a/lib/src/screens/exchange/widgets/present_provider_picker.dart +++ b/lib/src/screens/exchange/widgets/present_provider_picker.dart @@ -58,11 +58,13 @@ class PresentProviderPicker extends StatelessWidget { final items = exchangeViewModel.providersForCurrentPair(); final selectedItem = items.indexOf(exchangeViewModel.provider); final images = []; + String description; for (var provider in items) { switch (provider.description) { case ExchangeProviderDescription.xmrto: images.add(Image.asset('assets/images/xmr_btc.png')); + description = S.of(context).picker_description; break; case ExchangeProviderDescription.changeNow: images.add(Image.asset('assets/images/change_now.png')); @@ -79,6 +81,7 @@ class PresentProviderPicker extends StatelessWidget { images: images, selectedAtIndex: selectedItem, title: S.of(context).change_exchange_provider, + description: description, onItemSelected: (ExchangeProvider provider) => exchangeViewModel.changeProvider(provider: provider)), context: context); diff --git a/lib/src/screens/restore/restore_wallet_from_seed_page.dart b/lib/src/screens/restore/restore_wallet_from_seed_page.dart index 1c6575017..6c133ad5e 100644 --- a/lib/src/screens/restore/restore_wallet_from_seed_page.dart +++ b/lib/src/screens/restore/restore_wallet_from_seed_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/screens/restore/restore_from_keys.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart'; import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; @@ -30,6 +31,7 @@ class RestoreWalletFromSeedPage extends BasePage { String get title => S.current.restore_title_from_seed; final controller = PageController(initialPage: 0); + List _pages; Widget _page(BuildContext context, int index) { if (_pages == null || _pages.isEmpty) { @@ -49,48 +51,12 @@ class RestoreWalletFromSeedPage extends BasePage { void _setPages(BuildContext context) { _pages = [ - Container( - padding: EdgeInsets.only(left: 25, right: 25), - child: Column(children: [ - SeedWidget( - maxLength: mnemonicLength(WalletType.monero), - onMnemonicChange: (seed) => null, - onFinish: () => null, - // Navigator.of(context).pushNamed( - // Routes.restoreWalletFromSeedDetails, - // arguments: [WalletType.monero, '', '']), - validator: SeedValidator(type: WalletType.monero, language: ''), - ), - GestureDetector( - onTap: () async { - final selected = await showPopUp( - context: context, - builder: (BuildContext context) => - SeedLanguagePicker(selected: 'English')); //key: _pickerKey - print('Seletec $selected'); - }, - child: Container( - color: Colors.transparent, - padding: EdgeInsets.only(top: 20.0), - child: IgnorePointer(child: BaseTextFormField( - enableInteractiveSelection: false, - readOnly: true, - hintText: 'Language', - initialValue: 'English (Seed language)')))), - BlockchainHeightWidget( - // key: _blockchainHeightKey, - onHeightChange: (height) { - // widget.walletRestorationFromKeysVM.height = height; - print(height); - }) - ])), + WalletRestoreFromSeedForm(), RestoreFromKeysFrom(), // Container(color: Colors.yellow) ]; } - List _pages; - @override Widget body(BuildContext context) { return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -198,15 +164,15 @@ class _RestoreFromSeedFormState extends State { child: Column(children: [ SeedWidget( // key: _seedKey, - maxLength: mnemonicLength(widget.type), - onMnemonicChange: (seed) => null, - onFinish: () => Navigator.of(context).pushNamed( - Routes.restoreWalletFromSeedDetails, - arguments: [widget.type, widget.language, mnemonic()]), - leading: widget.leading, - middle: widget.middle, - validator: - SeedValidator(type: widget.type, language: widget.language), + // maxLength: mnemonicLength(widget.type), + // onMnemonicChange: (seed) => null, + // onFinish: () => Navigator.of(context).pushNamed( + // Routes.restoreWalletFromSeedDetails, + // arguments: [widget.type, widget.language, mnemonic()]), + // leading: widget.leading, + // middle: widget.middle, + // validator: + // SeedValidator(type: widget.type, language: widget.language), ), BlockchainHeightWidget( // key: _blockchainHeightKey, diff --git a/lib/src/screens/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart new file mode 100644 index 000000000..5a36b81d1 --- /dev/null +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -0,0 +1,70 @@ +import 'package:flutter/services.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; +import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; + +class WalletRestoreFromKeysFrom extends StatefulWidget { + WalletRestoreFromKeysFrom({Key key}) : super(key: key); + + @override + WalletRestoreFromKeysFromState createState() => + WalletRestoreFromKeysFromState(); +} + +class WalletRestoreFromKeysFromState extends State { + WalletRestoreFromKeysFromState() + : formKey = GlobalKey(), + blockchainHeightKey = GlobalKey(), + nameController = TextEditingController(), + addressController = TextEditingController(), + viewKeyController = TextEditingController(), + spendKeyController = TextEditingController(); + + final GlobalKey formKey; + final GlobalKey blockchainHeightKey; + final TextEditingController nameController; + final TextEditingController addressController; + final TextEditingController viewKeyController; + final TextEditingController spendKeyController; + + @override + void dispose() { + nameController.dispose(); + addressController.dispose(); + viewKeyController.dispose(); + spendKeyController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 25, right: 25), + child: Form( + key: formKey, + child: Column(children: [ + BaseTextFormField( + controller: addressController, + keyboardType: TextInputType.multiline, + maxLines: null, + hintText: S.of(context).restore_address), + Container( + padding: EdgeInsets.only(top: 20.0), + child: BaseTextFormField( + controller: viewKeyController, + hintText: S.of(context).restore_view_key_private, + maxLines: null)), + Container( + padding: EdgeInsets.only(top: 20.0), + child: BaseTextFormField( + controller: spendKeyController, + hintText: S.of(context).restore_spend_key_private, + maxLines: null)), + BlockchainHeightWidget( + key: blockchainHeightKey, onHeightChange: (_) => null) + ]), + )); + } +} diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart new file mode 100644 index 000000000..69dad9086 --- /dev/null +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -0,0 +1,73 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart'; +import 'package:cake_wallet/src/widgets/seed_widget.dart'; +import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; + +class WalletRestoreFromSeedForm extends StatefulWidget { + WalletRestoreFromSeedForm({Key key}) : super(key: key); + + @override + WalletRestoreFromSeedFormState createState() => + WalletRestoreFromSeedFormState('English'); +} + +class WalletRestoreFromSeedFormState extends State { + WalletRestoreFromSeedFormState(this.language) + : seedWidgetStateKey = GlobalKey(), + blockchainHeightKey = GlobalKey(), + languageController = TextEditingController(); + + final GlobalKey seedWidgetStateKey; + final GlobalKey blockchainHeightKey; + final TextEditingController languageController; + String language; + + @override + void initState() { + _setLanguageLabel(language); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 25, right: 25), + child: Column(children: [ + SeedWidget(key: seedWidgetStateKey), + GestureDetector( + onTap: () async { + final selected = await showPopUp( + context: context, + builder: (BuildContext context) => + SeedLanguagePicker(selected: language)); + _changeLanguage(selected); + }, + child: Container( + color: Colors.transparent, + padding: EdgeInsets.only(top: 20.0), + child: IgnorePointer( + child: BaseTextFormField( + controller: languageController, + enableInteractiveSelection: false, + readOnly: true)))), + BlockchainHeightWidget( + key: blockchainHeightKey, + onHeightChange: (height) { + print(height); + }) + ])); + } + + void _changeLanguage(String language) { + setState(() { + this.language = language; + _setLanguageLabel(language); + }); + } + + void _setLanguageLabel(String language) => + languageController.text = '$language (Seed language)'; +} diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart new file mode 100644 index 000000000..090032c2a --- /dev/null +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -0,0 +1,127 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:mobx/mobx.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:smooth_page_indicator/smooth_page_indicator.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart'; +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; + +class WalletRestorePage extends BasePage { + WalletRestorePage(this.walletRestoreViewModel) + : walletRestoreFromSeedFormKey = + GlobalKey(), + walletRestoreFromKeysFormKey = + GlobalKey(), + _pages = [], + _controller = PageController(initialPage: 0) { + _pages.addAll([ + WalletRestoreFromSeedForm(key: walletRestoreFromSeedFormKey), + WalletRestoreFromKeysFrom(key: walletRestoreFromKeysFormKey) + ]); + } + + @override + Widget middle(BuildContext context) => Observer( + builder: (_) => Text( + walletRestoreViewModel.mode == WalletRestoreMode.seed + ? S.current.restore_title_from_seed + : S.current.restore_title_from_keys, + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + fontFamily: 'Poppins', + color: titleColor ?? + Theme.of(context).primaryTextTheme.title.color), + )); + + @override + String get title => S.current.restore_title_from_seed; + + final WalletRestoreViewModel walletRestoreViewModel; + final PageController _controller; + final List _pages; + final GlobalKey walletRestoreFromSeedFormKey; + final GlobalKey walletRestoreFromKeysFormKey; + + @override + Widget body(BuildContext context) { + reaction((_) => walletRestoreViewModel.state, (ExecutionState state) { + if (state is FailureState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showPopUp( + context: context, + builder: (_) { + return AlertWithOneAction( + alertTitle: S.current.new_wallet, + alertContent: state.error, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + }); + } + }); + + return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + Expanded( + child: PageView.builder( + onPageChanged: (page) { + walletRestoreViewModel.mode = + page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys; + }, + controller: _controller, + itemCount: _pages.length, + itemBuilder: (_, index) => _pages[index])), + Padding( + padding: EdgeInsets.only(top: 10), + child: SmoothPageIndicator( + controller: _controller, + count: _pages.length, + effect: ColorTransitionEffect( + spacing: 6.0, + radius: 6.0, + dotWidth: 6.0, + dotHeight: 6.0, + dotColor: Theme.of(context).hintColor.withOpacity(0.5), + activeDotColor: Theme.of(context).hintColor), + )), + Padding( + padding: EdgeInsets.only(top: 20, bottom: 40, left: 25, right: 25), + child: PrimaryButton( + text: S.of(context).restore_recover, + isDisabled: false, + onPressed: () => + walletRestoreViewModel.create(options: _credentials()), + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white)), + ]); + } + + Map _credentials() { + final credentials = {}; + + if (walletRestoreViewModel.mode == WalletRestoreMode.seed) { + credentials['seed'] = walletRestoreFromSeedFormKey + .currentState.seedWidgetStateKey.currentState.text; + credentials['height'] = walletRestoreFromSeedFormKey + .currentState.blockchainHeightKey.currentState.height; + } else { + credentials['address'] = + walletRestoreFromKeysFormKey.currentState.addressController.text; + credentials['viewKey'] = + walletRestoreFromKeysFormKey.currentState.viewKeyController.text; + credentials['spendKey'] = + walletRestoreFromKeysFormKey.currentState.spendKeyController.text; + credentials['height'] = walletRestoreFromKeysFormKey + .currentState.blockchainHeightKey.currentState.height; + } + + return credentials; + } +} diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 0490463f9..811d940dd 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -1,7 +1,9 @@ import 'dart:ui'; import 'package:cake_wallet/entities/transaction_priority.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; +import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -416,53 +418,53 @@ class SendPage extends BasePage { )), ), ), - // Observer( - // builder: (_) { - // final templates = sendViewModel.templates; - // final itemCount = templates.length; + Observer( + builder: (_) { + final templates = sendViewModel.templates; + final itemCount = templates.length; - // return ListView.builder( - // scrollDirection: Axis.horizontal, - // shrinkWrap: true, - // physics: NeverScrollableScrollPhysics(), - // itemCount: itemCount, - // itemBuilder: (context, index) { - // final template = templates[index]; + return ListView.builder( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: itemCount, + itemBuilder: (context, index) { + final template = templates[index]; - // return TemplateTile( - // key: UniqueKey(), - // to: template.name, - // amount: template.amount, - // from: template.cryptoCurrency, - // onTap: () { - // _addressController.text = template.address; - // _cryptoAmountController.text = template.amount; - // getOpenaliasRecord(context); - // }, - // onRemove: () { - // showPopUp( - // context: context, - // builder: (dialogContext) { - // return AlertWithTwoActions( - // alertTitle: S.of(context).template, - // alertContent: S.of(context).confirm_delete_template, - // leftButtonText: S.of(context).delete, - // rightButtonText: S.of(context).cancel, - // actionLeftButton: () { - // Navigator.of(dialogContext).pop(); - // sendViewModel.sendTemplateStore.remove(template: template); - // sendViewModel.sendTemplateStore.update(); - // }, - // actionRightButton: () => Navigator.of(dialogContext).pop() - // ); - // } - // ); - // }, - // ); - // } - // ); - // } - // ) + return TemplateTile( + key: UniqueKey(), + to: template.name, + amount: template.amount, + from: template.cryptoCurrency, + onTap: () { + _addressController.text = template.address; + _cryptoAmountController.text = template.amount; + getOpenaliasRecord(context); + }, + onRemove: () { + showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).template, + alertContent: S.of(context).confirm_delete_template, + rightButtonText: S.of(context).delete, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + Navigator.of(dialogContext).pop(); + sendViewModel.removeTemplate(template: template); + sendViewModel.updateTemplate(); + }, + actionLeftButton: () => Navigator.of(dialogContext).pop() + ); + } + ); + }, + ); + } + ); + } + ) ], ), ), @@ -507,6 +509,14 @@ class SendPage extends BasePage { } }); + _fiatAmountController.addListener(() { + final amount = _fiatAmountController.text; + + if (amount != sendViewModel.fiatAmount) { + sendViewModel.setFiatAmount(amount); + } + }); + reaction((_) => sendViewModel.sendAll, (bool all) { if (all) { _cryptoAmountController.text = S.current.all; @@ -571,9 +581,9 @@ class SendPage extends BasePage { sendViewModel.pendingTransaction.amountFormatted, fee: S.of(context).send_fee, feeValue: sendViewModel.pendingTransaction.feeFormatted, - leftButtonText: S.of(context).ok, - rightButtonText: S.of(context).cancel, - actionLeftButton: () { + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () { Navigator.of(context).pop(); sendViewModel.commitTransaction(); showPopUp( @@ -670,7 +680,7 @@ class SendPage extends BasePage { }); }); }, - actionRightButton: () => Navigator.of(context).pop()); + actionLeftButton: () => Navigator.of(context).pop()); }); }); } diff --git a/lib/src/screens/send/send_template_page.dart b/lib/src/screens/send/send_template_page.dart index fcda9b7af..37708fcb2 100644 --- a/lib/src/screens/send/send_template_page.dart +++ b/lib/src/screens/send/send_template_page.dart @@ -134,7 +134,7 @@ class SendTemplatePage extends BasePage { signed: false, decimal: true), inputFormatters: [ BlacklistingTextInputFormatter( - RegExp('[\\-|\\ |\\,]')) + RegExp('[\\-|\\ ]')) ], prefixIcon: Padding( padding: EdgeInsets.only(top: 9), @@ -172,7 +172,7 @@ class SendTemplatePage extends BasePage { signed: false, decimal: true), inputFormatters: [ BlacklistingTextInputFormatter( - RegExp('[\\-|\\ |\\,]')) + RegExp('[\\-|\\ ]')) ], prefixIcon: Padding( padding: EdgeInsets.only(top: 9), @@ -210,12 +210,12 @@ class SendTemplatePage extends BasePage { bottomSection: PrimaryButton( onPressed: () { if (_formKey.currentState.validate()) { - // sendViewModel.sendTemplateStore.addTemplate( - // name: _nameController.text, - // address: _addressController.text, - // cryptoCurrency: sendViewModel.currency.title, - // amount: _cryptoAmountController.text); - // sendViewModel.sendTemplateStore.update(); + sendViewModel.addTemplate( + name: _nameController.text, + address: _addressController.text, + cryptoCurrency: sendViewModel.currency.title, + amount: _cryptoAmountController.text); + sendViewModel.updateTemplate(); Navigator.of(context).pop(); } }, @@ -249,6 +249,22 @@ class SendTemplatePage extends BasePage { } }); + _cryptoAmountController.addListener(() { + final amount = _cryptoAmountController.text; + + if (amount != sendViewModel.cryptoAmount) { + sendViewModel.setCryptoAmount(amount); + } + }); + + _fiatAmountController.addListener(() { + final amount = _fiatAmountController.text; + + if (amount != sendViewModel.fiatAmount) { + sendViewModel.setFiatAmount(amount); + } + }); + _addressController.addListener(() { final address = _addressController.text; diff --git a/lib/src/screens/send/widgets/confirm_sending_alert.dart b/lib/src/screens/send/widgets/confirm_sending_alert.dart index 663de3a16..fa5a97148 100644 --- a/lib/src/screens/send/widgets/confirm_sending_alert.dart +++ b/lib/src/screens/send/widgets/confirm_sending_alert.dart @@ -55,6 +55,7 @@ class ConfirmSendingAlert extends BaseAlertDialog { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, + fontFamily: 'Poppins', color: Theme.of(context).primaryTextTheme.title.color, decoration: TextDecoration.none, ), @@ -64,6 +65,7 @@ class ConfirmSendingAlert extends BaseAlertDialog { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, + fontFamily: 'Poppins', color: Theme.of(context).primaryTextTheme.title.color, decoration: TextDecoration.none, ), @@ -79,6 +81,7 @@ class ConfirmSendingAlert extends BaseAlertDialog { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, + fontFamily: 'Poppins', color: Theme.of(context).primaryTextTheme.title.color, decoration: TextDecoration.none, ), @@ -88,6 +91,7 @@ class ConfirmSendingAlert extends BaseAlertDialog { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, + fontFamily: 'Poppins', color: Theme.of(context).primaryTextTheme.title.color, decoration: TextDecoration.none, ), diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 70bc56a40..da329bece 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -175,7 +175,7 @@ class WalletListBodyState extends State { SizedBox(height: 10.0), PrimaryImageButton( onPressed: () => - Navigator.of(context).pushNamed(Routes.restoreWalletFromSeed), + Navigator.of(context).pushNamed(Routes.restoreWallet), image: restoreWalletImage, text: S.of(context).wallet_list_restore_wallet, color: Theme.of(context).accentTextTheme.caption.color, diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index 78cd04835..7a5b65516 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -14,118 +14,178 @@ class WelcomePage extends BasePage { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Theme.of(context).backgroundColor, + backgroundColor: Theme + .of(context) + .backgroundColor, resizeToAvoidBottomPadding: false, body: SafeArea(child: body(context))); } @override Widget body(BuildContext context) { - final welcomeImage = getIt.get().isDarkTheme + final welcomeImage = getIt + .get() + .isDarkTheme ? welcomeImageDark : welcomeImageLight; final newWalletImage = Image.asset('assets/images/new_wallet.png', height: 12, width: 12, - color: Theme.of(context).accentTextTheme.headline.decorationColor); + color: Theme + .of(context) + .accentTextTheme + .headline + .decorationColor); final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', height: 12, width: 12, - color: Theme.of(context).primaryTextTheme.title.color); + color: Theme + .of(context) + .primaryTextTheme + .title + .color); - return Container( - padding: EdgeInsets.all(24), - child: Column( - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: aspectRatioImage, - child: FittedBox(child: welcomeImage, fit: BoxFit.fill) - ) - ), - Flexible( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( + return WillPopScope(onWillPop: () async => false, child: Container( + padding: EdgeInsets.all(24), + child: Column( + children: [ + Flexible( + flex: 2, + child: AspectRatio( + aspectRatio: aspectRatioImage, + child: FittedBox(child: welcomeImage, fit: BoxFit.fill) + ) + ), + Flexible( + flex: 3, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: EdgeInsets.only(top: 24), - child: Text( - S.of(context).welcome, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme.display3.color, + Column( + children: [ + Padding( + padding: EdgeInsets.only(top: 24), + child: Text( + S + .of(context) + .welcome, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme + .of(context) + .accentTextTheme + .display3 + .color, + ), + textAlign: TextAlign.center, + ), ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - S.of(context).cake_wallet, - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.bold, - color: Theme.of(context).primaryTextTheme.title.color, + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + S + .of(context) + .cake_wallet, + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Theme + .of(context) + .primaryTextTheme + .title + .color, + ), + textAlign: TextAlign.center, + ), ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - S.of(context).first_wallet_text, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).accentTextTheme.display3.color, + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + S + .of(context) + .first_wallet_text, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme + .of(context) + .accentTextTheme + .display3 + .color, + ), + textAlign: TextAlign.center, + ), ), - textAlign: TextAlign.center, - ), + ], ), - ], - ), - Column( - children: [ - Text( - S.of(context).please_make_selection, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, - color: Theme.of(context).accentTextTheme.display3.color, - ), - textAlign: TextAlign.center, - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: PrimaryImageButton( - onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome), - image: newWalletImage, - text: S.of(context).create_new, - color: Theme.of(context).accentTextTheme.subtitle.decorationColor, - textColor: Theme.of(context).accentTextTheme.headline.decorationColor, - ), - ), - Padding( - padding: EdgeInsets.only(top: 10), - child: PrimaryImageButton( - onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome), - image: restoreWalletImage, - text: S.of(context).restore_wallet, - color: Theme.of(context).accentTextTheme.caption.color, - textColor: Theme.of(context).primaryTextTheme.title.color), + Column( + children: [ + Text( + S + .of(context) + .please_make_selection, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme + .of(context) + .accentTextTheme + .display3 + .color, + ), + textAlign: TextAlign.center, + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: PrimaryImageButton( + onPressed: () => + Navigator.pushNamed(context, + Routes.newWalletFromWelcome), + image: newWalletImage, + text: S + .of(context) + .create_new, + color: Theme + .of(context) + .accentTextTheme + .subtitle + .decorationColor, + textColor: Theme + .of(context) + .accentTextTheme + .headline + .decorationColor, + ), + ), + Padding( + padding: EdgeInsets.only(top: 10), + child: PrimaryImageButton( + onPressed: () => + Navigator.pushNamed(context, + Routes.restoreWalletOptionsFromWelcome), + image: restoreWalletImage, + text: S + .of(context) + .restore_wallet, + color: Theme + .of(context) + .accentTextTheme + .caption + .color, + textColor: Theme + .of(context) + .primaryTextTheme + .title + .color), + ) + ], ) ], ) - ], ) - ) - ], - ) - ); + ], + ) + )); } } diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 4ab07f860..d72431491 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -114,7 +114,7 @@ class AddressTextField extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(6))), child: Image.asset( - 'assets/images/duplicate.png', + 'assets/images/paste_ios.png', color: iconColor ?? Theme.of(context) .primaryTextTheme diff --git a/lib/src/widgets/annotated_editable_text.dart b/lib/src/widgets/annotated_editable_text.dart new file mode 100644 index 000000000..abba51d3b --- /dev/null +++ b/lib/src/widgets/annotated_editable_text.dart @@ -0,0 +1,161 @@ +import 'package:flutter/material.dart'; + +class Annotation extends Comparable { + Annotation({@required this.range, this.style}); + + final TextRange range; + final TextStyle style; + + @override + int compareTo(Annotation other) => range.start.compareTo(other.range.start); +} + +class TextAnnotation extends Comparable { + TextAnnotation({@required this.text, this.style}); + + final TextStyle style; + final String text; + + @override + int compareTo(TextAnnotation other) => text.compareTo(other.text); +} + +class AnnotatedEditableText extends EditableText { + AnnotatedEditableText({ + Key key, + FocusNode focusNode, + TextEditingController controller, + TextStyle style, + ValueChanged onChanged, + ValueChanged onSubmitted, + Color cursorColor, + Color selectionColor, + Color backgroundCursorColor, + TextSelectionControls selectionControls, + @required this.words, + }) : textAnnotations = words + .map((word) => TextAnnotation( + text: word, + style: TextStyle( + color: Colors.black, + backgroundColor: Colors.transparent, + fontWeight: FontWeight.normal, + fontSize: 20))) + .toList(), + super( + maxLines: null, + key: key, + focusNode: focusNode, + controller: controller, + cursorColor: cursorColor, + style: style, + keyboardType: TextInputType.text, + autocorrect: false, + autofocus: false, + selectionColor: selectionColor, + selectionControls: selectionControls, + backgroundCursorColor: backgroundCursorColor, + onChanged: onChanged, + onSubmitted: onSubmitted, + toolbarOptions: const ToolbarOptions( + copy: true, + cut: true, + paste: true, + selectAll: true, + ), + enableSuggestions: false, + enableInteractiveSelection: true, + showSelectionHandles: true, + showCursor: true, + ) { + textAnnotations.add(TextAnnotation( + text: ' ', style: TextStyle(backgroundColor: Colors.transparent))); + } + + final List words; + final List textAnnotations; + + @override + AnnotatedEditableTextState createState() => AnnotatedEditableTextState(); +} + +class AnnotatedEditableTextState extends EditableTextState { + @override + AnnotatedEditableText get widget => super.widget as AnnotatedEditableText; + + List getRanges() { + final source = widget.textAnnotations + .map((item) => range(item.text, textEditingValue.text) + .map((range) => Annotation(style: item.style, range: range))) + .expand((e) => e) + .toList(); + final result = List(); + final text = textEditingValue.text; + source.sort(); + Annotation prev; + + for (var item in source) { + if (prev == null) { + if (item.range.start > 0) { + result.add(Annotation( + range: TextRange(start: 0, end: item.range.start), + style: TextStyle( + color: Colors.black, backgroundColor: Colors.transparent))); + } + result.add(item); + prev = item; + continue; + } else { + if (prev.range.end > item.range.start) { + // throw StateError('Invalid (intersecting) ranges for annotated field'); + } else if (prev.range.end < item.range.start) { + result.add(Annotation( + range: TextRange(start: prev.range.end, end: item.range.start), + style: TextStyle( + color: Colors.red, backgroundColor: Colors.transparent))); + } + + result.add(item); + prev = item; + } + } + + if (result.length > 0 && result.last.range.end < text.length) { + result.add(Annotation( + range: TextRange(start: result.last.range.end, end: text.length), + style: TextStyle( backgroundColor: Colors.transparent))); + } + return result; + } + + List range(String pattern, String source) { + final result = List(); + + for (int index = source.indexOf(pattern); + index >= 0; + index = source.indexOf(pattern, index + 1)) { + final start = index; + final end = start + pattern.length; + result.add(TextRange(start: start, end: end)); + } + + return result; + } + + @override + TextSpan buildTextSpan() { + final text = textEditingValue.text; + final ranges = getRanges(); + + if (ranges.isNotEmpty) { + return TextSpan( + style: widget.style, + children: ranges + .map((item) => TextSpan( + style: item.style, text: item.range.textInside(text))) + .toList()); + } + + return TextSpan(style: widget.style, text: text); + } +} \ No newline at end of file diff --git a/lib/src/widgets/blockchain_height_widget.dart b/lib/src/widgets/blockchain_height_widget.dart index 87d64b222..de242d451 100644 --- a/lib/src/widgets/blockchain_height_widget.dart +++ b/lib/src/widgets/blockchain_height_widget.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/date_picker.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -88,7 +89,7 @@ class BlockchainHeightState extends State { Future _selectDate(BuildContext context) async { final now = DateTime.now(); - final date = await showDatePicker( + final date = await getDate( context: context, initialDate: now.subtract(Duration(days: 1)), firstDate: DateTime(2014, DateTime.april), diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index 935237573..17db6e82e 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -12,6 +12,7 @@ class Picker extends StatefulWidget { @required this.items, this.images, @required this.title, + this.description, @required this.onItemSelected, this.mainAxisAlignment = MainAxisAlignment.start, this.isAlwaysShowScrollThumb = false @@ -21,6 +22,7 @@ class Picker extends StatefulWidget { final List items; final List images; final String title; + final String description; final Function(Item) onItemSelected; final MainAxisAlignment mainAxisAlignment; final bool isAlwaysShowScrollThumb; @@ -145,6 +147,26 @@ class PickerState extends State { ); }, ), + ((widget.description != null) + &&(widget.description.isNotEmpty)) + ? Positioned( + bottom: 24, + left: 24, + right: 24, + child: Text( + widget.description, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + fontFamily: 'Poppins', + decoration: TextDecoration.none, + color: Theme.of(context).primaryTextTheme + .title.color + ), + ) + ) + : Offstage(), widget.isAlwaysShowScrollThumb ? CakeScrollbar( backgroundHeight: backgroundHeight, diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index b2e29789e..5ccb55830 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/src/widgets/annotated_editable_text.dart'; import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -10,367 +11,33 @@ import 'package:cake_wallet/entities/mnemonic_item.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:flutter/widgets.dart'; -class Annotation extends Comparable { - Annotation({@required this.range, this.style}); - - final TextRange range; - final TextStyle style; - - @override - int compareTo(Annotation other) => range.start.compareTo(other.range.start); -} - -class TextAnnotation extends Comparable { - TextAnnotation({@required this.text, this.style}); - - final TextStyle style; - final String text; - - @override - int compareTo(TextAnnotation other) => text.compareTo(other.text); -} - -class AnnotatedEditableText extends EditableText { - AnnotatedEditableText({ - Key key, - FocusNode focusNode, - TextEditingController controller, - TextStyle style, - ValueChanged onChanged, - ValueChanged onSubmitted, - Color cursorColor, - Color selectionColor, - Color backgroundCursorColor, - TextSelectionControls selectionControls, - @required this.words, - }) : textAnnotations = words - .map((word) => TextAnnotation( - text: word, - style: TextStyle( - color: Colors.black, - backgroundColor: Colors.transparent, - fontWeight: FontWeight.normal, - fontSize: 20))) - .toList(), - super( - maxLines: null, - key: key, - focusNode: focusNode, - controller: controller, - cursorColor: cursorColor, - style: style, - keyboardType: TextInputType.text, - autocorrect: false, - autofocus: false, - selectionColor: selectionColor, - selectionControls: selectionControls, - backgroundCursorColor: backgroundCursorColor, - onChanged: onChanged, - onSubmitted: onSubmitted, - toolbarOptions: const ToolbarOptions( - copy: true, - cut: true, - paste: true, - selectAll: true, - ), - enableSuggestions: false, - enableInteractiveSelection: true, - showSelectionHandles: true, - showCursor: true, - ) { - textAnnotations.add(TextAnnotation( - text: ' ', style: TextStyle(backgroundColor: Colors.transparent))); - } - - final List words; - final List textAnnotations; - - @override - AnnotatedEditableTextState createState() => AnnotatedEditableTextState(); -} - -class AnnotatedEditableTextState extends EditableTextState { - @override - AnnotatedEditableText get widget => super.widget as AnnotatedEditableText; - - List getRanges() { - final source = widget.textAnnotations - .map((item) => range(item.text, textEditingValue.text) - .map((range) => Annotation(style: item.style, range: range))) - .expand((e) => e) - .toList(); - final result = List(); - final text = textEditingValue.text; - source.sort(); - Annotation prev; - - for (var item in source) { - if (prev == null) { - if (item.range.start > 0) { - result.add(Annotation( - range: TextRange(start: 0, end: item.range.start), - style: TextStyle( - color: Colors.black, backgroundColor: Colors.transparent))); - } - result.add(item); - prev = item; - continue; - } else { - if (prev.range.end > item.range.start) { - // throw StateError('Invalid (intersecting) ranges for annotated field'); - } else if (prev.range.end < item.range.start) { - result.add(Annotation( - range: TextRange(start: prev.range.end, end: item.range.start), - style: TextStyle( - color: Colors.red, backgroundColor: Colors.transparent))); - } - - result.add(item); - prev = item; - } - } - - if (result.length > 0 && result.last.range.end < text.length) { - result.add(Annotation( - range: TextRange(start: result.last.range.end, end: text.length), - style: TextStyle( backgroundColor: Colors.transparent))); - } - return result; - } - - List range(String pattern, String source) { - final result = List(); - - for (int index = source.indexOf(pattern); - index >= 0; - index = source.indexOf(pattern, index + 1)) { - final start = index; - final end = start + pattern.length; - result.add(TextRange(start: start, end: end)); - } - - return result; - } - - @override - TextSpan buildTextSpan() { - final text = textEditingValue.text; - final ranges = getRanges(); - - if (ranges.isNotEmpty) { - return TextSpan( - style: widget.style, - children: ranges - .map((item) => TextSpan( - style: item.style, text: item.range.textInside(text))) - .toList()); - } - - return TextSpan(style: widget.style, text: text); - } -} - class SeedWidget extends StatefulWidget { - SeedWidget( - {Key key, - this.maxLength, - this.onMnemonicChange, - this.onFinish, - this.leading, - this.middle, - this.validator}) - : super(key: key); - - final int maxLength; - final Function(List) onMnemonicChange; - final Function() onFinish; - final SeedValidator validator; - final Widget leading; - final Widget middle; + SeedWidget({Key key}) : super(key: key); @override - SeedWidgetState createState() => SeedWidgetState(maxLength: maxLength); + SeedWidgetState createState() => SeedWidgetState(); } class SeedWidgetState extends State { - SeedWidgetState({this.maxLength}); + SeedWidgetState() + : controller = TextEditingController(), + focusNode = FocusNode(), + words = + SeedValidator.getWordList(type: WalletType.monero, language: 'en'); - List items = []; - final int maxLength; - final _seedController = TextEditingController(); - final _seedTextFieldKey = GlobalKey(); - MnemonicItem selectedItem; - bool isValid; - String errorMessage; + final TextEditingController controller; + final FocusNode focusNode; + final List words; + bool _showPlaceholder; - List currentMnemonics; - bool isCurrentMnemonicValid; - String _errorMessage; + String get text => controller.text; @override void initState() { super.initState(); - showPlaceholder = true; - isValid = false; - isCurrentMnemonicValid = false; - _seedController - .addListener(() => changeCurrentMnemonic(_seedController.text)); - focusNode.addListener(() => setState(() => - showPlaceholder = !focusNode.hasFocus && controller.text.isEmpty)); + _showPlaceholder = true; } - void addMnemonic(String text) { - setState(() => items.add(MnemonicItem(text: text.trim().toLowerCase()))); - _seedController.text = ''; - - if (widget.onMnemonicChange != null) { - widget.onMnemonicChange(items); - } - } - - void mnemonicFromText(String text) { - final splitted = text.split(' '); - - if (splitted.length >= 2) { - for (final text in splitted) { - if (text == ' ' || text.isEmpty) { - continue; - } - - if (selectedItem != null) { - editTextOfSelectedMnemonic(text); - } else { - addMnemonic(text); - } - } - } - } - - void selectMnemonic(MnemonicItem item) { - setState(() { - selectedItem = item; - currentMnemonics = [item]; - - _seedController - ..text = item.text - ..selection = TextSelection.collapsed(offset: item.text.length); - }); - } - - void onMnemonicTap(MnemonicItem item) { - if (selectedItem == item) { - setState(() => selectedItem = null); - _seedController.text = ''; - return; - } - - selectMnemonic(item); - } - - void editTextOfSelectedMnemonic(String text) { - setState(() => selectedItem.changeText(text)); - selectedItem = null; - _seedController.text = ''; - - if (widget.onMnemonicChange != null) { - widget.onMnemonicChange(items); - } - } - - void clear() { - setState(() { - items = []; - selectedItem = null; - _seedController.text = ''; - - if (widget.onMnemonicChange != null) { - widget.onMnemonicChange(items); - } - }); - } - - void invalidate() => setState(() => isValid = false); - - void validated() => setState(() => isValid = true); - - void setErrorMessage(String errorMessage) => - setState(() => this.errorMessage = errorMessage); - - void replaceText(String text) { - setState(() => items = []); - mnemonicFromText(text); - } - - void changeCurrentMnemonic(String text) { - setState(() { - final trimmedText = text.trim(); - final splitted = trimmedText.split(' '); - _errorMessage = null; - - if (text == null) { - currentMnemonics = []; - isCurrentMnemonicValid = false; - return; - } - - currentMnemonics = - splitted.map((text) => MnemonicItem(text: text)).toList(); - - var isValid = true; - - for (final word in currentMnemonics) { - isValid = widget.validator.isValid(word); - - if (!isValid) { - break; - } - } - - isCurrentMnemonicValid = isValid; - }); - } - - void saveCurrentMnemonicToItems() { - setState(() { - if (selectedItem != null) { - selectedItem.changeText(currentMnemonics.first.text.trim()); - selectedItem = null; - } else { - items.addAll(currentMnemonics); - } - - currentMnemonics = []; - _seedController.text = ''; - }); - } - - void showErrorIfExist() { - setState(() => _errorMessage = - !isCurrentMnemonicValid ? S.current.incorrect_seed : null); - } - - bool isSeedValid() { - bool isValid; - - for (final item in items) { - isValid = widget.validator.isValid(item); - - if (!isValid) { - break; - } - } - - return isValid; - } - - final controller = TextEditingController(); - final focusNode = FocusNode(); - - bool showPlaceholder; - - final words = - SeedValidator.getWordList(type: WalletType.monero, language: 'en'); - Future _pasteAddress() async { final value = await Clipboard.getData('text/plain'); @@ -381,7 +48,6 @@ class SeedWidgetState extends State { @override Widget build(BuildContext context) { - print('build'); return Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -389,7 +55,7 @@ class SeedWidgetState extends State { children: [ Stack(children: [ SizedBox(height: 35), - if (showPlaceholder) + if (_showPlaceholder) Positioned( top: 10, left: 0, @@ -418,15 +84,15 @@ class SeedWidgetState extends State { child: InkWell( onTap: () async => _pasteAddress(), child: Container( - decoration: BoxDecoration( - color: Theme.of(context).hintColor, - borderRadius: - BorderRadius.all(Radius.circular(6))), - child: Image.asset('assets/images/duplicate.png', - color: Theme.of(context) - .primaryTextTheme - .display1 - .decorationColor)), + decoration: BoxDecoration( + color: Theme.of(context).hintColor, + borderRadius: BorderRadius.all(Radius.circular(6))), + // child: Image.asset('assets/images/duplicate.png', + // color: Theme.of(context) + // .primaryTextTheme + // .display1 + // .decorationColor) + ), ))) ]), Container( @@ -434,237 +100,5 @@ class SeedWidgetState extends State { height: 1.0, color: Theme.of(context).primaryTextTheme.title.backgroundColor), ])); - // return Container( - // child: Column(children: [ - // Flexible( - // fit: FlexFit.tight, - // flex: 2, - // child: Container( - // width: double.infinity, - // height: double.infinity, - // padding: EdgeInsets.all(0), - // decoration: BoxDecoration( - // borderRadius: BorderRadius.only( - // bottomLeft: Radius.circular(24), - // bottomRight: Radius.circular(24)), - // gradient: LinearGradient(colors: [ - // Theme.of(context).primaryTextTheme.subhead.color, - // Theme.of(context).primaryTextTheme.subhead.decorationColor, - // ], begin: Alignment.topLeft, end: Alignment.bottomRight)), - // child: Column( - // children: [ - // CupertinoNavigationBar( - // leading: widget.leading, - // middle: widget.middle, - // backgroundColor: Colors.transparent, - // border: null, - // ), - // Expanded( - // child: Container( - // padding: EdgeInsets.all(24), - // alignment: Alignment.topLeft, - // child: SingleChildScrollView( - // child: Column( - // mainAxisAlignment: MainAxisAlignment.start, - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text( - // S.of(context).restore_active_seed, - // style: TextStyle( - // fontSize: 14, - // fontWeight: FontWeight.w500, - // color: Theme.of(context) - // .textTheme - // .overline - // .backgroundColor), - // ), - // Padding( - // padding: EdgeInsets.only(top: 5), - // child: Wrap( - // children: items.map((item) { - // final isValid = - // widget.validator.isValid(item); - // final isSelected = selectedItem == item; - // - // return InkWell( - // onTap: () => onMnemonicTap(item), - // child: Container( - // decoration: BoxDecoration( - // color: isValid - // ? Colors.transparent - // : Palette.red), - // margin: EdgeInsets.only( - // right: 7, bottom: 8), - // child: Text( - // item.toString(), - // style: TextStyle( - // color: isValid - // ? Colors.white - // : Colors.grey, - // fontSize: 16, - // fontWeight: isSelected - // ? FontWeight.w900 - // : FontWeight.w600, - // decoration: isSelected - // ? TextDecoration.underline - // : TextDecoration.none), - // )), - // ); - // }).toList(), - // )) - // ], - // ), - // ), - // )) - // ], - // )), - // ), - // Flexible( - // fit: FlexFit.tight, - // flex: 3, - // child: Padding( - // padding: - // EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24), - // child: Column( - // mainAxisAlignment: MainAxisAlignment.start, - // crossAxisAlignment: CrossAxisAlignment.center, - // children: [ - // Text( - // S.of(context).restore_new_seed, - // style: TextStyle( - // fontSize: 20, - // fontWeight: FontWeight.w500, - // color: - // Theme.of(context).primaryTextTheme.title.color), - // ), - // Padding( - // padding: EdgeInsets.only(top: 24), - // child: TextFormField( - // key: _seedTextFieldKey, - // onFieldSubmitted: (text) => isCurrentMnemonicValid - // ? saveCurrentMnemonicToItems() - // : null, - // style: TextStyle( - // fontSize: 16.0, - // fontWeight: FontWeight.normal, - // color: - // Theme.of(context).primaryTextTheme.title.color), - // controller: _seedController, - // textInputAction: TextInputAction.done, - // decoration: InputDecoration( - // suffixIcon: GestureDetector( - // behavior: HitTestBehavior.opaque, - // child: ConstrainedBox( - // constraints: BoxConstraints(maxWidth: 145), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Text('${items.length}/$maxLength', - // style: TextStyle( - // color: Theme.of(context) - // .accentTextTheme - // .display2 - // .decorationColor, - // fontWeight: FontWeight.normal, - // fontSize: 16)), - // SizedBox(width: 10), - // InkWell( - // onTap: () async => - // Clipboard.getData('text/plain').then( - // (clipboard) => - // replaceText(clipboard.text)), - // child: Container( - // height: 35, - // padding: EdgeInsets.all(7), - // decoration: BoxDecoration( - // color: Theme.of(context) - // .accentTextTheme - // .caption - // .color, - // borderRadius: - // BorderRadius.circular(10.0)), - // child: Text( - // S.of(context).paste, - // style: TextStyle( - // color: Palette.blueCraiola), - // )), - // ) - // ], - // ), - // ), - // ), - // hintStyle: TextStyle( - // color: Theme.of(context) - // .accentTextTheme - // .display2 - // .decorationColor, - // fontWeight: FontWeight.normal, - // fontSize: 16), - // hintText: - // S.of(context).restore_from_seed_placeholder, - // errorText: _errorMessage, - // focusedBorder: UnderlineInputBorder( - // borderSide: BorderSide( - // color: Theme.of(context) - // .accentTextTheme - // .subtitle - // .backgroundColor, - // width: 1.0)), - // enabledBorder: UnderlineInputBorder( - // borderSide: BorderSide( - // color: Theme.of(context) - // .accentTextTheme - // .subtitle - // .backgroundColor, - // width: 1.0))), - // enableInteractiveSelection: false, - // ), - // ) - // ]), - // )), - // Padding( - // padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - // child: Row( - // children: [ - // Flexible( - // child: Padding( - // padding: EdgeInsets.only(right: 8), - // child: PrimaryButton( - // onPressed: clear, - // text: S.of(context).clear, - // color: Colors.orange, - // textColor: Colors.white, - // isDisabled: items.isEmpty, - // ), - // )), - // Flexible( - // child: Padding( - // padding: EdgeInsets.only(left: 8), - // child: (selectedItem == null && items.length == maxLength) - // ? PrimaryButton( - // text: S.of(context).restore_next, - // isDisabled: !isSeedValid(), - // onPressed: () => widget.onFinish != null - // ? widget.onFinish() - // : null, - // color: Theme.of(context).accentTextTheme.body2.color, - // textColor: Colors.white) - // : PrimaryButton( - // text: selectedItem != null - // ? S.of(context).save - // : S.of(context).add_new_word, - // onPressed: () => isCurrentMnemonicValid - // ? saveCurrentMnemonicToItems() - // : null, - // onDisabledPressed: () => showErrorIfExist(), - // isDisabled: !isCurrentMnemonicValid, - // color: Theme.of(context).accentTextTheme.body2.color, - // textColor: Colors.white), - // ), - // ) - // ], - // )) - // ]), - // ); } } diff --git a/lib/utils/date_picker.dart b/lib/utils/date_picker.dart new file mode 100644 index 000000000..78e49666d --- /dev/null +++ b/lib/utils/date_picker.dart @@ -0,0 +1,54 @@ +import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +Future getDate({ + @required BuildContext context, + @required DateTime initialDate, + @required DateTime firstDate, + @required DateTime lastDate}) { + + if (Platform.isIOS) { + return _buildCupertinoDataPicker(context, initialDate, firstDate, lastDate); + } + + return _buildMaterialDataPicker(context, initialDate, firstDate, lastDate); +} + +Future _buildMaterialDataPicker( + BuildContext context, + DateTime initialDate, + DateTime firstDate, + DateTime lastDate) async { + return await showDatePicker( + context: context, + initialDate: initialDate, + firstDate: firstDate, + lastDate: lastDate, + helpText: ''); +} + +Future _buildCupertinoDataPicker( + BuildContext context, + DateTime initialDate, + DateTime firstDate, + DateTime lastDate) async { + DateTime date; + await showModalBottomSheet( + context: context, + builder: (_) { + return Container( + height: MediaQuery.of(context).size.height / 3, + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.date, + onDateTimeChanged: (picked) => date = picked, + initialDateTime: initialDate, + minimumDate: firstDate, + maximumDate: lastDate, + backgroundColor: Colors.white, + ), + ); + } + ); + return date; +} \ No newline at end of file diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index caca84b83..b6efa310f 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -27,10 +27,10 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { ExchangeViewModelBase( - {this.wallet, + this.wallet, this.trades, - this.exchangeTemplateStore, - this.tradesStore}) { + this._exchangeTemplateStore, + this.tradesStore) { providerList = [ XMRTOExchangeProvider(), ChangeNowExchangeProvider(), @@ -54,7 +54,7 @@ abstract class ExchangeViewModelBase with Store { final WalletBase wallet; final Box trades; - final ExchangeTemplateStore exchangeTemplateStore; + final ExchangeTemplateStore _exchangeTemplateStore; final TradesStore tradesStore; @observable @@ -101,7 +101,7 @@ abstract class ExchangeViewModelBase with Store { @computed ObservableList get templates => - exchangeTemplateStore.templates; + _exchangeTemplateStore.templates; @action void changeProvider({ExchangeProvider provider}) { @@ -267,6 +267,22 @@ abstract class ExchangeViewModelBase with Store { _onPairChange(); } + void updateTemplate() => _exchangeTemplateStore.update(); + + void addTemplate({String amount, String depositCurrency, String receiveCurrency, + String provider, String depositAddress, String receiveAddress}) => + _exchangeTemplateStore.addTemplate( + amount: amount, + depositCurrency: depositCurrency, + receiveCurrency: receiveCurrency, + provider: provider, + depositAddress: depositAddress, + receiveAddress: receiveAddress + ); + + void removeTemplate({ExchangeTemplate template}) => + _exchangeTemplateStore.remove(template: template); + List providersForCurrentPair() { return _providersForPair(from: depositCurrency, to: receiveCurrency); } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index eb4ec2f4a..3cdcc7bb2 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,4 +1,6 @@ import 'package:cake_wallet/entities/openalias_record.dart'; +import 'package:cake_wallet/entities/template.dart'; +import 'package:cake_wallet/store/templates/send_template_store.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/template_validator.dart'; @@ -29,7 +31,8 @@ class SendViewModel = SendViewModelBase with _$SendViewModel; abstract class SendViewModelBase with Store { SendViewModelBase( - this._wallet, this._settingsStore, this._fiatConversationStore) + this._wallet, this._settingsStore, this._sendTemplateStore, + this._fiatConversationStore) : state = InitialExecutionState(), _cryptoNumberFormat = NumberFormat(), sendAll = false { @@ -88,8 +91,12 @@ abstract class SendViewModelBase with Store { @computed bool get isReadyForSend => _wallet.syncStatus is SyncedSyncStatus; + @computed + ObservableList