This commit is contained in:
M 2020-10-14 00:11:04 +03:00
commit 598b8c9b9c
49 changed files with 1019 additions and 885 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 B

BIN
assets/images/paste_ios.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

View file

@ -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 = (

View file

@ -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<WalletAddressEditOrCreateViewModel>(param1: item)));
// getIt.get<SendTemplateStore>()
getIt.registerFactory<SendViewModel>(() => SendViewModel(
getIt.get<AppStore>().wallet,
getIt.get<AppStore>().settingsStore,
getIt.get<SendTemplateStore>(),
getIt.get<FiatConversionStore>()));
getIt.registerFactory(
@ -318,10 +320,10 @@ Future setup(
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
getIt.registerFactory(() => ExchangeViewModel(
wallet: getIt.get<AppStore>().wallet,
exchangeTemplateStore: getIt.get<ExchangeTemplateStore>(),
trades: tradesSource,
tradesStore: getIt.get<TradesStore>()));
getIt.get<AppStore>().wallet,
tradesSource,
getIt.get<ExchangeTemplateStore>(),
getIt.get<TradesStore>()));
getIt.registerFactory(() => ExchangeTradeViewModel(
wallet: getIt.get<AppStore>().wallet,
@ -371,4 +373,12 @@ Future setup(
getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>()));
getIt.registerFactory(() => LanguageListPage(getIt.get<SettingsStore>()));
getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>(
(type, _) => WalletRestoreViewModel(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), walletInfoSource,
type: type));
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) =>
WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
}

View file

@ -29,7 +29,8 @@ Future<void> migrate_android_v1() async {
await android_migrate_wallets(appDocDir: appDocDir);
}
Future<void> ios_migrate_v1(Box<WalletInfo> walletInfoSource, Box<Trade> tradeSource, Box<Contact> contactSource) async {
Future<void> ios_migrate_v1(Box<WalletInfo> walletInfoSource,
Box<Trade> tradeSource, Box<Contact> contactSource) async {
final prefs = await SharedPreferences.getInstance();
if (prefs.getBool('ios_migration_v1_completed') ?? false) {
@ -390,7 +391,7 @@ Future<void> ios_migrate_trades_list(Box<Trade> tradeSource) async {
Future<void> ios_migrate_address_book(Box<Contact> contactSource) async {
final prefs = await SharedPreferences.getInstance();
if (prefs.getBool('ios_migration_address_book_completed') ?? false) {
return;
}

View file

@ -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 => "恢复钱包";

View file

@ -120,7 +120,7 @@ class App extends StatelessWidget {
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
final authenticationStore = getIt.get<AuthenticationStore>();
final initialRoute = authenticationStore.state == AuthenticationState.denied
? Routes.welcome
? Routes.disclaimer
: Routes.login;
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(

View file

@ -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<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
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<WalletSeedPage>(param1: settings.arguments as bool));
case Routes.restoreWallet:
return MaterialPageRoute<void>(
builder: (_) =>
getIt.get<WalletRestorePage>(param1: WalletType.monero));
case Routes.restoreWalletFromSeed:
// final args = settings.arguments as List<dynamic>;
final type = WalletType.monero; //args.first as WalletType;
@ -144,8 +150,7 @@ class Router {
// : LanguageList.english;
return CupertinoPageRoute<void>(
builder: (_) =>
RestoreWalletFromSeedPage(type: type));
builder: (_) => RestoreWalletFromSeedPage(type: type));
case Routes.restoreWalletFromKeys:
final args = settings.arguments as List<dynamic>;

View file

@ -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';
}

View file

@ -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<DisclaimerPageBody> {
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<DisclaimerPageBody> {
setState(() {});
}
Future<void> _showAlertDialog(BuildContext context) async {
await showPopUp<void>(
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<DisclaimerPageBody> {
padding: EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
!_isAccepted
? Row(
Row(
children: <Widget>[
Expanded(
child: Text(
@ -102,13 +88,10 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
),
)
],
)
: Offstage(),
!_isAccepted
? SizedBox(
),
SizedBox(
height: 20.0,
)
: Offstage(),
),
Row(
children: <Widget>[
Expanded(
@ -254,8 +237,7 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
],
)),
if (!widget.isReadOnly) ...[
!_isAccepted
? Row(
Row(
children: <Widget>[
Expanded(
child: Container(
@ -303,25 +285,19 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
)),
),
],
)
: 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()
),
],
],
),

View file

@ -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());

View file

@ -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();
}
},

View file

@ -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<ExchangeCard> {
: 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,

View file

@ -58,11 +58,13 @@ class PresentProviderPicker extends StatelessWidget {
final items = exchangeViewModel.providersForCurrentPair();
final selectedItem = items.indexOf(exchangeViewModel.provider);
final images = <Image>[];
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);

View file

@ -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<Widget> _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 = <Widget>[
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<String>(
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<Widget> _pages;
@override
Widget body(BuildContext context) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
@ -198,15 +164,15 @@ class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
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,

View file

@ -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<WalletRestoreFromKeysFrom> {
WalletRestoreFromKeysFromState()
: formKey = GlobalKey<FormState>(),
blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
nameController = TextEditingController(),
addressController = TextEditingController(),
viewKeyController = TextEditingController(),
spendKeyController = TextEditingController();
final GlobalKey<FormState> formKey;
final GlobalKey<BlockchainHeightState> 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: <Widget>[
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)
]),
));
}
}

View file

@ -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<WalletRestoreFromSeedForm> {
WalletRestoreFromSeedFormState(this.language)
: seedWidgetStateKey = GlobalKey<SeedWidgetState>(),
blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
languageController = TextEditingController();
final GlobalKey<SeedWidgetState> seedWidgetStateKey;
final GlobalKey<BlockchainHeightState> 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<String>(
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)';
}

View file

@ -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<WalletRestoreFromSeedFormState>(),
walletRestoreFromKeysFormKey =
GlobalKey<WalletRestoreFromKeysFromState>(),
_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<Widget> _pages;
final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
@override
Widget body(BuildContext context) {
reaction((_) => walletRestoreViewModel.state, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
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<String, dynamic> _credentials() {
final credentials = <String, dynamic>{};
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;
}
}

View file

@ -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<void>(
// 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<void>(
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<void>(
@ -670,7 +680,7 @@ class SendPage extends BasePage {
});
});
},
actionRightButton: () => Navigator.of(context).pop());
actionLeftButton: () => Navigator.of(context).pop());
});
});
}

View file

@ -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;

View file

@ -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,
),

View file

@ -175,7 +175,7 @@ class WalletListBodyState extends State<WalletListBody> {
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,

View file

@ -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<SettingsStore>().isDarkTheme
final welcomeImage = getIt
.get<SettingsStore>()
.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: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)
)
),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
return WillPopScope(onWillPop: () async => false, child: Container(
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)
)
),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
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: <Widget>[
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: <Widget>[
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: <Widget>[
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),
)
],
)
],
)
],
)
)
],
)
);
],
)
));
}
}

View file

@ -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

View file

@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
class Annotation extends Comparable<Annotation> {
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> {
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<String> onChanged,
ValueChanged<String> 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<String> words;
final List<TextAnnotation> textAnnotations;
@override
AnnotatedEditableTextState createState() => AnnotatedEditableTextState();
}
class AnnotatedEditableTextState extends EditableTextState {
@override
AnnotatedEditableText get widget => super.widget as AnnotatedEditableText;
List<Annotation> 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<Annotation>();
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<TextRange> range(String pattern, String source) {
final result = List<TextRange>();
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);
}
}

View file

@ -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<BlockchainHeightWidget> {
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),

View file

@ -12,6 +12,7 @@ class Picker<Item extends Object> 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<Item extends Object> extends StatefulWidget {
final List<Item> items;
final List<Image> images;
final String title;
final String description;
final Function(Item) onItemSelected;
final MainAxisAlignment mainAxisAlignment;
final bool isAlwaysShowScrollThumb;
@ -145,6 +147,26 @@ class PickerState<Item> extends State<Picker> {
);
},
),
((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,

View file

@ -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> {
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> {
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<String> onChanged,
ValueChanged<String> 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<String> words;
final List<TextAnnotation> textAnnotations;
@override
AnnotatedEditableTextState createState() => AnnotatedEditableTextState();
}
class AnnotatedEditableTextState extends EditableTextState {
@override
AnnotatedEditableText get widget => super.widget as AnnotatedEditableText;
List<Annotation> 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<Annotation>();
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<TextRange> range(String pattern, String source) {
final result = List<TextRange>();
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<MnemonicItem>) 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<SeedWidget> {
SeedWidgetState({this.maxLength});
SeedWidgetState()
: controller = TextEditingController(),
focusNode = FocusNode(),
words =
SeedValidator.getWordList(type: WalletType.monero, language: 'en');
List<MnemonicItem> items = <MnemonicItem>[];
final int maxLength;
final _seedController = TextEditingController();
final _seedTextFieldKey = GlobalKey();
MnemonicItem selectedItem;
bool isValid;
String errorMessage;
final TextEditingController controller;
final FocusNode focusNode;
final List<String> words;
bool _showPlaceholder;
List<MnemonicItem> 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<void> _pasteAddress() async {
final value = await Clipboard.getData('text/plain');
@ -381,7 +48,6 @@ class SeedWidgetState extends State<SeedWidget> {
@override
Widget build(BuildContext context) {
print('build');
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -389,7 +55,7 @@ class SeedWidgetState extends State<SeedWidget> {
children: [
Stack(children: [
SizedBox(height: 35),
if (showPlaceholder)
if (_showPlaceholder)
Positioned(
top: 10,
left: 0,
@ -418,15 +84,15 @@ class SeedWidgetState extends State<SeedWidget> {
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<SeedWidget> {
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: <Widget>[
// 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: <Widget>[
// 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: <Widget>[
// 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: <Widget>[
// 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: <Widget>[
// 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),
// ),
// )
// ],
// ))
// ]),
// );
}
}

View file

@ -0,0 +1,54 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
Future<DateTime> 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<DateTime> _buildMaterialDataPicker(
BuildContext context,
DateTime initialDate,
DateTime firstDate,
DateTime lastDate) async {
return await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: firstDate,
lastDate: lastDate,
helpText: '');
}
Future<DateTime> _buildCupertinoDataPicker(
BuildContext context,
DateTime initialDate,
DateTime firstDate,
DateTime lastDate) async {
DateTime date;
await showModalBottomSheet<void>(
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;
}

View file

@ -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<Trade> trades;
final ExchangeTemplateStore exchangeTemplateStore;
final ExchangeTemplateStore _exchangeTemplateStore;
final TradesStore tradesStore;
@observable
@ -101,7 +101,7 @@ abstract class ExchangeViewModelBase with Store {
@computed
ObservableList<ExchangeTemplate> 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<ExchangeProvider> providersForCurrentPair() {
return _providersForPair(from: depositCurrency, to: receiveCurrency);
}

View file

@ -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<Template> get templates => _sendTemplateStore.templates;
final WalletBase _wallet;
final SettingsStore _settingsStore;
final SendTemplateStore _sendTemplateStore;
final FiatConversionStore _fiatConversationStore;
final NumberFormat _cryptoNumberFormat;
@ -219,4 +226,17 @@ abstract class SendViewModelBase with Store {
_cryptoNumberFormat.maximumFractionDigits = maximumFractionDigits;
}
void updateTemplate() => _sendTemplateStore.update();
void addTemplate({String name, String address, String cryptoCurrency,
String amount}) => _sendTemplateStore
.addTemplate(
name: name,
address: address,
cryptoCurrency: cryptoCurrency,
amount: amount);
void removeTemplate({Template template}) =>
_sendTemplateStore.remove(template: template);
}

View file

@ -144,7 +144,7 @@ abstract class SettingsViewModelBase with Store {
RegularListItem(
title: S.current.settings_terms_and_conditions,
handler: (BuildContext context) =>
Navigator.of(context).pushNamed(Routes.disclaimer),
Navigator.of(context).pushNamed(Routes.readDisclaimer),
),
RegularListItem(
title: S.current.faq,

View file

@ -0,0 +1,54 @@
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/core/wallet_credentials.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/entities/wallet_info.dart';
import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
part 'wallet_restore_view_model.g.dart';
enum WalletRestoreMode { seed, keys }
class WalletRestoreViewModel = WalletRestoreViewModelBase
with _$WalletRestoreViewModel;
abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
WalletRestoreViewModelBase(AppStore appStore, this._walletCreationService,
Box<WalletInfo> walletInfoSource,
{@required WalletType type})
: super(appStore, walletInfoSource, type: type, isRecovery: true) {
mode = WalletRestoreMode.seed;
}
@observable
WalletRestoreMode mode;
final WalletCreationService _walletCreationService;
@override
WalletCredentials getCredentials(dynamic options) {
final password = generateWalletPassword(type);
// switch (type) {
// case WalletType.monero:
// return MoneroRestoreWalletFromSeedCredentials(
// name: name, height: height, mnemonic: seed, password: password);
// case WalletType.bitcoin:
// return BitcoinRestoreWalletFromSeedCredentials(
// name: name, mnemonic: seed, password: password);
// default:
// return null;
// }
return null;
}
@override
Future<WalletBase> process(WalletCredentials credentials) async =>
_walletCreationService.restoreFromSeed(credentials);
}

View file

@ -389,5 +389,7 @@
"template" : "Vorlage",
"confirm_delete_template" : "Diese Aktion löscht diese Vorlage. Möchten Sie fortfahren?",
"confirm_delete_wallet" : "Diese Aktion löscht diese Brieftasche. Möchten Sie fortfahren?"
"confirm_delete_wallet" : "Diese Aktion löscht diese Brieftasche. Möchten Sie fortfahren?",
"picker_description" : "Um ChangeNOW oder MorphToken zu wählen, ändern Sie bitte zuerst Ihr Handelspaar"
}

View file

@ -389,5 +389,7 @@
"template" : "Template",
"confirm_delete_template" : "This action will delete this template. Do you wish to continue?",
"confirm_delete_wallet" : "This action will delete this wallet. Do you wish to continue?"
"confirm_delete_wallet" : "This action will delete this wallet. Do you wish to continue?",
"picker_description" : "To choose ChangeNOW or MorphToken, please change your trading pair first"
}

View file

@ -389,5 +389,7 @@
"template" : "Plantilla",
"confirm_delete_template" : "Esta acción eliminará esta plantilla. ¿Desea continuar?",
"confirm_delete_wallet" : "Esta acción eliminará esta billetera. ¿Desea continuar?"
"confirm_delete_wallet" : "Esta acción eliminará esta billetera. ¿Desea continuar?",
"picker_description" : "Para elegir ChangeNOW o MorphToken, primero cambie su par comercial"
}

View file

@ -389,5 +389,7 @@
"template" : "खाका",
"confirm_delete_template" : "यह क्रिया इस टेम्पलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?",
"confirm_delete_wallet" : "यह क्रिया इस वॉलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?"
"confirm_delete_wallet" : "यह क्रिया इस वॉलेट को हटा देगी। क्या आप जारी रखना चाहते हैं?",
"picker_description" : "ChangeNOW या MorphToken चुनने के लिए, कृपया अपनी ट्रेडिंग जोड़ी को पहले बदलें"
}

View file

@ -389,5 +389,7 @@
"template" : "テンプレート",
"confirm_delete_template" : "この操作により、このテンプレートが削除されます。 続行しますか?",
"confirm_delete_wallet" : "このアクションにより、このウォレットが削除されます。 続行しますか?"
"confirm_delete_wallet" : "このアクションにより、このウォレットが削除されます。 続行しますか?",
"picker_description" : "ChangeNOWまたはMorphTokenを選択するには、最初にトレーディングペアを変更してください"
}

View file

@ -389,5 +389,7 @@
"template" : "주형",
"confirm_delete_template" : "이 작업은이 템플릿을 삭제합니다. 계속 하시겠습니까?",
"confirm_delete_wallet" : "이 작업은이 지갑을 삭제합니다. 계속 하시겠습니까?"
"confirm_delete_wallet" : "이 작업은이 지갑을 삭제합니다. 계속 하시겠습니까?",
"picker_description" : "ChangeNOW 또는 MorphToken을 선택하려면 먼저 거래 쌍을 변경하십시오."
}

View file

@ -389,5 +389,7 @@
"template" : "Sjabloon",
"confirm_delete_template" : "Met deze actie wordt deze sjabloon verwijderd. Wilt u doorgaan?",
"confirm_delete_wallet" : "Met deze actie wordt deze portemonnee verwijderd. Wilt u doorgaan?"
"confirm_delete_wallet" : "Met deze actie wordt deze portemonnee verwijderd. Wilt u doorgaan?",
"picker_description" : "Om ChangeNOW of MorphToken te kiezen, moet u eerst uw handelspaar wijzigen"
}

View file

@ -389,5 +389,7 @@
"template" : "Szablon",
"confirm_delete_template" : "Ta czynność usunie ten szablon. Czy chcesz kontynuować?",
"confirm_delete_wallet" : "Ta czynność usunie ten portfel. Czy chcesz kontynuować?"
"confirm_delete_wallet" : "Ta czynność usunie ten portfel. Czy chcesz kontynuować?",
"picker_description" : "Aby wybrać ChangeNOW lub MorphToken, najpierw zmień swoją parę handlową"
}

View file

@ -389,5 +389,7 @@
"template" : "Modelo",
"confirm_delete_template" : "Esta ação excluirá este modelo. Você deseja continuar?",
"confirm_delete_wallet" : "Esta ação excluirá esta carteira. Você deseja continuar?"
"confirm_delete_wallet" : "Esta ação excluirá esta carteira. Você deseja continuar?",
"picker_description" : "Para escolher ChangeNOW ou MorphToken, altere primeiro o seu par de negociação"
}

View file

@ -389,5 +389,7 @@
"template" : "Шаблон",
"confirm_delete_template" : "Это действие удалит шаблон. Вы хотите продолжить?",
"confirm_delete_wallet" : "Это действие удалит кошелек. Вы хотите продолжить?"
"confirm_delete_wallet" : "Это действие удалит кошелек. Вы хотите продолжить?",
"picker_description" : "Чтобы выбрать ChangeNOW или MorphToken, сначала смените пару для обмена"
}

View file

@ -389,5 +389,7 @@
"template" : "Шаблон",
"confirm_delete_template" : "Ця дія видалить шаблон. Ви хочете продовжити?",
"confirm_delete_wallet" : "Ця дія видалить гаманець. Ви хочете продовжити?"
"confirm_delete_wallet" : "Ця дія видалить гаманець. Ви хочете продовжити?",
"picker_description" : "Щоб вибрати ChangeNOW або MorphToken, спочатку змініть пару для обміну"
}

View file

@ -389,5 +389,7 @@
"template" : "模板",
"confirm_delete_template" : "此操作將刪除此模板。 你想繼續嗎?",
"confirm_delete_wallet" : "此操作將刪除此錢包。 你想繼續嗎?"
"confirm_delete_wallet" : "此操作將刪除此錢包。 你想繼續嗎?",
"picker_description" : "要選擇ChangeNOW或MorphToken請先更改您的交易對"
}