2020-01-16 16:05:07 +00:00
|
|
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
2020-01-04 19:31:52 +00:00
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'package:cake_wallet/palette.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/english.dart';
|
2020-02-28 20:16:39 +00:00
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/chinese_simplified.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/dutch.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/german.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/japanese.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/portuguese.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/russian.dart';
|
|
|
|
import 'package:cake_wallet/src/domain/monero/mnemonics/spanish.dart';
|
2020-01-04 19:31:52 +00:00
|
|
|
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
|
|
|
|
import 'package:cake_wallet/generated/i18n.dart';
|
|
|
|
|
|
|
|
class SeedWidget extends StatefulWidget {
|
2020-02-28 20:16:39 +00:00
|
|
|
SeedWidget({Key key, this.onMnemoticChange, this.onFinish, this.seedLanguage}) : super(key: key) {
|
|
|
|
switch (seedLanguage) {
|
|
|
|
case 'English':
|
|
|
|
words = EnglishMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'Chinese (simplified)':
|
|
|
|
words = ChineseSimplifiedMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'Dutch':
|
|
|
|
words = DutchMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'German':
|
|
|
|
words = GermanMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'Japanese':
|
|
|
|
words = JapaneseMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'Portuguese':
|
|
|
|
words = PortugueseMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'Russian':
|
|
|
|
words = RussianMnemonics.words;
|
|
|
|
break;
|
|
|
|
case 'Spanish':
|
|
|
|
words = SpanishMnemonics.words;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
words = EnglishMnemonics.words;
|
|
|
|
}
|
|
|
|
}
|
2020-01-04 19:31:52 +00:00
|
|
|
|
2020-01-08 12:26:34 +00:00
|
|
|
final Function(List<MnemoticItem>) onMnemoticChange;
|
2020-01-16 16:05:07 +00:00
|
|
|
final Function() onFinish;
|
2020-02-28 20:16:39 +00:00
|
|
|
final String seedLanguage;
|
|
|
|
List<String> words;
|
2020-01-08 12:26:34 +00:00
|
|
|
|
|
|
|
@override
|
2020-01-04 19:31:52 +00:00
|
|
|
SeedWidgetState createState() => SeedWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class SeedWidgetState extends State<SeedWidget> {
|
2020-01-16 16:05:07 +00:00
|
|
|
static const maxLength = 25;
|
|
|
|
|
2020-01-04 19:31:52 +00:00
|
|
|
List<MnemoticItem> items = <MnemoticItem>[];
|
|
|
|
final _seedController = TextEditingController();
|
2020-01-16 16:05:07 +00:00
|
|
|
final _seedTextFieldKey = GlobalKey();
|
2020-01-04 19:31:52 +00:00
|
|
|
MnemoticItem selectedItem;
|
|
|
|
bool isValid;
|
|
|
|
String errorMessage;
|
|
|
|
|
2020-01-17 13:26:17 +00:00
|
|
|
List<MnemoticItem> currentMnemotics;
|
2020-01-16 16:05:07 +00:00
|
|
|
bool isCurrentMnemoticValid;
|
2020-01-17 13:26:17 +00:00
|
|
|
String _errorMessage;
|
2020-01-16 16:05:07 +00:00
|
|
|
|
2020-01-04 19:31:52 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
2020-01-16 16:05:07 +00:00
|
|
|
isValid = false;
|
|
|
|
isCurrentMnemoticValid = false;
|
|
|
|
_seedController
|
|
|
|
.addListener(() => changeCurrentMnemotic(_seedController.text));
|
2020-01-04 19:31:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void addMnemotic(String text) {
|
|
|
|
setState(() => items.add(MnemoticItem(
|
2020-02-28 20:16:39 +00:00
|
|
|
text: text.trim().toLowerCase(), dic: widget.words)));
|
2020-01-04 19:31:52 +00:00
|
|
|
_seedController.text = '';
|
|
|
|
|
|
|
|
if (widget.onMnemoticChange != null) {
|
|
|
|
widget.onMnemoticChange(items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mnemoticFromText(String text) {
|
|
|
|
final splitted = text.split(' ');
|
|
|
|
|
|
|
|
if (splitted.length >= 2) {
|
|
|
|
for (final text in splitted) {
|
|
|
|
if (text == ' ' || text.isEmpty) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectedItem != null) {
|
|
|
|
editTextOfSelectedMnemotic(text);
|
|
|
|
} else {
|
|
|
|
addMnemotic(text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void selectMnemotic(MnemoticItem item) {
|
2020-01-16 16:05:07 +00:00
|
|
|
setState(() {
|
|
|
|
selectedItem = item;
|
2020-01-17 13:26:17 +00:00
|
|
|
currentMnemotics = [item];
|
2020-01-16 16:05:07 +00:00
|
|
|
|
|
|
|
_seedController
|
|
|
|
..text = item.text
|
|
|
|
..selection = TextSelection.collapsed(offset: item.text.length);
|
|
|
|
});
|
2020-01-04 19:31:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void onMnemoticTap(MnemoticItem item) {
|
|
|
|
if (selectedItem == item) {
|
|
|
|
setState(() => selectedItem = null);
|
|
|
|
_seedController.text = '';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectMnemotic(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
void editTextOfSelectedMnemotic(String text) {
|
|
|
|
setState(() => selectedItem.changeText(text));
|
|
|
|
selectedItem = null;
|
|
|
|
_seedController.text = '';
|
|
|
|
|
|
|
|
if (widget.onMnemoticChange != null) {
|
|
|
|
widget.onMnemoticChange(items);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear() {
|
|
|
|
setState(() {
|
|
|
|
items = [];
|
2020-01-16 16:05:07 +00:00
|
|
|
selectedItem = null;
|
2020-01-04 19:31:52 +00:00
|
|
|
_seedController.text = '';
|
|
|
|
|
|
|
|
if (widget.onMnemoticChange != null) {
|
|
|
|
widget.onMnemoticChange(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 = []);
|
|
|
|
mnemoticFromText(text);
|
|
|
|
}
|
|
|
|
|
2020-01-16 16:05:07 +00:00
|
|
|
void changeCurrentMnemotic(String text) {
|
|
|
|
setState(() {
|
2020-01-17 13:26:17 +00:00
|
|
|
final trimmedText = text.trim();
|
|
|
|
final splitted = trimmedText.split(' ');
|
|
|
|
_errorMessage = null;
|
|
|
|
|
2020-01-16 16:05:07 +00:00
|
|
|
if (text == null) {
|
2020-01-17 13:26:17 +00:00
|
|
|
currentMnemotics = [];
|
2020-01-16 16:05:07 +00:00
|
|
|
isCurrentMnemoticValid = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-17 13:26:17 +00:00
|
|
|
currentMnemotics = splitted
|
2020-02-28 20:16:39 +00:00
|
|
|
.map((text) => MnemoticItem(text: text, dic: widget.words))
|
2020-01-17 13:26:17 +00:00
|
|
|
.toList();
|
|
|
|
|
|
|
|
bool isValid = true;
|
|
|
|
|
|
|
|
for (final word in currentMnemotics) {
|
|
|
|
isValid = word.isCorrect();
|
|
|
|
|
|
|
|
if (!isValid) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isCurrentMnemoticValid = isValid;
|
2020-01-16 16:05:07 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void saveCurrentMnemoticToItems() {
|
|
|
|
setState(() {
|
|
|
|
if (selectedItem != null) {
|
2020-01-17 13:26:17 +00:00
|
|
|
selectedItem.changeText(currentMnemotics.first.text.trim());
|
2020-01-16 16:05:07 +00:00
|
|
|
selectedItem = null;
|
|
|
|
} else {
|
2020-01-17 13:26:17 +00:00
|
|
|
items.addAll(currentMnemotics);
|
2020-01-16 16:05:07 +00:00
|
|
|
}
|
|
|
|
|
2020-01-17 13:26:17 +00:00
|
|
|
currentMnemotics = [];
|
2020-01-16 16:05:07 +00:00
|
|
|
_seedController.text = '';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-17 13:26:17 +00:00
|
|
|
void showErrorIfExist() {
|
|
|
|
setState(() => _errorMessage =
|
|
|
|
!isCurrentMnemoticValid ? S.current.incorrect_seed : null);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isSeedValid() {
|
|
|
|
bool isValid;
|
|
|
|
|
|
|
|
for (final item in items) {
|
|
|
|
isValid = item.isCorrect();
|
|
|
|
|
|
|
|
if (!isValid) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return isValid;
|
|
|
|
}
|
|
|
|
|
2020-01-04 19:31:52 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Container(
|
|
|
|
child: Column(children: [
|
2020-01-16 16:05:07 +00:00
|
|
|
Flexible(
|
|
|
|
fit: FlexFit.tight,
|
2020-05-01 15:57:22 +00:00
|
|
|
flex: 1,
|
2020-05-20 17:28:01 +00:00
|
|
|
child: Container(
|
|
|
|
width: double.infinity,
|
|
|
|
height: double.infinity,
|
|
|
|
padding: EdgeInsets.all(24),
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
borderRadius: BorderRadius.only(
|
|
|
|
bottomLeft: Radius.circular(24),
|
|
|
|
bottomRight: Radius.circular(24)
|
2020-05-01 15:57:22 +00:00
|
|
|
),
|
2020-05-20 17:28:01 +00:00
|
|
|
color: PaletteDark.menuList
|
|
|
|
),
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
child: Column(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: <Widget>[
|
|
|
|
Text(
|
|
|
|
S.of(context).restore_active_seed,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 14,
|
|
|
|
color: PaletteDark.walletCardText
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.only(top: 5),
|
|
|
|
child: Wrap(
|
|
|
|
children: items.map((item) {
|
|
|
|
final isValid = item.isCorrect();
|
|
|
|
final isSelected = selectedItem == item;
|
|
|
|
|
|
|
|
return InkWell(
|
|
|
|
onTap: () => onMnemoticTap(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 : Palette.lightGrey,
|
|
|
|
fontSize: 16,
|
|
|
|
fontWeight:
|
|
|
|
isSelected ? FontWeight.w900 : FontWeight.w400,
|
|
|
|
decoration: isSelected
|
|
|
|
? TextDecoration.underline
|
|
|
|
: TextDecoration.none),
|
|
|
|
)),
|
|
|
|
);
|
|
|
|
}).toList(),)
|
|
|
|
)
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
2020-05-01 15:57:22 +00:00
|
|
|
),
|
2020-01-16 16:05:07 +00:00
|
|
|
Flexible(
|
|
|
|
fit: FlexFit.tight,
|
2020-05-01 15:57:22 +00:00
|
|
|
flex: 2,
|
|
|
|
child: Padding(
|
|
|
|
padding: EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
|
|
|
|
child: Column(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
2020-01-16 16:05:07 +00:00
|
|
|
children: <Widget>[
|
2020-05-01 15:57:22 +00:00
|
|
|
Text(
|
|
|
|
S.of(context).restore_new_seed,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 18,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
color: Colors.white
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.only(top: 24),
|
|
|
|
child: TextFormField(
|
|
|
|
key: _seedTextFieldKey,
|
|
|
|
onFieldSubmitted: (text) => isCurrentMnemoticValid
|
|
|
|
? saveCurrentMnemoticToItems()
|
|
|
|
: null,
|
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 16.0,
|
|
|
|
color: Colors.white
|
|
|
|
),
|
|
|
|
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}/${SeedWidgetState.maxLength}',
|
|
|
|
style: TextStyle(
|
|
|
|
color: PaletteDark.walletCardText,
|
|
|
|
fontSize: 14)),
|
|
|
|
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:
|
|
|
|
PaletteDark.menuList,
|
|
|
|
borderRadius:
|
|
|
|
BorderRadius.circular(10.0)),
|
|
|
|
child: Text(
|
|
|
|
S.of(context).paste,
|
|
|
|
style: TextStyle(
|
|
|
|
color: Colors.white
|
|
|
|
),
|
|
|
|
)),
|
|
|
|
)
|
|
|
|
],
|
2020-01-16 16:05:07 +00:00
|
|
|
),
|
|
|
|
),
|
2020-05-01 15:57:22 +00:00
|
|
|
),
|
|
|
|
hintStyle:
|
|
|
|
TextStyle(
|
|
|
|
color: PaletteDark.walletCardText,
|
|
|
|
fontSize: 16
|
|
|
|
),
|
|
|
|
hintText: S.of(context).restore_from_seed_placeholder,
|
|
|
|
errorText: _errorMessage,
|
|
|
|
focusedBorder: UnderlineInputBorder(
|
|
|
|
borderSide: BorderSide(
|
|
|
|
color: PaletteDark.menuList, width: 1.0)),
|
|
|
|
enabledBorder: UnderlineInputBorder(
|
|
|
|
borderSide: BorderSide(
|
|
|
|
color: PaletteDark.menuList,
|
|
|
|
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.red,
|
|
|
|
textColor: Colors.white,
|
|
|
|
isDisabled: items.isEmpty,
|
2020-01-16 16:05:07 +00:00
|
|
|
),
|
2020-05-01 15:57:22 +00:00
|
|
|
)
|
|
|
|
),
|
|
|
|
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: Colors.green,
|
|
|
|
textColor: Colors.white)
|
|
|
|
: PrimaryButton(
|
|
|
|
text: selectedItem != null
|
|
|
|
? S.of(context).save
|
|
|
|
: S.of(context).add_new_word,
|
|
|
|
onPressed: () => isCurrentMnemoticValid
|
|
|
|
? saveCurrentMnemoticToItems()
|
|
|
|
: null,
|
|
|
|
onDisabledPressed: () => showErrorIfExist(),
|
|
|
|
isDisabled: !isCurrentMnemoticValid,
|
|
|
|
color: Colors.green,
|
|
|
|
textColor: Colors.white),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
],
|
|
|
|
))
|
2020-01-04 19:31:52 +00:00
|
|
|
]),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|