diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index c120b19b3..ad8c4f84b 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -46,6 +46,11 @@ class WalletRestoreFromSeedFormState extends State { context: context, builder: (BuildContext context) => SeedLanguagePicker(selected: language)); + + if (selected == null || selected.isEmpty) { + return; + } + _changeLanguage(selected); }, child: Container( diff --git a/lib/src/screens/seed_language/widgets/seed_language_picker.dart b/lib/src/screens/seed_language/widgets/seed_language_picker.dart index 47e43c7c7..36ca4de09 100644 --- a/lib/src/screens/seed_language/widgets/seed_language_picker.dart +++ b/lib/src/screens/seed_language/widgets/seed_language_picker.dart @@ -92,6 +92,7 @@ class SeedLanguagePickerState extends State { width: 300, color: Theme.of(context).accentTextTheme.title.backgroundColor, child: GridView.count( + padding: EdgeInsets.all(0), shrinkWrap: true, crossAxisCount: 3, childAspectRatio: 1, diff --git a/lib/src/widgets/annotated_editable_text.dart b/lib/src/widgets/annotated_editable_text.dart deleted file mode 100644 index cc42d9b35..000000000 --- a/lib/src/widgets/annotated_editable_text.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:flutter/material.dart'; - -class Annotation extends Comparable { - Annotation({@required this.range, this.style}); - - final TextRange range; - final TextStyle style; - - @override - int compareTo(Annotation other) => range.start.compareTo(other.range.start); -} - -class TextAnnotation extends Comparable { - TextAnnotation({@required this.text, this.style}); - - final TextStyle style; - final String text; - - @override - int compareTo(TextAnnotation other) => text.compareTo(other.text); -} - -class AnnotatedEditableText extends EditableText { - AnnotatedEditableText({ - Key key, - FocusNode focusNode, - TextEditingController controller, - TextStyle style, - ValueChanged onChanged, - ValueChanged onSubmitted, - Color cursorColor, - Color selectionColor, - Color backgroundCursorColor, - TextSelectionControls selectionControls, - TextStyle textStyle = const TextStyle( - color: Colors.black, - backgroundColor: Colors.transparent, - fontWeight: FontWeight.normal, - fontSize: 16), - @required this.words, - }) : textAnnotations = words - .map((word) => TextAnnotation(text: word, style: textStyle)) - .toList(), - super( - maxLines: null, - key: key, - focusNode: focusNode, - controller: controller, - cursorColor: cursorColor, - style: style, - keyboardType: TextInputType.text, - autocorrect: false, - autofocus: false, - selectionColor: selectionColor, - selectionControls: selectionControls, - backgroundCursorColor: backgroundCursorColor, - onChanged: onChanged, - onSubmitted: onSubmitted, - toolbarOptions: const ToolbarOptions( - copy: true, - cut: true, - paste: true, - selectAll: true, - ), - enableSuggestions: false, - enableInteractiveSelection: true, - showSelectionHandles: true, - showCursor: true, - ) { - textAnnotations.add(TextAnnotation( - text: ' ', style: TextStyle(backgroundColor: Colors.transparent))); - } - - final List words; - final List textAnnotations; - - @override - AnnotatedEditableTextState createState() => AnnotatedEditableTextState(); -} - -class AnnotatedEditableTextState extends EditableTextState { - @override - AnnotatedEditableText get widget => super.widget as AnnotatedEditableText; - - List getRanges() { - final source = widget.textAnnotations - .map((item) => range(item.text, textEditingValue.text) - .map((range) => Annotation(style: item.style, range: range))) - .expand((e) => e) - .toList(); - final result = List(); - final text = textEditingValue.text; - source.sort(); - Annotation prev; - - for (var item in source) { - if (prev == null) { - if (item.range.start > 0) { - result.add(Annotation( - range: TextRange(start: 0, end: item.range.start), - style: TextStyle( - color: Colors.black, backgroundColor: Colors.transparent))); - } - result.add(item); - prev = item; - continue; - } else { - if (prev.range.end > item.range.start) { - // throw StateError('Invalid (intersecting) ranges for annotated field'); - } else if (prev.range.end < item.range.start) { - result.add(Annotation( - range: TextRange(start: prev.range.end, end: item.range.start), - style: TextStyle( - color: Colors.red, backgroundColor: Colors.transparent))); - } - - result.add(item); - prev = item; - } - } - - if (result.length > 0 && result.last.range.end < text.length) { - result.add(Annotation( - range: TextRange(start: result.last.range.end, end: text.length), - style: TextStyle(backgroundColor: Colors.transparent))); - } - return result; - } - - List range(String pattern, String source) { - final result = List(); - - for (int index = source.indexOf(pattern); - index >= 0; - index = source.indexOf(pattern, index + 1)) { - final start = index; - final end = start + pattern.length; - result.add(TextRange(start: start, end: end)); - } - - return result; - } - - @override - TextSpan buildTextSpan() { - final text = textEditingValue.text; - final ranges = getRanges(); - - if (ranges.isNotEmpty) { - return TextSpan( - style: widget.style, - children: ranges - .map((item) => TextSpan( - style: item.style, text: item.range.textInside(text))) - .toList()); - } - - return TextSpan(style: widget.style, text: text); - } -} diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index 05589cdad..e444bc687 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -1,5 +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/validable_annotated_editable_text.dart'; import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -77,10 +77,15 @@ class SeedWidgetState extends State { fontSize: 16.0, color: Theme.of(context).hintColor))), Padding( padding: EdgeInsets.only(right: 40, top: 10), - child: AnnotatedEditableText( + child: ValidableAnnotatedEditableText( cursorColor: Colors.blue, backgroundCursorColor: Colors.blue, - style: TextStyle( + validStyle: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color, + backgroundColor: Colors.transparent, + fontWeight: FontWeight.normal, + fontSize: 16), + invalidStyle: TextStyle( fontSize: 16, color: Colors.red, fontWeight: FontWeight.normal, @@ -101,7 +106,7 @@ class SeedWidgetState extends State { width: 34, height: 34, child: InkWell( - onTap: () async => _pasteAddress(), + onTap: () async => _pasteText(), child: Container( padding: EdgeInsets.all(8), decoration: BoxDecoration( @@ -122,7 +127,7 @@ class SeedWidgetState extends State { ])); } - Future _pasteAddress() async { + Future _pasteText() async { final value = await Clipboard.getData('text/plain'); if (value?.text?.isNotEmpty ?? false) { diff --git a/lib/src/widgets/validable_annotated_editable_text.dart b/lib/src/widgets/validable_annotated_editable_text.dart new file mode 100644 index 000000000..b407a7950 --- /dev/null +++ b/lib/src/widgets/validable_annotated_editable_text.dart @@ -0,0 +1,172 @@ +import 'package:cake_wallet/core/seed_validator.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:flutter/material.dart'; + +class Annotation extends Comparable { + Annotation({@required this.range, this.style}); + + final TextRange range; + final TextStyle style; + + @override + int compareTo(Annotation other) => range.start.compareTo(other.range.start); +} + +class TextAnnotation extends Comparable { + TextAnnotation({@required this.text, this.style}); + + final TextStyle style; + final String text; + + @override + int compareTo(TextAnnotation other) => text.compareTo(other.text); +} + +class ValidableAnnotatedEditableText extends EditableText { + ValidableAnnotatedEditableText({ + Key key, + FocusNode focusNode, + TextEditingController controller, + List wordList, + ValueChanged onChanged, + ValueChanged onSubmitted, + Color cursorColor, + Color selectionColor, + Color backgroundCursorColor, + TextSelectionControls selectionControls, + this.validStyle, + this.invalidStyle, + TextStyle textStyle = const TextStyle( + color: Colors.black, + backgroundColor: Colors.transparent, + fontWeight: FontWeight.normal, + fontSize: 16), + @required this.words, + }) : super( + maxLines: null, + key: key, + focusNode: focusNode, + controller: controller, + cursorColor: cursorColor, + style: validStyle, + 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); + + final List words; + final TextStyle validStyle; + final TextStyle invalidStyle; + + @override + ValidableAnnotatedEditableTextState createState() => ValidableAnnotatedEditableTextState(); +} + +class ValidableAnnotatedEditableTextState extends EditableTextState { + @override + ValidableAnnotatedEditableText get widget => super.widget as ValidableAnnotatedEditableText; + + List getRanges() { + final result = List(); + final text = textEditingValue.text; + final source = text + .split(' ') + .map((word) { + final ranges = range(word, text); + final isValid = validate(word); + + return ranges.map((range) => Annotation( + style: isValid ? widget.validStyle : widget.invalidStyle, + range: range)); + }) + .expand((e) => e) + .toList(); + source.sort(); + Annotation prev; + + for (var item in source) { + Annotation annotation; + + if (prev == null) { + annotation = Annotation( + range: TextRange(start: 0, end: item.range.start), + style: TextStyle( + color: Colors.black, backgroundColor: Colors.transparent)); + } else if (prev.range.end < item.range.start) { + annotation = Annotation( + range: TextRange(start: prev.range.end, end: item.range.start), + style: TextStyle( + color: Colors.red, backgroundColor: Colors.transparent)); + } + + if (annotation != null) { + result.add(annotation); + 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; + } + + bool validate(String source) => widget.words.indexOf(source) >= 0; + + List range(String pattern, String source) { + final result = List(); + + if (pattern.isEmpty || source.isEmpty) { + return result; + } + + 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().toSet(); + + print('text $text'); + + if (ranges.isNotEmpty) { + return TextSpan( + style: widget.style, + children: ranges.map((item) { + final _text = item.range.textInside(text); + print( + '_text $_text; range ${item.range.start} : ${item.range.end}'); + return TextSpan(style: item.style, text: _text); + }).toList()); + } + + return TextSpan(style: widget.style, text: text); + } +}