commit
598b8c9b9c
Before Width: | Height: | Size: 549 B |
After Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 603 B |
After Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 403 B |
After Width: | Height: | Size: 903 B |
@ -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)
|
||||
]),
|
||||
));
|
||||
}
|
||||
}
|
@ -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)';
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
Loading…
Reference in new issue