This commit is contained in:
M 2020-10-09 21:34:21 +03:00
parent 04e7c18841
commit f9cc21478a
16 changed files with 796 additions and 303 deletions

View file

@ -219,6 +219,8 @@ extern "C"
bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error) bool create_wallet(char *path, char *password, char *language, int32_t networkType, char *error)
{ {
Monero::WalletManagerFactory::setLogLevel(4);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType); Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager();
Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType); Monero::Wallet *wallet = walletManager->createWallet(path, password, language, _networkType);
@ -228,9 +230,9 @@ extern "C"
wallet->statusWithErrorString(status, errorString); wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok) if (wallet->status() != Monero::Wallet::Status_Ok)
{ {
error = strdup(errorString.c_str()); error = strdup(wallet->errorString().c_str());
return false; return false;
} }
@ -254,7 +256,7 @@ extern "C"
wallet->statusWithErrorString(status, errorString); wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok) if (status != Monero::Wallet::Status_Ok || !errorString.empty())
{ {
error = strdup(errorString.c_str()); error = strdup(errorString.c_str());
return false; return false;
@ -282,7 +284,7 @@ extern "C"
wallet->statusWithErrorString(status, errorString); wallet->statusWithErrorString(status, errorString);
if (status != Monero::Wallet::Status_Ok) if (status != Monero::Wallet::Status_Ok || !errorString.empty())
{ {
error = strdup(errorString.c_str()); error = strdup(errorString.c_str());
return false; return false;
@ -349,11 +351,13 @@ extern "C"
uint64_t get_full_balance(uint32_t account_index) uint64_t get_full_balance(uint32_t account_index)
{ {
// return 0;
return get_current_wallet()->balance(account_index); return get_current_wallet()->balance(account_index);
} }
uint64_t get_unlocked_balance(uint32_t account_index) uint64_t get_unlocked_balance(uint32_t account_index)
{ {
// return 0;
return get_current_wallet()->unlockedBalance(account_index); return get_current_wallet()->unlockedBalance(account_index);
} }
@ -681,6 +685,7 @@ extern "C"
void on_startup() void on_startup()
{ {
Monero::Utils::onStartup(); Monero::Utils::onStartup();
Monero::WalletManagerFactory::setLogLevel(4);
} }
void rescan_blockchain() void rescan_blockchain()

View file

@ -2,4 +2,7 @@ class WalletCreationException implements Exception {
WalletCreationException({this.message}); WalletCreationException({this.message});
final String message; final String message;
@override
String toString() => message;
} }

View file

@ -1,4 +1,5 @@
import 'dart:ffi'; import 'dart:ffi';
import 'package:cw_monero/wallet.dart';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:cw_monero/convert_utf8_to_string.dart'; import 'package:cw_monero/convert_utf8_to_string.dart';
@ -49,6 +50,8 @@ void createWalletSync(
throw WalletCreationException( throw WalletCreationException(
message: convertUTF8ToString(pointer: errorMessagePointer)); message: convertUTF8ToString(pointer: errorMessagePointer));
} }
// setupNodeSync(address: "node.moneroworld.com:18089");
} }
bool isWalletExistSync({String path}) { bool isWalletExistSync({String path}) {

View file

@ -354,7 +354,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -493,7 +493,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -526,7 +526,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (

View file

@ -137,6 +137,11 @@ Future<void> ios_migrate_wallet_passwords() async {
final walletsDir = Directory('${appDocDir.path}/wallets'); final walletsDir = Directory('${appDocDir.path}/wallets');
final moneroWalletsDir = Directory('${walletsDir.path}/monero'); final moneroWalletsDir = Directory('${walletsDir.path}/monero');
if (!moneroWalletsDir.existsSync() || moneroWalletsDir.listSync().isEmpty) {
await prefs.setBool('ios_migration_wallet_passwords_completed', true);
return;
}
moneroWalletsDir.listSync().forEach((item) async { moneroWalletsDir.listSync().forEach((item) async {
try { try {
if (item is Directory) { if (item is Directory) {

View file

@ -95,7 +95,7 @@ Future<void> initialSetup(
tradesSource: tradesSource, tradesSource: tradesSource,
templates: templates, templates: templates,
exchangeTemplates: exchangeTemplates); exchangeTemplates: exchangeTemplates);
await bootstrap(navigatorKey); bootstrap(navigatorKey);
monero_wallet.onStartup(); monero_wallet.onStartup();
} }

View file

@ -67,7 +67,7 @@ class MoneroWalletService extends WalletService<
return wallet; return wallet;
} catch (e) { } catch (e) {
// TODO: Implement Exception for wallet list service. // TODO: Implement Exception for wallet list service.
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: ${e.toString()}');
rethrow; rethrow;
} }
} }

View file

@ -8,22 +8,25 @@ import 'package:cake_wallet/store/authentication_store.dart';
ReactionDisposer _onAuthenticationStateChange; ReactionDisposer _onAuthenticationStateChange;
void startAuthenticationStateChange(AuthenticationStore authenticationStore, void startAuthenticationStateChange(AuthenticationStore authenticationStore,
GlobalKey<NavigatorState> navigatorKey) { @required GlobalKey<NavigatorState> navigatorKey) {
_onAuthenticationStateChange ??= autorun((_) async { _onAuthenticationStateChange ??= autorun((_) async {
final state = authenticationStore.state; final state = authenticationStore.state;
if (state == AuthenticationState.installed) { if (state == AuthenticationState.installed) {
await loadCurrentWallet(); await loadCurrentWallet();
return;
} }
if (state == AuthenticationState.allowed) { if (state == AuthenticationState.allowed) {
await navigatorKey.currentState await navigatorKey.currentState
.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false); .pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
} }
if (state == AuthenticationState.denied) { if (state == AuthenticationState.denied) {
await navigatorKey.currentState await navigatorKey.currentState
.pushNamedAndRemoveUntil(Routes.welcome, (_) => false); .pushNamedAndRemoveUntil(Routes.welcome, (_) => false);
return;
} }
}); });
} }

View file

@ -58,7 +58,7 @@ class Router {
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>( builder: (_) => getIt.get<SetupPinCodePage>(
param1: (BuildContext context, dynamic _) => param1: (BuildContext context, dynamic _) =>
Navigator.pushNamed(context, Routes.newWalletType)), Navigator.pushNamed(context, Routes.newWallet)),
fullscreenDialog: true); fullscreenDialog: true);
case Routes.newWalletType: case Routes.newWalletType:
@ -128,7 +128,7 @@ class Router {
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>( builder: (_) => getIt.get<SetupPinCodePage>(
param1: (BuildContext context, dynamic _) => param1: (BuildContext context, dynamic _) =>
Navigator.pushNamed(context, Routes.restoreWalletType)), Navigator.pushNamed(context, Routes.restoreWalletFromSeed)),
fullscreenDialog: true); fullscreenDialog: true);
case Routes.seed: case Routes.seed:
@ -137,15 +137,15 @@ class Router {
getIt.get<WalletSeedPage>(param1: settings.arguments as bool)); getIt.get<WalletSeedPage>(param1: settings.arguments as bool));
case Routes.restoreWalletFromSeed: case Routes.restoreWalletFromSeed:
final args = settings.arguments as List<dynamic>; // final args = settings.arguments as List<dynamic>;
final type = args.first as WalletType; final type = WalletType.monero; //args.first as WalletType;
final language = type == WalletType.monero // final language = type == WalletType.monero
? args[1] as String // ? args[1] as String
: LanguageList.english; // : LanguageList.english;
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => builder: (_) =>
RestoreWalletFromSeedPage(type: type, language: language)); RestoreWalletFromSeedPage(type: type));
case Routes.restoreWalletFromKeys: case Routes.restoreWalletFromKeys:
final args = settings.arguments as List<dynamic>; final args = settings.arguments as List<dynamic>;

View file

@ -0,0 +1,83 @@
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 RestoreFromKeysFrom extends StatefulWidget {
@override
_RestoreFromKeysFromState createState() => _RestoreFromKeysFromState();
}
class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
final _formKey = GlobalKey<FormState>();
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
final _nameController = TextEditingController();
final _addressController = TextEditingController();
final _viewKeyController = TextEditingController();
final _spendKeyController = TextEditingController();
final _wifController = TextEditingController();
@override
void initState() {
// _nameController.addListener(() =>
// widget.walletRestorationFromKeysVM.name = _nameController.text);
// _addressController.addListener(() =>
// widget.walletRestorationFromKeysVM.address = _addressController.text);
// _viewKeyController.addListener(() =>
// widget.walletRestorationFromKeysVM.viewKey = _viewKeyController.text);
// _spendKeyController.addListener(() =>
// widget.walletRestorationFromKeysVM.spendKey = _spendKeyController.text);
// _wifController.addListener(() =>
// widget.walletRestorationFromKeysVM.wif = _wifController.text);
super.initState();
}
@override
void dispose() {
_nameController.dispose();
_addressController.dispose();
_viewKeyController.dispose();
_spendKeyController.dispose();
_wifController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
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,
)),
Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _spendKeyController,
hintText: S.of(context).restore_spend_key_private,
)),
BlockchainHeightWidget(
key: _blockchainHeightKey,
onHeightChange: (height) {
// widget.walletRestorationFromKeysVM.height = height;
print(height);
}),
]),
),
);
}
}

View file

@ -1,3 +1,11 @@
import 'package:cake_wallet/src/screens/restore/restore_from_keys.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';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
@ -7,48 +15,162 @@ import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/core/seed_validator.dart'; import 'package:cake_wallet/core/seed_validator.dart';
import 'package:cake_wallet/core/mnemonic_length.dart'; import 'package:cake_wallet/core/mnemonic_length.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class RestoreWalletFromSeedPage extends BasePage { class RestoreWalletFromSeedPage extends BasePage {
RestoreWalletFromSeedPage({@required this.type, @required this.language}); RestoreWalletFromSeedPage({@required this.type});
final WalletType type; final WalletType type;
final String language; final String language = 'en';
final formKey = GlobalKey<_RestoreFromSeedFormState>();
// final formKey = GlobalKey<_RestoreFromSeedFormState>();
// final formKey = GlobalKey<_RestoreFromSeedFormState>();
@override @override
String get title => S.current.restore_title_from_seed; String get title => S.current.restore_title_from_seed;
@override final controller = PageController(initialPage: 0);
Color get titleColor => Colors.white;
Widget _page(BuildContext context, int index) {
if (_pages == null || _pages.isEmpty) {
_setPages(context);
}
return _pages[index];
}
int _pageLength(BuildContext context) {
if (_pages == null || _pages.isEmpty) {
_setPages(context);
}
return _pages.length;
}
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);
})
])),
RestoreFromKeysFrom(),
// Container(color: Colors.yellow)
];
}
List<Widget> _pages;
@override @override
Color get backgroundLightColor => Colors.transparent; Widget body(BuildContext context) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Expanded(
child: PageView.builder(
onPageChanged: (page) {
print('Page index $page');
},
controller: controller,
itemCount: _pageLength(context),
itemBuilder: (context, index) => _page(context, index))),
Padding(
padding: EdgeInsets.only(top: 10),
child: SmoothPageIndicator(
controller: controller,
count: _pageLength(context),
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: () => null,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white)),
]);
@override // return GestureDetector(
Color get backgroundDarkColor => Colors.transparent; // onTap: () =>
// SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
@override // child: ScrollableWithBottomSection(
bool get resizeToAvoidBottomPadding => false; // bottomSection: Column(children: [
// GestureDetector(
@override // onTap: () {},
Widget body(BuildContext context) => // child: Text('Switch to restore from keys',
RestoreFromSeedForm(key: formKey, type: type, language: language, // style: TextStyle(fontSize: 15, color: Theme.of(context).hintColor))),
leading: leading(context), middle: middle(context)); // SizedBox(height: 30),
// PrimaryButton(
@override // text: S.of(context).restore_next,
Widget build(BuildContext context) { // isDisabled: false,
return Scaffold( // onPressed: () => null,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding, // color: Theme.of(context).accentTextTheme.body2.color,
body: Container( // textColor: Colors.white)
color: Theme.of(context).backgroundColor, // ]),
child: body(context) // contentPadding: EdgeInsets.only(bottom: 24),
) // content: Container(
); // padding: EdgeInsets.only(left: 25, right: 25),
// child: Column(children: [
// SeedWidget(
// maxLength: mnemonicLength(type),
// onMnemonicChange: (seed) => null,
// onFinish: () => Navigator.of(context).pushNamed(
// Routes.restoreWalletFromSeedDetails,
// arguments: [type, language, '']),
// validator: SeedValidator(type: type, language: language),
// ),
// // SizedBox(height: 15),
// // BaseTextFormField(hintText: 'Language', initialValue: 'English'),
// BlockchainHeightWidget(
// // key: _blockchainHeightKey,
// onHeightChange: (height) {
// // widget.walletRestorationFromKeysVM.height = height;
// print(height);
// })
// ]))),
// );
} }
} }
class RestoreFromSeedForm extends StatefulWidget { class RestoreFromSeedForm extends StatefulWidget {
RestoreFromSeedForm({Key key, this.type, this.language, this.leading, this.middle}) : super(key: key); RestoreFromSeedForm(
{Key key, this.type, this.language, this.leading, this.middle})
: super(key: key);
final WalletType type; final WalletType type;
final String language; final String language;
final Widget leading; final Widget leading;
@ -59,27 +181,46 @@ class RestoreFromSeedForm extends StatefulWidget {
} }
class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> { class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
final _seedKey = GlobalKey<SeedWidgetState>(); // final _seedKey = GlobalKey<SeedWidgetState>();
String mnemonic() => _seedKey.currentState.items.map((e) => e.text).join(' '); String mnemonic() =>
''; // _seedKey.currentState.items.map((e) => e.text).join(' ');
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () => onTap: () =>
SystemChannels.textInput.invokeMethod<void>('TextInput.hide'), SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
child: SeedWidget( child: Container(
key: _seedKey, padding: EdgeInsets.only(left: 25, right: 25),
maxLength: mnemonicLength(widget.type), // color: Colors.blue,
onMnemonicChange: (seed) => null, // height: 300,
onFinish: () => Navigator.of(context).pushNamed( child: Column(children: [
Routes.restoreWalletFromSeedDetails, SeedWidget(
arguments: [widget.type, widget.language, mnemonic()]), // key: _seedKey,
leading: widget.leading, maxLength: mnemonicLength(widget.type),
middle: widget.middle, onMnemonicChange: (seed) => null,
validator: onFinish: () => Navigator.of(context).pushNamed(
SeedValidator(type: widget.type, language: widget.language), 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,
onHeightChange: (height) {
// widget.walletRestorationFromKeysVM.height = height;
print(height);
}),
Container(
color: Colors.green,
width: 100,
height: 56,
child: BaseTextFormField(
hintText: 'Language', initialValue: 'English')),
])),
); );
} }
} }

View file

@ -35,13 +35,15 @@ class SeedLanguageFormState extends State<SeedLanguageForm> {
static const aspectRatioImage = 1.22; static const aspectRatioImage = 1.22;
final walletNameImage = Image.asset('assets/images/wallet_name.png'); final walletNameImage = Image.asset('assets/images/wallet_name.png');
final walletNameLightImage = Image.asset('assets/images/wallet_name_light.png'); final walletNameLightImage =
Image.asset('assets/images/wallet_name_light.png');
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>(); final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final walletImage = getIt.get<SettingsStore>().isDarkTheme final walletImage = getIt.get<SettingsStore>().isDarkTheme
? walletNameImage : walletNameLightImage; ? walletNameImage
: walletNameLightImage;
return Container( return Container(
padding: EdgeInsets.only(top: 24), padding: EdgeInsets.only(top: 24),
@ -78,8 +80,8 @@ class SeedLanguageFormState extends State<SeedLanguageForm> {
bottomSection: Observer( bottomSection: Observer(
builder: (context) { builder: (context) {
return PrimaryButton( return PrimaryButton(
onPressed: () => widget onPressed: () => widget.onConfirm(
.onConfirm(context, _languageSelectorKey.currentState.selected), context, _languageSelectorKey.currentState.selected),
text: S.of(context).seed_language_next, text: S.of(context).seed_language_next,
color: Colors.green, color: Colors.green,
textColor: Colors.white); textColor: Colors.white);

View file

@ -175,7 +175,7 @@ class WalletListBodyState extends State<WalletListBody> {
SizedBox(height: 10.0), SizedBox(height: 10.0),
PrimaryImageButton( PrimaryImageButton(
onPressed: () => onPressed: () =>
Navigator.of(context).pushNamed(Routes.restoreWalletType), Navigator.of(context).pushNamed(Routes.restoreWalletFromSeed),
image: restoreWalletImage, image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet, text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).accentTextTheme.caption.color, color: Theme.of(context).accentTextTheme.caption.color,

View file

@ -103,7 +103,7 @@ class WelcomePage extends BasePage {
Padding( Padding(
padding: EdgeInsets.only(top: 24), padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton( child: PrimaryImageButton(
onPressed: () => Navigator.pushNamed(context, Routes.newWallet), onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome),
image: newWalletImage, image: newWalletImage,
text: S.of(context).create_new, text: S.of(context).create_new,
color: Theme.of(context).accentTextTheme.subtitle.decorationColor, color: Theme.of(context).accentTextTheme.subtitle.decorationColor,
@ -113,7 +113,7 @@ class WelcomePage extends BasePage {
Padding( Padding(
padding: EdgeInsets.only(top: 10), padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton( child: PrimaryImageButton(
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptions), onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome),
image: restoreWalletImage, image: restoreWalletImage,
text: S.of(context).restore_wallet, text: S.of(context).restore_wallet,
color: Theme.of(context).accentTextTheme.caption.color, color: Theme.of(context).accentTextTheme.caption.color,

View file

@ -19,11 +19,14 @@ class BaseTextFormField extends StatelessWidget {
this.suffix, this.suffix,
this.suffixIcon, this.suffixIcon,
this.enabled = true, this.enabled = true,
this.readOnly = false,
this.enableInteractiveSelection = true,
this.validator, this.validator,
this.textStyle, this.textStyle,
this.placeholderTextStyle, this.placeholderTextStyle,
this.maxLength, this.maxLength,
this.focusNode}); this.focusNode,
this.initialValue});
final TextEditingController controller; final TextEditingController controller;
final TextInputType keyboardType; final TextInputType keyboardType;
@ -46,10 +49,16 @@ class BaseTextFormField extends StatelessWidget {
final TextStyle textStyle; final TextStyle textStyle;
final int maxLength; final int maxLength;
final FocusNode focusNode; final FocusNode focusNode;
final bool readOnly;
final bool enableInteractiveSelection;
String initialValue;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return TextFormField( return TextFormField(
enableInteractiveSelection: enableInteractiveSelection,
readOnly: readOnly,
initialValue: initialValue,
focusNode: focusNode, focusNode: focusNode,
controller: controller, controller: controller,
keyboardType: keyboardType, keyboardType: keyboardType,
@ -60,9 +69,11 @@ class BaseTextFormField extends StatelessWidget {
inputFormatters: inputFormatters, inputFormatters: inputFormatters,
enabled: enabled, enabled: enabled,
maxLength: maxLength, maxLength: maxLength,
style: textStyle ?? TextStyle( style: textStyle ??
fontSize: 16.0, TextStyle(
color: textColor ?? Theme.of(context).primaryTextTheme.title.color), fontSize: 16.0,
color:
textColor ?? Theme.of(context).primaryTextTheme.title.color),
decoration: InputDecoration( decoration: InputDecoration(
prefix: prefix, prefix: prefix,
prefixIcon: prefixIcon, prefixIcon: prefixIcon,
@ -75,15 +86,18 @@ class BaseTextFormField extends StatelessWidget {
hintText: hintText, hintText: hintText,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor, color: borderColor ??
Theme.of(context).primaryTextTheme.title.backgroundColor,
width: 1.0)), width: 1.0)),
disabledBorder: UnderlineInputBorder( disabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor, color: borderColor ??
Theme.of(context).primaryTextTheme.title.backgroundColor,
width: 1.0)), width: 1.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor, color: borderColor ??
Theme.of(context).primaryTextTheme.title.backgroundColor,
width: 1.0))), width: 1.0))),
validator: validator, validator: validator,
); );

View file

@ -1,3 +1,5 @@
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -8,6 +10,166 @@ import 'package:cake_wallet/entities/mnemonic_item.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/widgets.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 { class SeedWidget extends StatefulWidget {
SeedWidget( SeedWidget(
{Key key, {Key key,
@ -48,10 +210,13 @@ class SeedWidgetState extends State<SeedWidget> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
showPlaceholder = true;
isValid = false; isValid = false;
isCurrentMnemonicValid = false; isCurrentMnemonicValid = false;
_seedController _seedController
.addListener(() => changeCurrentMnemonic(_seedController.text)); .addListener(() => changeCurrentMnemonic(_seedController.text));
focusNode.addListener(() => setState(() =>
showPlaceholder = !focusNode.hasFocus && controller.text.isEmpty));
} }
void addMnemonic(String text) { void addMnemonic(String text) {
@ -198,239 +363,308 @@ class SeedWidgetState extends State<SeedWidget> {
return isValid; 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');
if (value?.text?.isNotEmpty ?? false) {
controller.text = value.text;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print('build');
return Container( return Container(
child: Column(children: [ child: Column(
Flexible( crossAxisAlignment: CrossAxisAlignment.start,
fit: FlexFit.tight, mainAxisAlignment: MainAxisAlignment.start,
flex: 2, children: [
child: Container( Stack(children: [
width: double.infinity, SizedBox(height: 35),
height: double.infinity, if (showPlaceholder)
padding: EdgeInsets.all(0), Positioned(
decoration: BoxDecoration( top: 10,
borderRadius: BorderRadius.only( left: 0,
bottomLeft: Radius.circular(24), child: Text('Enter your seed',
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( style: TextStyle(
fontSize: 20, fontSize: 16.0, color: Theme.of(context).hintColor))),
fontWeight: FontWeight.w500, Padding(
color: padding: EdgeInsets.only(right: 40, top: 10),
Theme.of(context).primaryTextTheme.title.color), child: AnnotatedEditableText(
), cursorColor: Colors.green,
Padding( backgroundCursorColor: Colors.blue,
padding: EdgeInsets.only(top: 24), style: TextStyle(
child: TextFormField( fontSize: 20,
key: _seedTextFieldKey, color: Colors.red,
onFieldSubmitted: (text) => isCurrentMnemonicValid fontWeight: FontWeight.normal,
? saveCurrentMnemonicToItems() backgroundColor: Colors.transparent),
: null, focusNode: focusNode,
style: TextStyle( controller: controller,
fontSize: 16.0, words: words)),
fontWeight: FontWeight.normal, Positioned(
color: top: 0,
Theme.of(context).primaryTextTheme.title.color), right: 0,
controller: _seedController, child: Container(
textInputAction: TextInputAction.done, width: 34,
decoration: InputDecoration( height: 34,
suffixIcon: GestureDetector( child: InkWell(
behavior: HitTestBehavior.opaque, onTap: () async => _pasteAddress(),
child: ConstrainedBox( child: Container(
constraints: BoxConstraints(maxWidth: 145), decoration: BoxDecoration(
child: Row( color: Theme.of(context).hintColor,
mainAxisAlignment: MainAxisAlignment.end, borderRadius:
children: <Widget>[ BorderRadius.all(Radius.circular(6))),
Text('${items.length}/$maxLength', child: Image.asset('assets/images/duplicate.png',
style: TextStyle( color: Theme.of(context)
color: Theme.of(context) .primaryTextTheme
.accentTextTheme .display1
.display2 .decorationColor)),
.decorationColor, )))
fontWeight: FontWeight.normal, ]),
fontSize: 16)), Container(
SizedBox(width: 10), margin: EdgeInsets.only(top: 15),
InkWell( height: 1.0,
onTap: () async => color: Theme.of(context).primaryTextTheme.title.backgroundColor),
Clipboard.getData('text/plain').then( ]));
(clipboard) => // return Container(
replaceText(clipboard.text)), // child: Column(children: [
child: Container( // Flexible(
height: 35, // fit: FlexFit.tight,
padding: EdgeInsets.all(7), // flex: 2,
decoration: BoxDecoration( // child: Container(
color: Theme.of(context) // width: double.infinity,
.accentTextTheme // height: double.infinity,
.caption // padding: EdgeInsets.all(0),
.color, // decoration: BoxDecoration(
borderRadius: // borderRadius: BorderRadius.only(
BorderRadius.circular(10.0)), // bottomLeft: Radius.circular(24),
child: Text( // bottomRight: Radius.circular(24)),
S.of(context).paste, // gradient: LinearGradient(colors: [
style: TextStyle( // Theme.of(context).primaryTextTheme.subhead.color,
color: Palette.blueCraiola), // Theme.of(context).primaryTextTheme.subhead.decorationColor,
)), // ], begin: Alignment.topLeft, end: Alignment.bottomRight)),
) // child: Column(
], // children: <Widget>[
), // CupertinoNavigationBar(
), // leading: widget.leading,
), // middle: widget.middle,
hintStyle: TextStyle( // backgroundColor: Colors.transparent,
color: Theme.of(context) // border: null,
.accentTextTheme // ),
.display2 // Expanded(
.decorationColor, // child: Container(
fontWeight: FontWeight.normal, // padding: EdgeInsets.all(24),
fontSize: 16), // alignment: Alignment.topLeft,
hintText: // child: SingleChildScrollView(
S.of(context).restore_from_seed_placeholder, // child: Column(
errorText: _errorMessage, // mainAxisAlignment: MainAxisAlignment.start,
focusedBorder: UnderlineInputBorder( // crossAxisAlignment: CrossAxisAlignment.start,
borderSide: BorderSide( // children: <Widget>[
color: Theme.of(context) // Text(
.accentTextTheme // S.of(context).restore_active_seed,
.subtitle // style: TextStyle(
.backgroundColor, // fontSize: 14,
width: 1.0)), // fontWeight: FontWeight.w500,
enabledBorder: UnderlineInputBorder( // color: Theme.of(context)
borderSide: BorderSide( // .textTheme
color: Theme.of(context) // .overline
.accentTextTheme // .backgroundColor),
.subtitle // ),
.backgroundColor, // Padding(
width: 1.0))), // padding: EdgeInsets.only(top: 5),
enableInteractiveSelection: false, // child: Wrap(
), // children: items.map((item) {
) // final isValid =
]), // widget.validator.isValid(item);
)), // final isSelected = selectedItem == item;
Padding( //
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24), // return InkWell(
child: Row( // onTap: () => onMnemonicTap(item),
children: <Widget>[ // child: Container(
Flexible( // decoration: BoxDecoration(
child: Padding( // color: isValid
padding: EdgeInsets.only(right: 8), // ? Colors.transparent
child: PrimaryButton( // : Palette.red),
onPressed: clear, // margin: EdgeInsets.only(
text: S.of(context).clear, // right: 7, bottom: 8),
color: Colors.orange, // child: Text(
textColor: Colors.white, // item.toString(),
isDisabled: items.isEmpty, // style: TextStyle(
), // color: isValid
)), // ? Colors.white
Flexible( // : Colors.grey,
child: Padding( // fontSize: 16,
padding: EdgeInsets.only(left: 8), // fontWeight: isSelected
child: (selectedItem == null && items.length == maxLength) // ? FontWeight.w900
? PrimaryButton( // : FontWeight.w600,
text: S.of(context).restore_next, // decoration: isSelected
isDisabled: !isSeedValid(), // ? TextDecoration.underline
onPressed: () => widget.onFinish != null // : TextDecoration.none),
? widget.onFinish() // )),
: null, // );
color: Theme.of(context).accentTextTheme.body2.color, // }).toList(),
textColor: Colors.white) // ))
: PrimaryButton( // ],
text: selectedItem != null // ),
? S.of(context).save // ),
: S.of(context).add_new_word, // ))
onPressed: () => isCurrentMnemonicValid // ],
? saveCurrentMnemonicToItems() // )),
: null, // ),
onDisabledPressed: () => showErrorIfExist(), // Flexible(
isDisabled: !isCurrentMnemonicValid, // fit: FlexFit.tight,
color: Theme.of(context).accentTextTheme.body2.color, // flex: 3,
textColor: Colors.white), // 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),
// ),
// )
// ],
// ))
// ]),
// );
} }
} }