diff --git a/assets/images/2.0x/transfer.png b/assets/images/2.0x/transfer.png new file mode 100644 index 000000000..61be79142 Binary files /dev/null and b/assets/images/2.0x/transfer.png differ diff --git a/assets/images/2.0x/upload.png b/assets/images/2.0x/upload.png new file mode 100644 index 000000000..bb6dfbf44 Binary files /dev/null and b/assets/images/2.0x/upload.png differ diff --git a/assets/images/3.0x/transfer.png b/assets/images/3.0x/transfer.png new file mode 100644 index 000000000..049253da5 Binary files /dev/null and b/assets/images/3.0x/transfer.png differ diff --git a/assets/images/3.0x/upload.png b/assets/images/3.0x/upload.png new file mode 100644 index 000000000..f42a50eca Binary files /dev/null and b/assets/images/3.0x/upload.png differ diff --git a/assets/images/transfer.png b/assets/images/transfer.png index 383c35fb1..d388f818f 100644 Binary files a/assets/images/transfer.png and b/assets/images/transfer.png differ diff --git a/assets/images/upload.png b/assets/images/upload.png index dba320367..fefd5f660 100644 Binary files a/assets/images/upload.png and b/assets/images/upload.png differ diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ca92f3f09..ab6767c8d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -354,7 +354,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -371,7 +371,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.9; + MARKETING_VERSION = 4.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -494,7 +494,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -511,7 +511,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.9; + MARKETING_VERSION = 4.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -528,7 +528,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = 32J6BB6VUS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -545,7 +545,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 4.0.9; + MARKETING_VERSION = 4.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/bitcoin/address_to_output_script.dart b/lib/bitcoin/address_to_output_script.dart new file mode 100644 index 000000000..2f6698ff0 --- /dev/null +++ b/lib/bitcoin/address_to_output_script.dart @@ -0,0 +1,25 @@ +import 'dart:typed_data'; +import 'package:bs58check/bs58check.dart' as bs58check; +import 'package:bitcoin_flutter/src/utils/constants/op.dart'; +import 'package:bitcoin_flutter/src/utils/script.dart' as bscript; +import 'package:bitcoin_flutter/src/address.dart'; + + +Uint8List p2shAddressToOutputScript(String address) { + final decodeBase58 = bs58check.decode(address); + final hash = decodeBase58.sublist(1); + return bscript.compile([OPS['OP_HASH160'], hash, OPS['OP_EQUAL']]); +} + +Uint8List addressToOutputScript(String address) { + try { + // FIXME: improve validation for p2sh addresses + if (address.startsWith('3')) { + return p2shAddressToOutputScript(address); + } + + return Address.addressToOutputScript(address); + } catch (_) { + return Uint8List(0); + } +} \ No newline at end of file diff --git a/lib/bitcoin/bitcoin_balance.dart b/lib/bitcoin/bitcoin_balance.dart index 6aefe28f4..1f5a70b72 100644 --- a/lib/bitcoin/bitcoin_balance.dart +++ b/lib/bitcoin/bitcoin_balance.dart @@ -1,11 +1,16 @@ import 'dart:convert'; +import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:flutter/foundation.dart'; import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/entities/balance.dart'; class BitcoinBalance extends Balance { - const BitcoinBalance({@required this.confirmed, @required this.unconfirmed}) : super(); + const BitcoinBalance({@required this.confirmed, @required this.unconfirmed}) + : super(const [ + BalanceDisplayMode.availableBalance, + BalanceDisplayMode.fullBalance + ]); factory BitcoinBalance.fromJSON(String jsonSource) { if (jsonSource == null) { @@ -24,12 +29,29 @@ class BitcoinBalance extends Balance { int get total => confirmed + unconfirmed; + int get availableBalance => confirmed + (unconfirmed < 0 ? unconfirmed : 0); + String get confirmedFormatted => bitcoinAmountToString(amount: confirmed); String get unconfirmedFormatted => bitcoinAmountToString(amount: unconfirmed); String get totalFormatted => bitcoinAmountToString(amount: total); + String get availableBalanceFormatted => + bitcoinAmountToString(amount: availableBalance); + + @override + String formattedBalance(BalanceDisplayMode mode) { + switch (mode) { + case BalanceDisplayMode.fullBalance: + return totalFormatted; + case BalanceDisplayMode.availableBalance: + return availableBalanceFormatted; + default: + return null; + } + } + String toJSON() => json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed}); } diff --git a/lib/bitcoin/bitcoin_mnemonic.dart b/lib/bitcoin/bitcoin_mnemonic.dart new file mode 100644 index 000000000..7f4bcd13c --- /dev/null +++ b/lib/bitcoin/bitcoin_mnemonic.dart @@ -0,0 +1,2308 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; +import 'package:unorm_dart/unorm_dart.dart' as unorm; +import 'package:cryptography/cryptography.dart' as cryptography; + +const segwit = '100'; +final wordlist = englishWordlist; + +Uint8List randomBytes(int length, {bool secure = false}) { + assert(length > 0); + + final random = secure ? Random.secure() : Random(); + final ret = Uint8List(length); + + for (var i = 0; i < length; i++) { + ret[i] = random.nextInt(256); + } + + return ret; +} + +double logBase(num x, num base) => log(x) / log(base); + +String mnemonicEncode(int i) { + var _i = i; + final n = wordlist.length; + final words = []; + + while (_i > 0) { + final x = i % n; + _i = (i / n).floor(); + words.add(wordlist[x]); + } + + return words.join(' '); +} + +int mnemonicDecode(String seed) { + var i = 0; + final n = wordlist.length; + final words = seed.split(' '); + + while (words.length > 0) { + final word = words.removeLast(); + final k = wordlist.indexOf(word); + i = i * n + k; + } + + return i; +} + +bool isNewSeed(String seed, {String prefix = segwit}) { + final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); + final digest = hmacSha512.convert(utf8.encode(normalizeText(seed))); + final hx = digest.toString(); + return hx.startsWith(prefix.toLowerCase()); +} + +void maskBytes(Uint8List bytes, int bits) { + final skipCount = (bits / 8).floor(); + var lastByte = (1 << bits % 8) - 1; + + for (var i = bytes.length - 1 - skipCount; i >= 0; i--) { + bytes[i] &= lastByte; + + if (lastByte > 0) { + lastByte = 0; + } + } +} + +String bufferToBin(Uint8List data) { + final q1 = data.map((e) => e.toRadixString(2).padLeft(8, '0')); + final q2 = q1.join(''); + return q2; +} + +String encode(Uint8List data) { + final dataBitLen = data.length * 8; + final wordBitLen = logBase(wordlist.length, 2).ceil(); + final wordCount = (dataBitLen / wordBitLen).floor(); + maskBytes(data, wordCount * wordBitLen); + final bin = bufferToBin(data); + final binStr = bin.substring(bin.length - (wordCount * wordBitLen)); + final result = []; + + for (var i = 0; i < wordCount; i++) { + final wordBin = binStr.substring(i * wordBitLen, (i + 1) * wordBitLen); + result.add(wordlist[int.parse(wordBin, radix: 2)]); + } + + return result.join(' '); +} + +List prefixMatches(String source, List prefixes) { + final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); + final digest = hmacSha512.convert(utf8.encode(normalizeText(source))); + final hx = digest.toString(); + + return prefixes.map((prefix) => hx.startsWith(prefix.toLowerCase())).toList(); +} + +String generateMnemonic({int strength = 132, String prefix = segwit}) { + final wordBitlen = logBase(wordlist.length, 2).ceil(); + final wordCount = strength / wordBitlen; + final byteCount = ((wordCount * wordBitlen).ceil() / 8).ceil(); + var result = ''; + + do { + final bytes = randomBytes(byteCount); + maskBytes(bytes, strength); + result = encode(bytes); + } while (!prefixMatches(result, [prefix]).first); + + return result; +} + +Uint8List mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) { + final pbkdf2 = cryptography.Pbkdf2( + macAlgorithm: cryptography.Hmac(cryptography.sha512), + iterations: 2048, + bits: 512); + final text = normalizeText(mnemonic); + + return pbkdf2.deriveBitsSync(text.codeUnits, + nonce: cryptography.Nonce('electrum'.codeUnits)); +} + +bool matchesAnyPrefix(String mnemonic) => + prefixMatches(mnemonic, [segwit]).any((el) => el); + +bool validateMnemonic(String mnemonic, {String prefix = segwit}) { + try { + return matchesAnyPrefix(mnemonic); + } catch(e) { + return false; + } +} + +final COMBININGCODEPOINTS = combiningcodepoints(); + +List combiningcodepoints() { + final source = '300:34e|350:36f|483:487|591:5bd|5bf|5c1|5c2|5c4|5c5|5c7|610:61a|64b:65f|670|' + + '6d6:6dc|6df:6e4|6e7|6e8|6ea:6ed|711|730:74a|7eb:7f3|816:819|81b:823|825:827|' + + '829:82d|859:85b|8d4:8e1|8e3:8ff|93c|94d|951:954|9bc|9cd|a3c|a4d|abc|acd|b3c|' + + 'b4d|bcd|c4d|c55|c56|cbc|ccd|d4d|dca|e38:e3a|e48:e4b|eb8|eb9|ec8:ecb|f18|f19|' + + 'f35|f37|f39|f71|f72|f74|f7a:f7d|f80|f82:f84|f86|f87|fc6|1037|1039|103a|108d|' + + '135d:135f|1714|1734|17d2|17dd|18a9|1939:193b|1a17|1a18|1a60|1a75:1a7c|1a7f|' + + '1ab0:1abd|1b34|1b44|1b6b:1b73|1baa|1bab|1be6|1bf2|1bf3|1c37|1cd0:1cd2|' + + '1cd4:1ce0|1ce2:1ce8|1ced|1cf4|1cf8|1cf9|1dc0:1df5|1dfb:1dff|20d0:20dc|20e1|' + + '20e5:20f0|2cef:2cf1|2d7f|2de0:2dff|302a:302f|3099|309a|a66f|a674:a67d|a69e|' + + 'a69f|a6f0|a6f1|a806|a8c4|a8e0:a8f1|a92b:a92d|a953|a9b3|a9c0|aab0|aab2:aab4|' + + 'aab7|aab8|aabe|aabf|aac1|aaf6|abed|fb1e|fe20:fe2f|101fd|102e0|10376:1037a|' + + '10a0d|10a0f|10a38:10a3a|10a3f|10ae5|10ae6|11046|1107f|110b9|110ba|11100:11102|' + + '11133|11134|11173|111c0|111ca|11235|11236|112e9|112ea|1133c|1134d|11366:1136c|' + + '11370:11374|11442|11446|114c2|114c3|115bf|115c0|1163f|116b6|116b7|1172b|11c3f|' + + '16af0:16af4|16b30:16b36|1bc9e|1d165:1d169|1d16d:1d172|1d17b:1d182|1d185:1d18b|' + + '1d1aa:1d1ad|1d242:1d244|1e000:1e006|1e008:1e018|1e01b:1e021|1e023|1e024|' + + '1e026:1e02a|1e8d0:1e8d6|1e944:1e94a'; + + return source.split('|').map((e) { + if (e.contains(':')) { + return e.split(':').map((hex) => int.parse(hex, radix: 16)); + } + + return int.parse(e, radix: 16); + }).fold([], (List acc, element) { + if (element is List) { + for (var i = element[0] as int; i <= (element[1] as int); i++) {} + } else if (element is int) { + acc.add(element); + } + + return acc; + }).toList(); +} + +String removeCombiningCharacters(String source) { + return source + .split('') + .where((char) => !COMBININGCODEPOINTS.contains(char.codeUnits.first)) + .join(''); +} + +bool isCJK(String char) { + final n = char.codeUnitAt(0); + + for (var x in CJKINTERVALS) { + final imin = x[0] as num; + final imax = x[1] as num; + + if (n >= imin && n <= imax) return true; + } + + return false; +} + +String removeCJKSpaces(String source) { + final splitted = source.split(''); + final filtered = []; + + for (var i = 0; i < splitted.length; i++) { + final char = splitted[i]; + final isSpace = char.trim() == ''; + final prevIsCJK = i != 0 && isCJK(splitted[i - 1]); + final nextIsCJK = i != splitted.length - 1 && isCJK(splitted[i + 1]); + + if (!(isSpace && prevIsCJK && nextIsCJK)) { + filtered.add(char); + } + } + + return filtered.join(''); +} + +String normalizeText(String source) { + final res = removeCombiningCharacters(unorm.nfkd(source).toLowerCase()) + .trim() + .split('/\s+/') + .join(' '); + + return removeCJKSpaces(res); +} + +const CJKINTERVALS = [ + [0x4e00, 0x9fff, 'CJK Unified Ideographs'], + [0x3400, 0x4dbf, 'CJK Unified Ideographs Extension A'], + [0x20000, 0x2a6df, 'CJK Unified Ideographs Extension B'], + [0x2a700, 0x2b73f, 'CJK Unified Ideographs Extension C'], + [0x2b740, 0x2b81f, 'CJK Unified Ideographs Extension D'], + [0xf900, 0xfaff, 'CJK Compatibility Ideographs'], + [0x2f800, 0x2fa1d, 'CJK Compatibility Ideographs Supplement'], + [0x3190, 0x319f, 'Kanbun'], + [0x2e80, 0x2eff, 'CJK Radicals Supplement'], + [0x2f00, 0x2fdf, 'CJK Radicals'], + [0x31c0, 0x31ef, 'CJK Strokes'], + [0x2ff0, 0x2fff, 'Ideographic Description Characters'], + [0xe0100, 0xe01ef, 'Variation Selectors Supplement'], + [0x3100, 0x312f, 'Bopomofo'], + [0x31a0, 0x31bf, 'Bopomofo Extended'], + [0xff00, 0xffef, 'Halfwidth and Fullwidth Forms'], + [0x3040, 0x309f, 'Hiragana'], + [0x30a0, 0x30ff, 'Katakana'], + [0x31f0, 0x31ff, 'Katakana Phonetic Extensions'], + [0x1b000, 0x1b0ff, 'Kana Supplement'], + [0xac00, 0xd7af, 'Hangul Syllables'], + [0x1100, 0x11ff, 'Hangul Jamo'], + [0xa960, 0xa97f, 'Hangul Jamo Extended A'], + [0xd7b0, 0xd7ff, 'Hangul Jamo Extended B'], + [0x3130, 0x318f, 'Hangul Compatibility Jamo'], + [0xa4d0, 0xa4ff, 'Lisu'], + [0x16f00, 0x16f9f, 'Miao'], + [0xa000, 0xa48f, 'Yi Syllables'], + [0xa490, 0xa4cf, 'Yi Radicals'], +]; + +final englishWordlist = [ + 'abandon', + 'ability', + 'able', + 'about', + 'above', + 'absent', + 'absorb', + 'abstract', + 'absurd', + 'abuse', + 'access', + 'accident', + 'account', + 'accuse', + 'achieve', + 'acid', + 'acoustic', + 'acquire', + 'across', + 'act', + 'action', + 'actor', + 'actress', + 'actual', + 'adapt', + 'add', + 'addict', + 'address', + 'adjust', + 'admit', + 'adult', + 'advance', + 'advice', + 'aerobic', + 'affair', + 'afford', + 'afraid', + 'again', + 'age', + 'agent', + 'agree', + 'ahead', + 'aim', + 'air', + 'airport', + 'aisle', + 'alarm', + 'album', + 'alcohol', + 'alert', + 'alien', + 'all', + 'alley', + 'allow', + 'almost', + 'alone', + 'alpha', + 'already', + 'also', + 'alter', + 'always', + 'amateur', + 'amazing', + 'among', + 'amount', + 'amused', + 'analyst', + 'anchor', + 'ancient', + 'anger', + 'angle', + 'angry', + 'animal', + 'ankle', + 'announce', + 'annual', + 'another', + 'answer', + 'antenna', + 'antique', + 'anxiety', + 'any', + 'apart', + 'apology', + 'appear', + 'apple', + 'approve', + 'april', + 'arch', + 'arctic', + 'area', + 'arena', + 'argue', + 'arm', + 'armed', + 'armor', + 'army', + 'around', + 'arrange', + 'arrest', + 'arrive', + 'arrow', + 'art', + 'artefact', + 'artist', + 'artwork', + 'ask', + 'aspect', + 'assault', + 'asset', + 'assist', + 'assume', + 'asthma', + 'athlete', + 'atom', + 'attack', + 'attend', + 'attitude', + 'attract', + 'auction', + 'audit', + 'august', + 'aunt', + 'author', + 'auto', + 'autumn', + 'average', + 'avocado', + 'avoid', + 'awake', + 'aware', + 'away', + 'awesome', + 'awful', + 'awkward', + 'axis', + 'baby', + 'bachelor', + 'bacon', + 'badge', + 'bag', + 'balance', + 'balcony', + 'ball', + 'bamboo', + 'banana', + 'banner', + 'bar', + 'barely', + 'bargain', + 'barrel', + 'base', + 'basic', + 'basket', + 'battle', + 'beach', + 'bean', + 'beauty', + 'because', + 'become', + 'beef', + 'before', + 'begin', + 'behave', + 'behind', + 'believe', + 'below', + 'belt', + 'bench', + 'benefit', + 'best', + 'betray', + 'better', + 'between', + 'beyond', + 'bicycle', + 'bid', + 'bike', + 'bind', + 'biology', + 'bird', + 'birth', + 'bitter', + 'black', + 'blade', + 'blame', + 'blanket', + 'blast', + 'bleak', + 'bless', + 'blind', + 'blood', + 'blossom', + 'blouse', + 'blue', + 'blur', + 'blush', + 'board', + 'boat', + 'body', + 'boil', + 'bomb', + 'bone', + 'bonus', + 'book', + 'boost', + 'border', + 'boring', + 'borrow', + 'boss', + 'bottom', + 'bounce', + 'box', + 'boy', + 'bracket', + 'brain', + 'brand', + 'brass', + 'brave', + 'bread', + 'breeze', + 'brick', + 'bridge', + 'brief', + 'bright', + 'bring', + 'brisk', + 'broccoli', + 'broken', + 'bronze', + 'broom', + 'brother', + 'brown', + 'brush', + 'bubble', + 'buddy', + 'budget', + 'buffalo', + 'build', + 'bulb', + 'bulk', + 'bullet', + 'bundle', + 'bunker', + 'burden', + 'burger', + 'burst', + 'bus', + 'business', + 'busy', + 'butter', + 'buyer', + 'buzz', + 'cabbage', + 'cabin', + 'cable', + 'cactus', + 'cage', + 'cake', + 'call', + 'calm', + 'camera', + 'camp', + 'can', + 'canal', + 'cancel', + 'candy', + 'cannon', + 'canoe', + 'canvas', + 'canyon', + 'capable', + 'capital', + 'captain', + 'car', + 'carbon', + 'card', + 'cargo', + 'carpet', + 'carry', + 'cart', + 'case', + 'cash', + 'casino', + 'castle', + 'casual', + 'cat', + 'catalog', + 'catch', + 'category', + 'cattle', + 'caught', + 'cause', + 'caution', + 'cave', + 'ceiling', + 'celery', + 'cement', + 'census', + 'century', + 'cereal', + 'certain', + 'chair', + 'chalk', + 'champion', + 'change', + 'chaos', + 'chapter', + 'charge', + 'chase', + 'chat', + 'cheap', + 'check', + 'cheese', + 'chef', + 'cherry', + 'chest', + 'chicken', + 'chief', + 'child', + 'chimney', + 'choice', + 'choose', + 'chronic', + 'chuckle', + 'chunk', + 'churn', + 'cigar', + 'cinnamon', + 'circle', + 'citizen', + 'city', + 'civil', + 'claim', + 'clap', + 'clarify', + 'claw', + 'clay', + 'clean', + 'clerk', + 'clever', + 'click', + 'client', + 'cliff', + 'climb', + 'clinic', + 'clip', + 'clock', + 'clog', + 'close', + 'cloth', + 'cloud', + 'clown', + 'club', + 'clump', + 'cluster', + 'clutch', + 'coach', + 'coast', + 'coconut', + 'code', + 'coffee', + 'coil', + 'coin', + 'collect', + 'color', + 'column', + 'combine', + 'come', + 'comfort', + 'comic', + 'common', + 'company', + 'concert', + 'conduct', + 'confirm', + 'congress', + 'connect', + 'consider', + 'control', + 'convince', + 'cook', + 'cool', + 'copper', + 'copy', + 'coral', + 'core', + 'corn', + 'correct', + 'cost', + 'cotton', + 'couch', + 'country', + 'couple', + 'course', + 'cousin', + 'cover', + 'coyote', + 'crack', + 'cradle', + 'craft', + 'cram', + 'crane', + 'crash', + 'crater', + 'crawl', + 'crazy', + 'cream', + 'credit', + 'creek', + 'crew', + 'cricket', + 'crime', + 'crisp', + 'critic', + 'crop', + 'cross', + 'crouch', + 'crowd', + 'crucial', + 'cruel', + 'cruise', + 'crumble', + 'crunch', + 'crush', + 'cry', + 'crystal', + 'cube', + 'culture', + 'cup', + 'cupboard', + 'curious', + 'current', + 'curtain', + 'curve', + 'cushion', + 'custom', + 'cute', + 'cycle', + 'dad', + 'damage', + 'damp', + 'dance', + 'danger', + 'daring', + 'dash', + 'daughter', + 'dawn', + 'day', + 'deal', + 'debate', + 'debris', + 'decade', + 'december', + 'decide', + 'decline', + 'decorate', + 'decrease', + 'deer', + 'defense', + 'define', + 'defy', + 'degree', + 'delay', + 'deliver', + 'demand', + 'demise', + 'denial', + 'dentist', + 'deny', + 'depart', + 'depend', + 'deposit', + 'depth', + 'deputy', + 'derive', + 'describe', + 'desert', + 'design', + 'desk', + 'despair', + 'destroy', + 'detail', + 'detect', + 'develop', + 'device', + 'devote', + 'diagram', + 'dial', + 'diamond', + 'diary', + 'dice', + 'diesel', + 'diet', + 'differ', + 'digital', + 'dignity', + 'dilemma', + 'dinner', + 'dinosaur', + 'direct', + 'dirt', + 'disagree', + 'discover', + 'disease', + 'dish', + 'dismiss', + 'disorder', + 'display', + 'distance', + 'divert', + 'divide', + 'divorce', + 'dizzy', + 'doctor', + 'document', + 'dog', + 'doll', + 'dolphin', + 'domain', + 'donate', + 'donkey', + 'donor', + 'door', + 'dose', + 'double', + 'dove', + 'draft', + 'dragon', + 'drama', + 'drastic', + 'draw', + 'dream', + 'dress', + 'drift', + 'drill', + 'drink', + 'drip', + 'drive', + 'drop', + 'drum', + 'dry', + 'duck', + 'dumb', + 'dune', + 'during', + 'dust', + 'dutch', + 'duty', + 'dwarf', + 'dynamic', + 'eager', + 'eagle', + 'early', + 'earn', + 'earth', + 'easily', + 'east', + 'easy', + 'echo', + 'ecology', + 'economy', + 'edge', + 'edit', + 'educate', + 'effort', + 'egg', + 'eight', + 'either', + 'elbow', + 'elder', + 'electric', + 'elegant', + 'element', + 'elephant', + 'elevator', + 'elite', + 'else', + 'embark', + 'embody', + 'embrace', + 'emerge', + 'emotion', + 'employ', + 'empower', + 'empty', + 'enable', + 'enact', + 'end', + 'endless', + 'endorse', + 'enemy', + 'energy', + 'enforce', + 'engage', + 'engine', + 'enhance', + 'enjoy', + 'enlist', + 'enough', + 'enrich', + 'enroll', + 'ensure', + 'enter', + 'entire', + 'entry', + 'envelope', + 'episode', + 'equal', + 'equip', + 'era', + 'erase', + 'erode', + 'erosion', + 'error', + 'erupt', + 'escape', + 'essay', + 'essence', + 'estate', + 'eternal', + 'ethics', + 'evidence', + 'evil', + 'evoke', + 'evolve', + 'exact', + 'example', + 'excess', + 'exchange', + 'excite', + 'exclude', + 'excuse', + 'execute', + 'exercise', + 'exhaust', + 'exhibit', + 'exile', + 'exist', + 'exit', + 'exotic', + 'expand', + 'expect', + 'expire', + 'explain', + 'expose', + 'express', + 'extend', + 'extra', + 'eye', + 'eyebrow', + 'fabric', + 'face', + 'faculty', + 'fade', + 'faint', + 'faith', + 'fall', + 'false', + 'fame', + 'family', + 'famous', + 'fan', + 'fancy', + 'fantasy', + 'farm', + 'fashion', + 'fat', + 'fatal', + 'father', + 'fatigue', + 'fault', + 'favorite', + 'feature', + 'february', + 'federal', + 'fee', + 'feed', + 'feel', + 'female', + 'fence', + 'festival', + 'fetch', + 'fever', + 'few', + 'fiber', + 'fiction', + 'field', + 'figure', + 'file', + 'film', + 'filter', + 'final', + 'find', + 'fine', + 'finger', + 'finish', + 'fire', + 'firm', + 'first', + 'fiscal', + 'fish', + 'fit', + 'fitness', + 'fix', + 'flag', + 'flame', + 'flash', + 'flat', + 'flavor', + 'flee', + 'flight', + 'flip', + 'float', + 'flock', + 'floor', + 'flower', + 'fluid', + 'flush', + 'fly', + 'foam', + 'focus', + 'fog', + 'foil', + 'fold', + 'follow', + 'food', + 'foot', + 'force', + 'forest', + 'forget', + 'fork', + 'fortune', + 'forum', + 'forward', + 'fossil', + 'foster', + 'found', + 'fox', + 'fragile', + 'frame', + 'frequent', + 'fresh', + 'friend', + 'fringe', + 'frog', + 'front', + 'frost', + 'frown', + 'frozen', + 'fruit', + 'fuel', + 'fun', + 'funny', + 'furnace', + 'fury', + 'future', + 'gadget', + 'gain', + 'galaxy', + 'gallery', + 'game', + 'gap', + 'garage', + 'garbage', + 'garden', + 'garlic', + 'garment', + 'gas', + 'gasp', + 'gate', + 'gather', + 'gauge', + 'gaze', + 'general', + 'genius', + 'genre', + 'gentle', + 'genuine', + 'gesture', + 'ghost', + 'giant', + 'gift', + 'giggle', + 'ginger', + 'giraffe', + 'girl', + 'give', + 'glad', + 'glance', + 'glare', + 'glass', + 'glide', + 'glimpse', + 'globe', + 'gloom', + 'glory', + 'glove', + 'glow', + 'glue', + 'goat', + 'goddess', + 'gold', + 'good', + 'goose', + 'gorilla', + 'gospel', + 'gossip', + 'govern', + 'gown', + 'grab', + 'grace', + 'grain', + 'grant', + 'grape', + 'grass', + 'gravity', + 'great', + 'green', + 'grid', + 'grief', + 'grit', + 'grocery', + 'group', + 'grow', + 'grunt', + 'guard', + 'guess', + 'guide', + 'guilt', + 'guitar', + 'gun', + 'gym', + 'habit', + 'hair', + 'half', + 'hammer', + 'hamster', + 'hand', + 'happy', + 'harbor', + 'hard', + 'harsh', + 'harvest', + 'hat', + 'have', + 'hawk', + 'hazard', + 'head', + 'health', + 'heart', + 'heavy', + 'hedgehog', + 'height', + 'hello', + 'helmet', + 'help', + 'hen', + 'hero', + 'hidden', + 'high', + 'hill', + 'hint', + 'hip', + 'hire', + 'history', + 'hobby', + 'hockey', + 'hold', + 'hole', + 'holiday', + 'hollow', + 'home', + 'honey', + 'hood', + 'hope', + 'horn', + 'horror', + 'horse', + 'hospital', + 'host', + 'hotel', + 'hour', + 'hover', + 'hub', + 'huge', + 'human', + 'humble', + 'humor', + 'hundred', + 'hungry', + 'hunt', + 'hurdle', + 'hurry', + 'hurt', + 'husband', + 'hybrid', + 'ice', + 'icon', + 'idea', + 'identify', + 'idle', + 'ignore', + 'ill', + 'illegal', + 'illness', + 'image', + 'imitate', + 'immense', + 'immune', + 'impact', + 'impose', + 'improve', + 'impulse', + 'inch', + 'include', + 'income', + 'increase', + 'index', + 'indicate', + 'indoor', + 'industry', + 'infant', + 'inflict', + 'inform', + 'inhale', + 'inherit', + 'initial', + 'inject', + 'injury', + 'inmate', + 'inner', + 'innocent', + 'input', + 'inquiry', + 'insane', + 'insect', + 'inside', + 'inspire', + 'install', + 'intact', + 'interest', + 'into', + 'invest', + 'invite', + 'involve', + 'iron', + 'island', + 'isolate', + 'issue', + 'item', + 'ivory', + 'jacket', + 'jaguar', + 'jar', + 'jazz', + 'jealous', + 'jeans', + 'jelly', + 'jewel', + 'job', + 'join', + 'joke', + 'journey', + 'joy', + 'judge', + 'juice', + 'jump', + 'jungle', + 'junior', + 'junk', + 'just', + 'kangaroo', + 'keen', + 'keep', + 'ketchup', + 'key', + 'kick', + 'kid', + 'kidney', + 'kind', + 'kingdom', + 'kiss', + 'kit', + 'kitchen', + 'kite', + 'kitten', + 'kiwi', + 'knee', + 'knife', + 'knock', + 'know', + 'lab', + 'label', + 'labor', + 'ladder', + 'lady', + 'lake', + 'lamp', + 'language', + 'laptop', + 'large', + 'later', + 'latin', + 'laugh', + 'laundry', + 'lava', + 'law', + 'lawn', + 'lawsuit', + 'layer', + 'lazy', + 'leader', + 'leaf', + 'learn', + 'leave', + 'lecture', + 'left', + 'leg', + 'legal', + 'legend', + 'leisure', + 'lemon', + 'lend', + 'length', + 'lens', + 'leopard', + 'lesson', + 'letter', + 'level', + 'liar', + 'liberty', + 'library', + 'license', + 'life', + 'lift', + 'light', + 'like', + 'limb', + 'limit', + 'link', + 'lion', + 'liquid', + 'list', + 'little', + 'live', + 'lizard', + 'load', + 'loan', + 'lobster', + 'local', + 'lock', + 'logic', + 'lonely', + 'long', + 'loop', + 'lottery', + 'loud', + 'lounge', + 'love', + 'loyal', + 'lucky', + 'luggage', + 'lumber', + 'lunar', + 'lunch', + 'luxury', + 'lyrics', + 'machine', + 'mad', + 'magic', + 'magnet', + 'maid', + 'mail', + 'main', + 'major', + 'make', + 'mammal', + 'man', + 'manage', + 'mandate', + 'mango', + 'mansion', + 'manual', + 'maple', + 'marble', + 'march', + 'margin', + 'marine', + 'market', + 'marriage', + 'mask', + 'mass', + 'master', + 'match', + 'material', + 'math', + 'matrix', + 'matter', + 'maximum', + 'maze', + 'meadow', + 'mean', + 'measure', + 'meat', + 'mechanic', + 'medal', + 'media', + 'melody', + 'melt', + 'member', + 'memory', + 'mention', + 'menu', + 'mercy', + 'merge', + 'merit', + 'merry', + 'mesh', + 'message', + 'metal', + 'method', + 'middle', + 'midnight', + 'milk', + 'million', + 'mimic', + 'mind', + 'minimum', + 'minor', + 'minute', + 'miracle', + 'mirror', + 'misery', + 'miss', + 'mistake', + 'mix', + 'mixed', + 'mixture', + 'mobile', + 'model', + 'modify', + 'mom', + 'moment', + 'monitor', + 'monkey', + 'monster', + 'month', + 'moon', + 'moral', + 'more', + 'morning', + 'mosquito', + 'mother', + 'motion', + 'motor', + 'mountain', + 'mouse', + 'move', + 'movie', + 'much', + 'muffin', + 'mule', + 'multiply', + 'muscle', + 'museum', + 'mushroom', + 'music', + 'must', + 'mutual', + 'myself', + 'mystery', + 'myth', + 'naive', + 'name', + 'napkin', + 'narrow', + 'nasty', + 'nation', + 'nature', + 'near', + 'neck', + 'need', + 'negative', + 'neglect', + 'neither', + 'nephew', + 'nerve', + 'nest', + 'net', + 'network', + 'neutral', + 'never', + 'news', + 'next', + 'nice', + 'night', + 'noble', + 'noise', + 'nominee', + 'noodle', + 'normal', + 'north', + 'nose', + 'notable', + 'note', + 'nothing', + 'notice', + 'novel', + 'now', + 'nuclear', + 'number', + 'nurse', + 'nut', + 'oak', + 'obey', + 'object', + 'oblige', + 'obscure', + 'observe', + 'obtain', + 'obvious', + 'occur', + 'ocean', + 'october', + 'odor', + 'off', + 'offer', + 'office', + 'often', + 'oil', + 'okay', + 'old', + 'olive', + 'olympic', + 'omit', + 'once', + 'one', + 'onion', + 'online', + 'only', + 'open', + 'opera', + 'opinion', + 'oppose', + 'option', + 'orange', + 'orbit', + 'orchard', + 'order', + 'ordinary', + 'organ', + 'orient', + 'original', + 'orphan', + 'ostrich', + 'other', + 'outdoor', + 'outer', + 'output', + 'outside', + 'oval', + 'oven', + 'over', + 'own', + 'owner', + 'oxygen', + 'oyster', + 'ozone', + 'pact', + 'paddle', + 'page', + 'pair', + 'palace', + 'palm', + 'panda', + 'panel', + 'panic', + 'panther', + 'paper', + 'parade', + 'parent', + 'park', + 'parrot', + 'party', + 'pass', + 'patch', + 'path', + 'patient', + 'patrol', + 'pattern', + 'pause', + 'pave', + 'payment', + 'peace', + 'peanut', + 'pear', + 'peasant', + 'pelican', + 'pen', + 'penalty', + 'pencil', + 'people', + 'pepper', + 'perfect', + 'permit', + 'person', + 'pet', + 'phone', + 'photo', + 'phrase', + 'physical', + 'piano', + 'picnic', + 'picture', + 'piece', + 'pig', + 'pigeon', + 'pill', + 'pilot', + 'pink', + 'pioneer', + 'pipe', + 'pistol', + 'pitch', + 'pizza', + 'place', + 'planet', + 'plastic', + 'plate', + 'play', + 'please', + 'pledge', + 'pluck', + 'plug', + 'plunge', + 'poem', + 'poet', + 'point', + 'polar', + 'pole', + 'police', + 'pond', + 'pony', + 'pool', + 'popular', + 'portion', + 'position', + 'possible', + 'post', + 'potato', + 'pottery', + 'poverty', + 'powder', + 'power', + 'practice', + 'praise', + 'predict', + 'prefer', + 'prepare', + 'present', + 'pretty', + 'prevent', + 'price', + 'pride', + 'primary', + 'print', + 'priority', + 'prison', + 'private', + 'prize', + 'problem', + 'process', + 'produce', + 'profit', + 'program', + 'project', + 'promote', + 'proof', + 'property', + 'prosper', + 'protect', + 'proud', + 'provide', + 'public', + 'pudding', + 'pull', + 'pulp', + 'pulse', + 'pumpkin', + 'punch', + 'pupil', + 'puppy', + 'purchase', + 'purity', + 'purpose', + 'purse', + 'push', + 'put', + 'puzzle', + 'pyramid', + 'quality', + 'quantum', + 'quarter', + 'question', + 'quick', + 'quit', + 'quiz', + 'quote', + 'rabbit', + 'raccoon', + 'race', + 'rack', + 'radar', + 'radio', + 'rail', + 'rain', + 'raise', + 'rally', + 'ramp', + 'ranch', + 'random', + 'range', + 'rapid', + 'rare', + 'rate', + 'rather', + 'raven', + 'raw', + 'razor', + 'ready', + 'real', + 'reason', + 'rebel', + 'rebuild', + 'recall', + 'receive', + 'recipe', + 'record', + 'recycle', + 'reduce', + 'reflect', + 'reform', + 'refuse', + 'region', + 'regret', + 'regular', + 'reject', + 'relax', + 'release', + 'relief', + 'rely', + 'remain', + 'remember', + 'remind', + 'remove', + 'render', + 'renew', + 'rent', + 'reopen', + 'repair', + 'repeat', + 'replace', + 'report', + 'require', + 'rescue', + 'resemble', + 'resist', + 'resource', + 'response', + 'result', + 'retire', + 'retreat', + 'return', + 'reunion', + 'reveal', + 'review', + 'reward', + 'rhythm', + 'rib', + 'ribbon', + 'rice', + 'rich', + 'ride', + 'ridge', + 'rifle', + 'right', + 'rigid', + 'ring', + 'riot', + 'ripple', + 'risk', + 'ritual', + 'rival', + 'river', + 'road', + 'roast', + 'robot', + 'robust', + 'rocket', + 'romance', + 'roof', + 'rookie', + 'room', + 'rose', + 'rotate', + 'rough', + 'round', + 'route', + 'royal', + 'rubber', + 'rude', + 'rug', + 'rule', + 'run', + 'runway', + 'rural', + 'sad', + 'saddle', + 'sadness', + 'safe', + 'sail', + 'salad', + 'salmon', + 'salon', + 'salt', + 'salute', + 'same', + 'sample', + 'sand', + 'satisfy', + 'satoshi', + 'sauce', + 'sausage', + 'save', + 'say', + 'scale', + 'scan', + 'scare', + 'scatter', + 'scene', + 'scheme', + 'school', + 'science', + 'scissors', + 'scorpion', + 'scout', + 'scrap', + 'screen', + 'script', + 'scrub', + 'sea', + 'search', + 'season', + 'seat', + 'second', + 'secret', + 'section', + 'security', + 'seed', + 'seek', + 'segment', + 'select', + 'sell', + 'seminar', + 'senior', + 'sense', + 'sentence', + 'series', + 'service', + 'session', + 'settle', + 'setup', + 'seven', + 'shadow', + 'shaft', + 'shallow', + 'share', + 'shed', + 'shell', + 'sheriff', + 'shield', + 'shift', + 'shine', + 'ship', + 'shiver', + 'shock', + 'shoe', + 'shoot', + 'shop', + 'short', + 'shoulder', + 'shove', + 'shrimp', + 'shrug', + 'shuffle', + 'shy', + 'sibling', + 'sick', + 'side', + 'siege', + 'sight', + 'sign', + 'silent', + 'silk', + 'silly', + 'silver', + 'similar', + 'simple', + 'since', + 'sing', + 'siren', + 'sister', + 'situate', + 'six', + 'size', + 'skate', + 'sketch', + 'ski', + 'skill', + 'skin', + 'skirt', + 'skull', + 'slab', + 'slam', + 'sleep', + 'slender', + 'slice', + 'slide', + 'slight', + 'slim', + 'slogan', + 'slot', + 'slow', + 'slush', + 'small', + 'smart', + 'smile', + 'smoke', + 'smooth', + 'snack', + 'snake', + 'snap', + 'sniff', + 'snow', + 'soap', + 'soccer', + 'social', + 'sock', + 'soda', + 'soft', + 'solar', + 'soldier', + 'solid', + 'solution', + 'solve', + 'someone', + 'song', + 'soon', + 'sorry', + 'sort', + 'soul', + 'sound', + 'soup', + 'source', + 'south', + 'space', + 'spare', + 'spatial', + 'spawn', + 'speak', + 'special', + 'speed', + 'spell', + 'spend', + 'sphere', + 'spice', + 'spider', + 'spike', + 'spin', + 'spirit', + 'split', + 'spoil', + 'sponsor', + 'spoon', + 'sport', + 'spot', + 'spray', + 'spread', + 'spring', + 'spy', + 'square', + 'squeeze', + 'squirrel', + 'stable', + 'stadium', + 'staff', + 'stage', + 'stairs', + 'stamp', + 'stand', + 'start', + 'state', + 'stay', + 'steak', + 'steel', + 'stem', + 'step', + 'stereo', + 'stick', + 'still', + 'sting', + 'stock', + 'stomach', + 'stone', + 'stool', + 'story', + 'stove', + 'strategy', + 'street', + 'strike', + 'strong', + 'struggle', + 'student', + 'stuff', + 'stumble', + 'style', + 'subject', + 'submit', + 'subway', + 'success', + 'such', + 'sudden', + 'suffer', + 'sugar', + 'suggest', + 'suit', + 'summer', + 'sun', + 'sunny', + 'sunset', + 'super', + 'supply', + 'supreme', + 'sure', + 'surface', + 'surge', + 'surprise', + 'surround', + 'survey', + 'suspect', + 'sustain', + 'swallow', + 'swamp', + 'swap', + 'swarm', + 'swear', + 'sweet', + 'swift', + 'swim', + 'swing', + 'switch', + 'sword', + 'symbol', + 'symptom', + 'syrup', + 'system', + 'table', + 'tackle', + 'tag', + 'tail', + 'talent', + 'talk', + 'tank', + 'tape', + 'target', + 'task', + 'taste', + 'tattoo', + 'taxi', + 'teach', + 'team', + 'tell', + 'ten', + 'tenant', + 'tennis', + 'tent', + 'term', + 'test', + 'text', + 'thank', + 'that', + 'theme', + 'then', + 'theory', + 'there', + 'they', + 'thing', + 'this', + 'thought', + 'three', + 'thrive', + 'throw', + 'thumb', + 'thunder', + 'ticket', + 'tide', + 'tiger', + 'tilt', + 'timber', + 'time', + 'tiny', + 'tip', + 'tired', + 'tissue', + 'title', + 'toast', + 'tobacco', + 'today', + 'toddler', + 'toe', + 'together', + 'toilet', + 'token', + 'tomato', + 'tomorrow', + 'tone', + 'tongue', + 'tonight', + 'tool', + 'tooth', + 'top', + 'topic', + 'topple', + 'torch', + 'tornado', + 'tortoise', + 'toss', + 'total', + 'tourist', + 'toward', + 'tower', + 'town', + 'toy', + 'track', + 'trade', + 'traffic', + 'tragic', + 'train', + 'transfer', + 'trap', + 'trash', + 'travel', + 'tray', + 'treat', + 'tree', + 'trend', + 'trial', + 'tribe', + 'trick', + 'trigger', + 'trim', + 'trip', + 'trophy', + 'trouble', + 'truck', + 'true', + 'truly', + 'trumpet', + 'trust', + 'truth', + 'try', + 'tube', + 'tuition', + 'tumble', + 'tuna', + 'tunnel', + 'turkey', + 'turn', + 'turtle', + 'twelve', + 'twenty', + 'twice', + 'twin', + 'twist', + 'two', + 'type', + 'typical', + 'ugly', + 'umbrella', + 'unable', + 'unaware', + 'uncle', + 'uncover', + 'under', + 'undo', + 'unfair', + 'unfold', + 'unhappy', + 'uniform', + 'unique', + 'unit', + 'universe', + 'unknown', + 'unlock', + 'until', + 'unusual', + 'unveil', + 'update', + 'upgrade', + 'uphold', + 'upon', + 'upper', + 'upset', + 'urban', + 'urge', + 'usage', + 'use', + 'used', + 'useful', + 'useless', + 'usual', + 'utility', + 'vacant', + 'vacuum', + 'vague', + 'valid', + 'valley', + 'valve', + 'van', + 'vanish', + 'vapor', + 'various', + 'vast', + 'vault', + 'vehicle', + 'velvet', + 'vendor', + 'venture', + 'venue', + 'verb', + 'verify', + 'version', + 'very', + 'vessel', + 'veteran', + 'viable', + 'vibrant', + 'vicious', + 'victory', + 'video', + 'view', + 'village', + 'vintage', + 'violin', + 'virtual', + 'virus', + 'visa', + 'visit', + 'visual', + 'vital', + 'vivid', + 'vocal', + 'voice', + 'void', + 'volcano', + 'volume', + 'vote', + 'voyage', + 'wage', + 'wagon', + 'wait', + 'walk', + 'wall', + 'walnut', + 'want', + 'warfare', + 'warm', + 'warrior', + 'wash', + 'wasp', + 'waste', + 'water', + 'wave', + 'way', + 'wealth', + 'weapon', + 'wear', + 'weasel', + 'weather', + 'web', + 'wedding', + 'weekend', + 'weird', + 'welcome', + 'west', + 'wet', + 'whale', + 'what', + 'wheat', + 'wheel', + 'when', + 'where', + 'whip', + 'whisper', + 'wide', + 'width', + 'wife', + 'wild', + 'will', + 'win', + 'window', + 'wine', + 'wing', + 'wink', + 'winner', + 'winter', + 'wire', + 'wisdom', + 'wise', + 'wish', + 'witness', + 'wolf', + 'woman', + 'wonder', + 'wood', + 'wool', + 'word', + 'work', + 'world', + 'worry', + 'worth', + 'wrap', + 'wreck', + 'wrestle', + 'wrist', + 'write', + 'wrong', + 'yard', + 'year', + 'yellow', + 'you', + 'young', + 'youth', + 'zebra', + 'zero', + 'zone', + 'zoo' +]; diff --git a/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart b/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart new file mode 100644 index 000000000..761b02601 --- /dev/null +++ b/lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart @@ -0,0 +1,5 @@ +class BitcoinMnemonicIsIncorrectException implements Exception { + @override + String toString() => + 'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 words separated by space.'; +} diff --git a/lib/bitcoin/bitcoin_transaction_history.dart b/lib/bitcoin/bitcoin_transaction_history.dart index c687c0ba4..09cefe5d4 100644 --- a/lib/bitcoin/bitcoin_transaction_history.dart +++ b/lib/bitcoin/bitcoin_transaction_history.dart @@ -65,7 +65,7 @@ abstract class BitcoinTransactionHistoryBase return historiesWithDetails.fold>( {}, (acc, tx) { - acc[tx.id] = tx; + acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx; return acc; }); } @@ -103,10 +103,6 @@ abstract class BitcoinTransactionHistoryBase Future save() async { final data = json.encode({'height': _height, 'transactions': transactions}); - - print('data'); - print(data); - await writeData(path: path, password: _password, data: data); } @@ -168,7 +164,9 @@ abstract class BitcoinTransactionHistoryBase }); _height = content['height'] as int; - } catch (_) {} + } catch (e) { + print(e); + } } void _updateOrInsert(BitcoinTransactionInfo transaction) { diff --git a/lib/bitcoin/bitcoin_transaction_info.dart b/lib/bitcoin/bitcoin_transaction_info.dart index 29cb8521e..f9051a5e0 100644 --- a/lib/bitcoin/bitcoin_transaction_info.dart +++ b/lib/bitcoin/bitcoin_transaction_info.dart @@ -12,6 +12,7 @@ class BitcoinTransactionInfo extends TransactionInfo { {@required String id, @required int height, @required int amount, + @required int fee, @required TransactionDirection direction, @required bool isPending, @required DateTime date, @@ -19,6 +20,7 @@ class BitcoinTransactionInfo extends TransactionInfo { this.id = id; this.height = height; this.amount = amount; + this.fee = fee; this.direction = direction; this.date = date; this.isPending = isPending; @@ -36,37 +38,42 @@ class BitcoinTransactionInfo extends TransactionInfo { : DateTime.now(); final confirmations = obj['confirmations'] as int ?? 0; var direction = TransactionDirection.incoming; + var inputsAmount = 0; + var amount = 0; + var totalOutAmount = 0; for (dynamic vin in vins) { final vout = vin['vout'] as int; final out = vin['tx']['vout'][vout] as Map; final outAddresses = (out['scriptPubKey']['addresses'] as List)?.toSet(); + inputsAmount += doubleToBitcoinAmount(out['value'] as double ?? 0); if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) { direction = TransactionDirection.outgoing; - break; } } - final amount = vout.fold(0, (int acc, dynamic out) { + for (dynamic out in vout) { final outAddresses = out['scriptPubKey']['addresses'] as List ?? []; final ntrs = outAddresses.toSet().intersection(addressesSet); - var amount = acc; + final value = doubleToBitcoinAmount(out['value'] as double ?? 0.0); + totalOutAmount += value; if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) || (direction == TransactionDirection.outgoing && ntrs.isEmpty)) { - amount += doubleToBitcoinAmount(out['value'] as double ?? 0.0); + amount += value; } + } - return amount; - }); + final fee = inputsAmount - totalOutAmount; return BitcoinTransactionInfo( id: id, height: height, isPending: false, + fee: fee, direction: direction, amount: amount, date: date, @@ -101,6 +108,7 @@ class BitcoinTransactionInfo extends TransactionInfo { id: tx.getId(), height: height, isPending: false, + fee: null, direction: TransactionDirection.incoming, amount: amount, date: date, @@ -112,6 +120,7 @@ class BitcoinTransactionInfo extends TransactionInfo { id: data['id'] as String, height: data['height'] as int, amount: data['amount'] as int, + fee: data['fee'] as int, direction: parseTransactionDirectionFromInt(data['direction'] as int), date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int), isPending: data['isPending'] as bool, @@ -124,12 +133,29 @@ class BitcoinTransactionInfo extends TransactionInfo { String amountFormatted() => '${formatAmount(bitcoinAmountToString(amount: amount))} BTC'; + @override + String feeFormatted() => fee != null + ? '${formatAmount(bitcoinAmountToString(amount: fee))} BTC' + : ''; + @override String fiatAmount() => _fiatAmount ?? ''; @override void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount); + BitcoinTransactionInfo updated(BitcoinTransactionInfo info) { + return BitcoinTransactionInfo( + id: id, + height: info.height, + amount: info.amount, + fee: info.fee, + direction: direction ?? info.direction, + date: date ?? info.date, + isPending: isPending ?? info.isPending, + confirmations: info.confirmations); + } + Map toJson() { final m = {}; m['id'] = id; @@ -139,6 +165,7 @@ class BitcoinTransactionInfo extends TransactionInfo { m['date'] = date.millisecondsSinceEpoch; m['isPending'] = isPending; m['confirmations'] = confirmations; + m['fee'] = fee; return m; } } diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index e43c0aeaa..eaf5d2864 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -1,7 +1,8 @@ import 'dart:async'; import 'dart:convert'; +import 'package:cake_wallet/bitcoin/address_to_output_script.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; import 'package:mobx/mobx.dart'; -import 'package:bip39/bip39.dart' as bip39; import 'package:flutter/foundation.dart'; import 'package:rxdart/rxdart.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; @@ -42,8 +43,9 @@ abstract class BitcoinWalletBase extends WalletBase with Store { BitcoinBalance initialBalance}) : balance = initialBalance ?? BitcoinBalance(confirmed: 0, unconfirmed: 0), - hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic), - network: bitcoin.bitcoin), + hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic), + network: bitcoin.bitcoin) + .derivePath("m/0'/0"), addresses = initialAddresses != null ? ObservableList.of(initialAddresses) : ObservableList(), @@ -58,6 +60,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { {@required String password, @required String name, @required String dirPath, + @required WalletInfo walletInfo, String jsonSource}) { final data = json.decode(jsonSource) as Map; final mnemonic = data['mnemonic'] as String; @@ -83,7 +86,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { name: name, accountIndex: accountIndex, initialAddresses: addresses, - initialBalance: balance); + initialBalance: balance, + walletInfo: walletInfo); } static BitcoinWallet build( @@ -91,6 +95,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { @required String password, @required String name, @required String dirPath, + @required WalletInfo walletInfo, List initialAddresses, BitcoinBalance initialBalance, int accountIndex = 0}) { @@ -107,7 +112,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { accountIndex: accountIndex, initialAddresses: initialAddresses, initialBalance: initialBalance, - transactionHistory: history); + transactionHistory: history, + walletInfo: walletInfo); } @override @@ -170,6 +176,22 @@ abstract class BitcoinWalletBase extends WalletBase with Store { return address; } + Future> generateNewAddresses(int count) async { + final list = []; + + for (var i = 0; i < count; i++) { + _accountIndex += 1; + final address = BitcoinAddressRecord(_getAddress(index: _accountIndex), + index: _accountIndex, label: null); + list.add(address); + } + + addresses.addAll(list); + await save(); + + return list; + } + Future updateAddress(String address, {String label}) async { for (final addr in addresses) { if (addr.address == address) { @@ -185,8 +207,10 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Future startSync() async { try { syncStatus = StartingSyncStatus(); - transactionHistory.updateAsync( - onFinished: () => print('transactionHistory update finished!')); + transactionHistory.updateAsync(onFinished: () { + print('transactionHistory update finished!'); + transactionHistory.save(); + }); _subscribeForUpdates(); await _updateBalance(); syncStatus = SyncedSyncStatus(); @@ -238,9 +262,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { final utxs = await unptsFutures; for (final utx in utxs) { - final inAmount = utx.value > totalAmount ? totalAmount : utx.value; - leftAmount = leftAmount - inAmount; - totalInputAmount += inAmount; + leftAmount = leftAmount - utx.value; + totalInputAmount += utx.value; inputs.add(utx); if (leftAmount <= 0) { @@ -279,7 +302,8 @@ abstract class BitcoinWalletBase extends WalletBase with Store { } }); - txb.addOutput(transactionCredentials.address, amount); + txb.addOutput( + addressToOutputScript(transactionCredentials.address), amount); if (changeValue > 0) { txb.addOutput(changeAddress, changeValue); @@ -310,8 +334,10 @@ abstract class BitcoinWalletBase extends WalletBase with Store { bitcoinAmountToDouble(amount: _feeMultiplier(priority)); @override - Future save() async => - await write(path: path, password: _password, data: toJSON()); + Future save() async { + await write(path: path, password: _password, data: toJSON()); + await transactionHistory.save(); + } bitcoin.ECPair keyPairFor({@required int index}) => generateKeyPair(hd: hd, index: index); @@ -321,13 +347,18 @@ abstract class BitcoinWalletBase extends WalletBase with Store { // FIXME: Unimplemented } + @override + void close() async{ + await eclient.close(); + } + void _subscribeForUpdates() { scriptHashes.forEach((sh) async { await _scripthashesUpdateSubject[sh]?.close(); _scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh); _scripthashesUpdateSubject[sh].listen((event) async { - transactionHistory.updateAsync(); await _updateBalance(); + transactionHistory.updateAsync(); }); }); } @@ -358,9 +389,9 @@ abstract class BitcoinWalletBase extends WalletBase with Store { case TransactionPriority.slow: return 6000; case TransactionPriority.regular: - return 9000; + return 22080; case TransactionPriority.fast: - return 15000; + return 24000; default: return 0; } diff --git a/lib/bitcoin/bitcoin_wallet_creation_credentials.dart b/lib/bitcoin/bitcoin_wallet_creation_credentials.dart index 051a5700e..f8df42df0 100644 --- a/lib/bitcoin/bitcoin_wallet_creation_credentials.dart +++ b/lib/bitcoin/bitcoin_wallet_creation_credentials.dart @@ -1,21 +1,23 @@ import 'package:cake_wallet/core/wallet_credentials.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; class BitcoinNewWalletCredentials extends WalletCredentials { - BitcoinNewWalletCredentials({String name}) : super(name: name); + BitcoinNewWalletCredentials({String name, WalletInfo walletInfo}) + : super(name: name, walletInfo: walletInfo); } class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials { BitcoinRestoreWalletFromSeedCredentials( - {String name, String password, this.mnemonic}) - : super(name: name, password: password); + {String name, String password, this.mnemonic, WalletInfo walletInfo}) + : super(name: name, password: password, walletInfo: walletInfo); final String mnemonic; } class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials { BitcoinRestoreWalletFromWIFCredentials( - {String name, String password, this.wif}) - : super(name: name, password: password); + {String name, String password, this.wif, WalletInfo walletInfo}) + : super(name: name, password: password, walletInfo: walletInfo); final String wif; } diff --git a/lib/bitcoin/bitcoin_wallet_service.dart b/lib/bitcoin/bitcoin_wallet_service.dart index 2e9a1c305..205abdcca 100644 --- a/lib/bitcoin/bitcoin_wallet_service.dart +++ b/lib/bitcoin/bitcoin_wallet_service.dart @@ -1,25 +1,34 @@ import 'dart:io'; -import 'package:bip39/bip39.dart' as bip39; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart'; import 'package:cake_wallet/bitcoin/file.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/entities/pathForWallet.dart'; +import 'package:cake_wallet/entities/wallet_info.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:hive/hive.dart'; class BitcoinWalletService extends WalletService< BitcoinNewWalletCredentials, BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> { + BitcoinWalletService(this.walletInfoSource); + + final Box walletInfoSource; + @override Future create(BitcoinNewWalletCredentials credentials) async { final dirPath = await pathForWalletDir( type: WalletType.bitcoin, name: credentials.name); final wallet = BitcoinWalletBase.build( dirPath: dirPath, - mnemonic: bip39.generateMnemonic(), + mnemonic: generateMnemonic(), password: credentials.password, - name: credentials.name); + name: credentials.name, + walletInfo: credentials.walletInfo); await wallet.save(); await wallet.init(); @@ -37,11 +46,15 @@ class BitcoinWalletService extends WalletService< await pathForWalletDir(name: name, type: WalletType.bitcoin); final walletPath = '$walletDirPath/$name'; final walletJSONRaw = await read(path: walletPath, password: password); + final walletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(name, WalletType.bitcoin), + orElse: () => null); final wallet = BitcoinWalletBase.fromJSON( password: password, name: name, dirPath: walletDirPath, - jsonSource: walletJSONRaw); + jsonSource: walletJSONRaw, + walletInfo: walletInfo); await wallet.init(); return wallet; @@ -62,15 +75,21 @@ class BitcoinWalletService extends WalletService< @override Future restoreFromSeed( BitcoinRestoreWalletFromSeedCredentials credentials) async { + if (!validateMnemonic(credentials.mnemonic)) { + throw BitcoinMnemonicIsIncorrectException(); + } + final dirPath = await pathForWalletDir( type: WalletType.bitcoin, name: credentials.name); final wallet = BitcoinWalletBase.build( dirPath: dirPath, name: credentials.name, password: credentials.password, - mnemonic: credentials.mnemonic); + mnemonic: credentials.mnemonic, + walletInfo: credentials.walletInfo); await wallet.save(); await wallet.init(); + await wallet.generateNewAddresses(32); return wallet; } diff --git a/lib/bitcoin/electrum.dart b/lib/bitcoin/electrum.dart index 5348959e7..20dc29688 100644 --- a/lib/bitcoin/electrum.dart +++ b/lib/bitcoin/electrum.dart @@ -22,8 +22,9 @@ String jsonrpcparams(List params) { } String jsonrpc( - {String method, List params, int id, double version = 2.0}) => - '{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n'; + {String method, List params, int id, double version = 2.0}) => + '{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json + .encode(params)}}\n'; class SocketTask { SocketTask({this.completer, this.isSubscription, this.subject}); @@ -49,6 +50,7 @@ class ElectrumClient { final Map _tasks; bool _isConnected; Timer _aliveTimer; + String unterminatedString; Future connectToUri(String uri) async { final splittedUri = uri.split(':'); @@ -73,26 +75,31 @@ class ElectrumClient { socket.listen((Uint8List event) { try { - final jsoned = - json.decode(utf8.decode(event.toList())) as Map; - print(jsoned); - final method = jsoned['method']; - final id = jsoned['id'] as String; - final params = jsoned['result']; + _handleResponse(utf8.decode(event.toList())); + } on FormatException catch (e) { + final msg = e.message.toLowerCase(); - if (method is String) { - _methodHandler(method: method, request: jsoned); - return; + if (msg == 'Unterminated string'.toLowerCase()) { + unterminatedString = e.source as String; } - _finish(id, params); + if (msg == 'Unexpected character'.toLowerCase()) { + unterminatedString += e.source as String; + } + + if (isJSONStringCorrect(unterminatedString)) { + _handleResponse(unterminatedString); + unterminatedString = null; + } } catch (e) { print(e); } }, onError: (Object error) { print(error.toString()); _setIsConnected(false); - }, onDone: () => _setIsConnected(false)); + }, onDone: () { + _setIsConnected(false); + }); keepAlive(); } @@ -103,7 +110,7 @@ class ElectrumClient { Future ping() async { try { - // await callWithTimeout(method: 'server.ping'); + await callWithTimeout(method: 'server.ping'); _setIsConnected(true); } on RequestFailedTimeoutException catch (_) { _setIsConnected(false); @@ -146,7 +153,7 @@ class ElectrumClient { }); Future>> getListUnspentWithAddress( - String address) => + String address) => call( method: 'blockchain.scripthash.listunspent', params: [scriptHash(address)]).then((dynamic result) { @@ -197,7 +204,7 @@ class ElectrumClient { }); Future> getTransactionRaw( - {@required String hash}) async => + {@required String hash}) async => call(method: 'blockchain.transaction.get', params: [hash, true]) .then((dynamic result) { if (result is Map) { @@ -209,20 +216,24 @@ class ElectrumClient { Future> getTransactionExpanded( {@required String hash}) async { - final originalTx = await getTransactionRaw(hash: hash); - final vins = originalTx['vin'] as List; + try { + final originalTx = await getTransactionRaw(hash: hash); + final vins = originalTx['vin'] as List; - for (dynamic vin in vins) { - if (vin is Map) { - vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String); + for (dynamic vin in vins) { + if (vin is Map) { + vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String); + } } - } - return originalTx; + return originalTx; + } catch (_) { + return {}; + } } Future broadcastTransaction( - {@required String transactionRaw}) async => + {@required String transactionRaw}) async => call(method: 'blockchain.transaction.broadcast', params: [transactionRaw]) .then((dynamic result) { if (result is String) { @@ -233,14 +244,14 @@ class ElectrumClient { }); Future> getMerkle( - {@required String hash, @required int height}) async => + {@required String hash, @required int height}) async => await call( method: 'blockchain.transaction.get_merkle', params: [hash, height]) as Map; Future> getHeader({@required int height}) async => await call(method: 'blockchain.block.get_header', params: [height]) - as Map; + as Map; Future estimatefee({@required int p}) => call(method: 'blockchain.estimatefee', params: [p]) @@ -256,16 +267,17 @@ class ElectrumClient { return 0; }); - BehaviorSubject scripthashUpdate(String scripthash) => - subscribe( - id: 'blockchain.scripthash.subscribe:$scripthash', - method: 'blockchain.scripthash.subscribe', - params: [scripthash]); + BehaviorSubject scripthashUpdate(String scripthash) { + _id += 1; + return subscribe( + id: 'blockchain.scripthash.subscribe:$scripthash', + method: 'blockchain.scripthash.subscribe', + params: [scripthash]); + } - BehaviorSubject subscribe( - {@required String id, - @required String method, - List params = const []}) { + BehaviorSubject subscribe({@required String id, + @required String method, + List params = const []}) { final subscription = BehaviorSubject(); _regisrySubscription(id, subscription); socket.write(jsonrpc(method: method, id: _id, params: params)); @@ -273,7 +285,8 @@ class ElectrumClient { return subscription; } - Future call({String method, List params = const []}) { + Future call({String method, List params = const []}) async { + await Future.delayed(Duration(milliseconds: 100)); final completer = Completer(); _id += 1; final id = _id; @@ -283,10 +296,9 @@ class ElectrumClient { return completer.future; } - Future callWithTimeout( - {String method, - List params = const [], - int timeout = 2000}) async { + Future callWithTimeout({String method, + List params = const [], + int timeout = 2000}) async { final completer = Completer(); _id += 1; final id = _id; @@ -307,8 +319,15 @@ class ElectrumClient { socket.write(jsonrpc(method: method, id: _id, params: params)); } - void _regisryTask(int id, Completer completer) => _tasks[id.toString()] = - SocketTask(completer: completer, isSubscription: false); + Future close() async { + _aliveTimer.cancel(); + await socket.close(); + onConnectionStatusChange = null; + } + + void _regisryTask(int id, Completer completer) => + _tasks[id.toString()] = + SocketTask(completer: completer, isSubscription: false); void _regisrySubscription(String id, BehaviorSubject subject) => _tasks[id] = SocketTask(subject: subject, isSubscription: true); @@ -351,6 +370,31 @@ class ElectrumClient { _isConnected = isConnected; } + + void _handleResponse(String response) { + print('Response: $response'); + final jsoned = json.decode(response) as Map; + // print(jsoned); + final method = jsoned['method']; + final id = jsoned['id'] as String; + final result = jsoned['result']; + + if (method is String) { + _methodHandler(method: method, request: jsoned); + return; + } + + _finish(id, result); + } +} +// FIXME: move me +bool isJSONStringCorrect(String source) { + try { + json.decode(source); + return true; + } catch (_) { + return false; + } } class RequestFailedTimeoutException implements Exception { diff --git a/lib/bitcoin/pending_bitcoin_transaction.dart b/lib/bitcoin/pending_bitcoin_transaction.dart index d2c018a35..edd5a0450 100644 --- a/lib/bitcoin/pending_bitcoin_transaction.dart +++ b/lib/bitcoin/pending_bitcoin_transaction.dart @@ -16,6 +16,7 @@ class PendingBitcoinTransaction with PendingTransaction { final int amount; final int fee; + @override String get id => _tx.getId(); @override diff --git a/lib/core/seed_validator.dart b/lib/core/seed_validator.dart index 36a06cb03..94ada7481 100644 --- a/lib/core/seed_validator.dart +++ b/lib/core/seed_validator.dart @@ -1,4 +1,4 @@ -import 'package:bip39/src/wordlists/english.dart' as bitcoin_english; +import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart' as bitcoin_electrum; import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/entities/mnemonic_item.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; @@ -64,7 +64,7 @@ class SeedValidator extends Validator { static List getBitcoinWordList(String language) { assert(language.toLowerCase() == LanguageList.english.toLowerCase()); - return bitcoin_english.WORDLIST; + return bitcoin_electrum.englishWordlist; } @override diff --git a/lib/core/wallet_base.dart b/lib/core/wallet_base.dart index 8eea39dca..d08eff0ef 100644 --- a/lib/core/wallet_base.dart +++ b/lib/core/wallet_base.dart @@ -52,4 +52,6 @@ abstract class WalletBase { Future save(); Future rescan({int height}); + + void close(); } diff --git a/lib/core/wallet_credentials.dart b/lib/core/wallet_credentials.dart index 3998bb005..ddfafab1a 100644 --- a/lib/core/wallet_credentials.dart +++ b/lib/core/wallet_credentials.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/entities/wallet_info.dart'; abstract class WalletCredentials { - WalletCredentials({this.name, this.password, this.height}); + WalletCredentials({this.name, this.password, this.height, this.walletInfo}); final String name; final int height; diff --git a/lib/di.dart b/lib/di.dart index fd73da8fc..12828300d 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -16,6 +16,7 @@ import 'package:cake_wallet/src/screens/contact/contact_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart'; import 'package:cake_wallet/src/screens/faq/faq_page.dart'; +import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_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/pin_code/pin_code_widget.dart'; @@ -291,17 +292,16 @@ Future setup( getIt.get()));*/ getIt.registerFactoryParam( - (AccountListItem account, _) => - MoneroAccountEditOrCreateViewModel(( - getIt.get().wallet as MoneroWallet).accountList, - accountListItem: account)); + AccountListItem, void>( + (AccountListItem account, _) => MoneroAccountEditOrCreateViewModel( + (getIt.get().wallet as MoneroWallet).accountList, + accountListItem: account)); - getIt.registerFactoryParam((AccountListItem account, _) => - MoneroAccountEditOrCreatePage( + getIt.registerFactoryParam( + (AccountListItem account, _) => MoneroAccountEditOrCreatePage( moneroAccountCreationViewModel: - getIt.get(param1: account))); + getIt.get(param1: account))); getIt.registerFactory(() { final appStore = getIt.get(); @@ -376,7 +376,7 @@ Future setup( getIt.registerFactory(() => MoneroWalletService(walletInfoSource)); - getIt.registerFactory(() => BitcoinWalletService()); + getIt.registerFactory(() => BitcoinWalletService(walletInfoSource)); getIt.registerFactoryParam( (WalletType param1, __) { @@ -421,5 +421,10 @@ Future setup( getIt.get().shouldSaveRecipientAddress, transactionDescriptionBox)); + getIt.registerFactoryParam( + (para1, param2) => NewWalletTypePage(getIt.get(), + onTypeSelected: para1, isNewWallet: param2)); + getIt.registerFactory(() => PreSeedPage()); } diff --git a/lib/entities/balance.dart b/lib/entities/balance.dart index e8564c5a2..89f3cd725 100644 --- a/lib/entities/balance.dart +++ b/lib/entities/balance.dart @@ -1,3 +1,9 @@ +import 'package:cake_wallet/entities/balance_display_mode.dart'; + abstract class Balance { - const Balance(); + const Balance(this.availableModes); + + final List availableModes; + + String formattedBalance(BalanceDisplayMode mode); } diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index 28fc3da90..11e9fa863 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -45,7 +45,7 @@ Future defaultSettingsMigration( FiatCurrency.usd.toString()); await sharedPreferences.setInt( PreferencesKey.currentTransactionPriorityKey, - TransactionPriority.standart.raw); + TransactionPriority.standard.raw); await sharedPreferences.setInt( PreferencesKey.currentBalanceDisplayModeKey, BalanceDisplayMode.availableBalance.raw); diff --git a/lib/entities/load_current_wallet.dart b/lib/entities/load_current_wallet.dart index ef690521f..88c236088 100644 --- a/lib/entities/load_current_wallet.dart +++ b/lib/entities/load_current_wallet.dart @@ -19,5 +19,5 @@ Future loadCurrentWallet() async { await getIt.get().getWalletPassword(walletName: name); final _service = getIt.get(param1: type); final wallet = await _service.openWallet(name, password); - appStore.wallet = wallet; + appStore.changeCurrentWallet(wallet); } diff --git a/lib/entities/node_list.dart b/lib/entities/node_list.dart index 470b9bc99..118394e6f 100644 --- a/lib/entities/node_list.dart +++ b/lib/entities/node_list.dart @@ -39,9 +39,9 @@ Future> loadElectrumServerList() async { Future resetToDefault(Box nodeSource) async { final moneroNodes = await loadDefaultNodes(); - // final bitcoinElectrumServerList = await loadElectrumServerList(); - // final nodes = moneroNodes + bitcoinElectrumServerList; + final bitcoinElectrumServerList = await loadElectrumServerList(); + final nodes = moneroNodes + bitcoinElectrumServerList; await nodeSource.clear(); - await nodeSource.addAll(moneroNodes); + await nodeSource.addAll(nodes); } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 53d59460a..f9bf224f8 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -9,7 +9,7 @@ class PreferencesKey { static const shouldSaveRecipientAddressKey = 'save_recipient_address'; static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication'; - static const currentDarkTheme = 'dark_theme'; + static const currentTheme = 'current_theme'; static const displayActionListModeKey = 'display_list_mode'; static const currentPinLength = 'current_pin_length'; static const currentLanguageCode = 'language_code'; diff --git a/lib/entities/transaction_info.dart b/lib/entities/transaction_info.dart index c9ad89c71..986ab384d 100644 --- a/lib/entities/transaction_info.dart +++ b/lib/entities/transaction_info.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/utils/mobx.dart'; abstract class TransactionInfo extends Object with Keyable { String id; int amount; + int fee; TransactionDirection direction; bool isPending; DateTime date; @@ -11,6 +12,7 @@ abstract class TransactionInfo extends Object with Keyable { int confirmations; String amountFormatted(); String fiatAmount(); + String feeFormatted(); void changeFiatAmount(String amount); @override diff --git a/lib/entities/transaction_priority.dart b/lib/entities/transaction_priority.dart index 9bad84c9f..b7661dbdc 100644 --- a/lib/entities/transaction_priority.dart +++ b/lib/entities/transaction_priority.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/entities/enumerable_item.dart'; @@ -17,7 +18,23 @@ class TransactionPriority extends EnumerableItem with Serializable { static const medium = TransactionPriority(title: 'Medium', raw: 2); static const fast = TransactionPriority(title: 'Fast', raw: 3); static const fastest = TransactionPriority(title: 'Fastest', raw: 4); - static const standart = slow; + static const standard = slow; + + + static List forWalletType(WalletType type) { + switch (type) { + case WalletType.monero: + return TransactionPriority.all; + case WalletType.bitcoin: + return [ + TransactionPriority.slow, + TransactionPriority.regular, + TransactionPriority.fast + ]; + default: + return []; + } + } static TransactionPriority deserialize({int raw}) { switch (raw) { diff --git a/lib/entities/wallet_type.dart b/lib/entities/wallet_type.dart index f1a48ce52..ef4027a54 100644 --- a/lib/entities/wallet_type.dart +++ b/lib/entities/wallet_type.dart @@ -48,3 +48,14 @@ String walletTypeToString(WalletType type) { return ''; } } + +String walletTypeToDisplayName(WalletType type) { + switch (type) { + case WalletType.monero: + return 'Monero'; + case WalletType.bitcoin: + return 'Bitcoin (Electrum)'; + default: + return ''; + } +} diff --git a/lib/exchange/xmrto/xmrto_exchange_provider.dart b/lib/exchange/xmrto/xmrto_exchange_provider.dart index 26adaedef..cc63ff1a3 100644 --- a/lib/exchange/xmrto/xmrto_exchange_provider.dart +++ b/lib/exchange/xmrto/xmrto_exchange_provider.dart @@ -27,11 +27,14 @@ class XMRTOExchangeProvider extends ExchangeProvider { static const _orderParameterUriSuffix = '/order_parameter_query'; static const _orderStatusUriSuffix = '/order_status_query/'; static const _orderCreateUriSuffix = '/order_create/'; + static const _headers = { + 'Content-Type': 'application/json', + 'User-Agent': userAgent + }; static Future _checkIsAvailable() async { const url = originalApiUri + _orderParameterUriSuffix; - final response = - await get(url, headers: {'Content-Type': 'application/json'}); + final response = await get(url, headers: _headers); return !(response.statusCode == 403); } @@ -91,9 +94,8 @@ class XMRTOExchangeProvider extends ExchangeProvider { Future createTrade({TradeRequest request}) async { final _request = request as XMRTOTradeRequest; final url = originalApiUri + _orderCreateUriSuffix; - final _amount = _request.isBTCRequest - ? _request.receiveAmount - : _request.amount; + final _amount = + _request.isBTCRequest ? _request.receiveAmount : _request.amount; final _amountCurrency = _request.isBTCRequest ? _request.to.toString() @@ -112,8 +114,8 @@ class XMRTOExchangeProvider extends ExchangeProvider { 'amount_currency': _amountCurrency, 'btc_dest_address': _request.address }; - final response = await post(url, - headers: {'Content-Type': 'application/json'}, body: json.encode(body)); + final response = + await post(url, headers: _headers, body: json.encode(body)); if (response.statusCode != 201) { if (response.statusCode == 400) { @@ -141,13 +143,10 @@ class XMRTOExchangeProvider extends ExchangeProvider { @override Future findTradeById({@required String id}) async { - const headers = { - 'Content-Type': 'application/json', - 'User-Agent': userAgent - }; final url = originalApiUri + _orderStatusUriSuffix; final body = {'uuid': id}; - final response = await post(url, headers: headers, body: json.encode(body)); + final response = + await post(url, headers: _headers, body: json.encode(body)); if (response.statusCode != 200) { if (response.statusCode == 400) { @@ -210,8 +209,7 @@ class XMRTOExchangeProvider extends ExchangeProvider { Future _fetchRates() async { try { final url = originalApiUri + _orderParameterUriSuffix; - final response = - await get(url, headers: {'Content-Type': 'application/json'}); + final response = await get(url, headers: _headers); final responseJSON = json.decode(response.body) as Map; final price = double.parse(responseJSON['price'] as String); diff --git a/lib/generated/i18n.dart b/lib/generated/i18n.dart index fd0c0f70e..106b786b7 100644 --- a/lib/generated/i18n.dart +++ b/lib/generated/i18n.dart @@ -44,6 +44,7 @@ class S implements WidgetsLocalizations { String get authentication => "Authentication"; String get available_balance => "Available Balance"; String get biometric_auth_reason => "Scan your fingerprint to authenticate"; + String get bright_theme => "Bright"; String get buy => "Buy"; String get cake_wallet => "Cake Wallet"; String get cancel => "Cancel"; @@ -57,6 +58,7 @@ class S implements WidgetsLocalizations { String get choose_account => "Choose account"; String get choose_wallet_currency => "Please choose wallet currency:"; String get clear => "Clear"; + String get color_theme => "Color theme"; String get confirm => "Confirm"; String get confirm_delete_template => "This action will delete this template. Do you wish to continue?"; String get confirm_delete_wallet => "This action will delete this wallet. Do you wish to continue?"; @@ -71,6 +73,7 @@ class S implements WidgetsLocalizations { String get create_new => "Create New Wallet"; String get create_new_account => "Create new account"; String get creating_new_wallet => "Creating new wallet"; + String get dark_theme => "Dark"; String get delete => "Delete"; String get digit_pin => "-digit PIN"; String get edit => "Edit"; @@ -107,6 +110,7 @@ class S implements WidgetsLocalizations { String get id => "ID: "; String get incoming => "Incoming"; String get incorrect_seed => "The text entered is not valid."; + String get light_theme => "Light"; String get loading_your_wallet => "Loading your wallet"; String get login => "Login"; String get new_node_testing => "New node testing"; @@ -208,13 +212,13 @@ class S implements WidgetsLocalizations { String get send_error_currency => "Currency can only contain numbers"; String get send_error_minimum_value => "Minimum value of amount is 0.01"; String get send_estimated_fee => "Estimated fee:"; - String get send_fee => "Fee:"; + String get send_fee => "Fee"; String get send_got_it => "Got it"; String get send_name => "Name"; String get send_new => "New"; String get send_payment_id => "Payment ID (optional)"; String get send_sending => "Sending..."; - String get send_success => "Your Monero was successfully sent"; + String send_success(String crypto) => "Your ${crypto} was successfully sent"; String get send_templates => "Templates"; String get send_title => "Send"; String get send_xmr => "Send XMR"; @@ -394,9 +398,11 @@ class $de extends S { @override String get biometric_auth_reason => "Scannen Sie Ihren Fingerabdruck zur Authentifizierung"; @override + String get dark_theme => "Dunkel"; + @override String get transaction_sent => "Transaktion gesendet!"; @override - String get send_fee => "Gebühr:"; + String get send_fee => "Gebühr"; @override String get password => "Passwort"; @override @@ -636,7 +642,7 @@ class $de extends S { @override String get trade_details_created_at => "Hergestellt in"; @override - String get send_success => "Ihr Monero wurde erfolgreich gesendet"; + String send_success(String crypto) => "Ihr ${crypto} wurde erfolgreich gesendet"; @override String get settings_wallets => "Brieftaschen"; @override @@ -646,6 +652,8 @@ class $de extends S { @override String get filters => "Filter"; @override + String get color_theme => "Farbthema"; + @override String get settings_current_node => "Aktueller Knoten"; @override String get copy_id => "ID kopieren"; @@ -886,12 +894,16 @@ class $de extends S { @override String get trade_state_confirming => "Bestätigung"; @override + String get bright_theme => "Hell"; + @override String get send => "Senden"; @override String get send_title => "Senden Sie"; @override String get error_text_keys => "Walletschlüssel können nur 64 hexadezimale Zeichen enthalten"; @override + String get light_theme => "Licht"; + @override String get settings_save_recipient_address => "Empfängeradresse speichern"; @override String get change_exchange_provider => "Wechseln Sie den Exchange-Anbieter"; @@ -1086,6 +1098,8 @@ class $hi extends S { @override String get biometric_auth_reason => "प्रमाणित करने के लिए अपने फ़िंगरप्रिंट को स्कैन करें"; @override + String get dark_theme => "अंधेरा"; + @override String get transaction_sent => "भेजा गया लेन-देन"; @override String get send_fee => "शुल्क:"; @@ -1328,7 +1342,7 @@ class $hi extends S { @override String get trade_details_created_at => "पर बनाया गया"; @override - String get send_success => "आपका Monero सफलतापूर्वक भेजा गया"; + String send_success(String crypto) => "आपका ${crypto} सफलतापूर्वक भेजा गया"; @override String get settings_wallets => "पर्स"; @override @@ -1338,6 +1352,8 @@ class $hi extends S { @override String get filters => "फ़िल्टर"; @override + String get color_theme => "रंग विषय"; + @override String get settings_current_node => "वर्तमान नोड"; @override String get copy_id => "प्रतिलिपि ID"; @@ -1578,12 +1594,16 @@ class $hi extends S { @override String get trade_state_confirming => "पुष्टि"; @override + String get bright_theme => "उज्ज्वल"; + @override String get send => "संदेश"; @override String get send_title => "संदेश"; @override String get error_text_keys => "वॉलेट कीज़ में हेक्स में केवल 64 वर्ण हो सकते हैं"; @override + String get light_theme => "रोशनी"; + @override String get settings_save_recipient_address => "प्राप्तकर्ता का पता सहेजें"; @override String get change_exchange_provider => "एक्सचेंज प्रदाता बदलें"; @@ -1778,9 +1798,11 @@ class $ru extends S { @override String get biometric_auth_reason => "Отсканируйте свой отпечаток пальца для аутентификации"; @override + String get dark_theme => "Темная"; + @override String get transaction_sent => "Tранзакция отправлена!"; @override - String get send_fee => "Комиссия:"; + String get send_fee => "Комиссия"; @override String get password => "Пароль"; @override @@ -2020,7 +2042,7 @@ class $ru extends S { @override String get trade_details_created_at => "Создано"; @override - String get send_success => "Ваш Monero был успешно отправлен"; + String send_success(String crypto) => "Ваш ${crypto} был успешно отправлен"; @override String get settings_wallets => "Кошельки"; @override @@ -2030,6 +2052,8 @@ class $ru extends S { @override String get filters => "Фильтр"; @override + String get color_theme => "Цветовая тема"; + @override String get settings_current_node => "Текущая нода"; @override String get copy_id => "Скопировать ID"; @@ -2270,12 +2294,16 @@ class $ru extends S { @override String get trade_state_confirming => "Подтверждение"; @override + String get bright_theme => "Яркая"; + @override String get send => "Отправить"; @override String get send_title => "Отправить"; @override String get error_text_keys => "Ключи кошелька могут содержать только 64 символа в hex"; @override + String get light_theme => "Светлая"; + @override String get settings_save_recipient_address => "Сохранять адрес получателя"; @override String get change_exchange_provider => "Изменить провайдера обмена"; @@ -2470,9 +2498,11 @@ class $ko extends S { @override String get biometric_auth_reason => "지문을 스캔하여 인증"; @override + String get dark_theme => "어두운"; + @override String get transaction_sent => "거래가 전송되었습니다!"; @override - String get send_fee => "회비:"; + String get send_fee => "회비"; @override String get password => "암호"; @override @@ -2712,7 +2742,7 @@ class $ko extends S { @override String get trade_details_created_at => "에 작성"; @override - String get send_success => "Monero가 성공적으로 전송되었습니다"; + String send_success(String crypto) => "${crypto}가 성공적으로 전송되었습니다"; @override String get settings_wallets => "지갑"; @override @@ -2722,6 +2752,8 @@ class $ko extends S { @override String get filters => "필터"; @override + String get color_theme => "색상 테마"; + @override String get settings_current_node => "현재 노드"; @override String get copy_id => "부 ID"; @@ -2962,12 +2994,16 @@ class $ko extends S { @override String get trade_state_confirming => "확인 중"; @override + String get bright_theme => "선명한"; + @override String get send => "보내다"; @override String get send_title => "보내다"; @override String get error_text_keys => "지갑 키는 16 진수로 64 자만 포함 할 수 있습니다"; @override + String get light_theme => "빛"; + @override String get settings_save_recipient_address => "수신자 주소 저장"; @override String get change_exchange_provider => "교환 공급자 변경"; @@ -3162,9 +3198,11 @@ class $pt extends S { @override String get biometric_auth_reason => "Digitalize sua impressão digital para autenticar"; @override + String get dark_theme => "Sombria"; + @override String get transaction_sent => "Transação enviada!"; @override - String get send_fee => "Taxa:"; + String get send_fee => "Taxa"; @override String get password => "Senha"; @override @@ -3404,7 +3442,7 @@ class $pt extends S { @override String get trade_details_created_at => "Criada em"; @override - String get send_success => "Seu Monero foi enviado com sucesso"; + String send_success(String crypto) => "Seu ${crypto} foi enviado com sucesso"; @override String get settings_wallets => "Carteiras"; @override @@ -3414,6 +3452,8 @@ class $pt extends S { @override String get filters => "Filtro"; @override + String get color_theme => "Tema de cor"; + @override String get settings_current_node => "Nó atual"; @override String get copy_id => "Copiar ID"; @@ -3654,12 +3694,16 @@ class $pt extends S { @override String get trade_state_confirming => "Confirmando"; @override + String get bright_theme => "Brilhante"; + @override String get send => "Enviar"; @override String get send_title => "Enviar"; @override String get error_text_keys => "As chaves da carteira podem conter apenas 64 caracteres em hexadecimal"; @override + String get light_theme => "Luz"; + @override String get settings_save_recipient_address => "Salvar endereço do destinatário"; @override String get change_exchange_provider => "Alterar o provedor de troca"; @@ -3854,9 +3898,11 @@ class $uk extends S { @override String get biometric_auth_reason => "Відскануйте свій відбиток пальця для аутентифікації"; @override + String get dark_theme => "Темна"; + @override String get transaction_sent => "Tранзакцію відправлено!"; @override - String get send_fee => "Комісія:"; + String get send_fee => "Комісія"; @override String get password => "Пароль"; @override @@ -4096,7 +4142,7 @@ class $uk extends S { @override String get trade_details_created_at => "Створено"; @override - String get send_success => "Ваш Monero успішно надісланий"; + String send_success(String crypto) => "Ваш ${crypto} успішно надісланий"; @override String get settings_wallets => "Гаманці"; @override @@ -4106,6 +4152,8 @@ class $uk extends S { @override String get filters => "Фільтр"; @override + String get color_theme => "Кольорова тема"; + @override String get settings_current_node => "Поточний вузол"; @override String get copy_id => "Скопіювати ID"; @@ -4346,12 +4394,16 @@ class $uk extends S { @override String get trade_state_confirming => "Підтвердження"; @override + String get bright_theme => "Яскрава"; + @override String get send => "Відправити"; @override String get send_title => "Відправити"; @override String get error_text_keys => "Ключі гаманця можуть містити тільки 64 символів в hex"; @override + String get light_theme => "Світла"; + @override String get settings_save_recipient_address => "Зберігати адресу отримувача"; @override String get change_exchange_provider => "Змінити провайдера обміну"; @@ -4546,9 +4598,11 @@ class $ja extends S { @override String get biometric_auth_reason => "प指紋をスキャンして認証する"; @override + String get dark_theme => "闇"; + @override String get transaction_sent => "トランザクションが送信されました!"; @override - String get send_fee => "費用:"; + String get send_fee => "費用"; @override String get password => "パスワード"; @override @@ -4788,7 +4842,7 @@ class $ja extends S { @override String get trade_details_created_at => "で作成"; @override - String get send_success => "Moneroが送信されました"; + String send_success(String crypto) => "${crypto}が送信されました"; @override String get settings_wallets => "財布"; @override @@ -4798,6 +4852,8 @@ class $ja extends S { @override String get filters => "フィルタ"; @override + String get color_theme => "カラーテーマ"; + @override String get settings_current_node => "現在のノード"; @override String get copy_id => "IDをコピー"; @@ -5038,12 +5094,16 @@ class $ja extends S { @override String get trade_state_confirming => "確認中"; @override + String get bright_theme => "明るい"; + @override String get send => "送る"; @override String get send_title => "を送信"; @override String get error_text_keys => "ウォレットキーには、16進数で64文字しか含めることができません"; @override + String get light_theme => "光"; + @override String get settings_save_recipient_address => "受信者のアドレスを保存"; @override String get change_exchange_provider => "Exchangeプロバイダーの変更"; @@ -5242,9 +5302,11 @@ class $pl extends S { @override String get biometric_auth_reason => "Zeskanuj swój odcisk palca, aby go uwierzytelnić"; @override + String get dark_theme => "Ciemny"; + @override String get transaction_sent => "Transakcja wysłana!"; @override - String get send_fee => "Opłata:"; + String get send_fee => "Opłata"; @override String get password => "Hasło"; @override @@ -5484,7 +5546,7 @@ class $pl extends S { @override String get trade_details_created_at => "Utworzono w"; @override - String get send_success => "Twoje Monero zostało pomyślnie wysłane"; + String send_success(String crypto) => "Twoje ${crypto} zostało pomyślnie wysłane"; @override String get settings_wallets => "Portfele"; @override @@ -5494,6 +5556,8 @@ class $pl extends S { @override String get filters => "Filtr"; @override + String get color_theme => "Motyw kolorystyczny"; + @override String get settings_current_node => "Bieżący węzeł"; @override String get copy_id => "ID kopii"; @@ -5734,12 +5798,16 @@ class $pl extends S { @override String get trade_state_confirming => "Potwierdzam"; @override + String get bright_theme => "Jasny"; + @override String get send => "Wysłać"; @override String get send_title => "Wyślij"; @override String get error_text_keys => "Klucze portfela mogą zawierać tylko 64 znaki w systemie szesnastkowym"; @override + String get light_theme => "Lekki"; + @override String get settings_save_recipient_address => "Zapisz adres odbiorcy"; @override String get change_exchange_provider => "Zmień dostawcę programu Exchange"; @@ -5934,9 +6002,11 @@ class $es extends S { @override String get biometric_auth_reason => "Escanee su huella digital para autenticar"; @override + String get dark_theme => "Oscura"; + @override String get transaction_sent => "Transacción enviada!"; @override - String get send_fee => "Cuota:"; + String get send_fee => "Cuota"; @override String get password => "Contraseña"; @override @@ -6176,7 +6246,7 @@ class $es extends S { @override String get trade_details_created_at => "Creado en"; @override - String get send_success => "Su Monero fue enviado con éxito"; + String send_success(String crypto) => "Su ${crypto} fue enviado con éxito"; @override String get settings_wallets => "Carteras"; @override @@ -6186,6 +6256,8 @@ class $es extends S { @override String get filters => "Filtrar"; @override + String get color_theme => "Tema de color"; + @override String get settings_current_node => "Nodo actual"; @override String get copy_id => "Copiar ID"; @@ -6426,12 +6498,16 @@ class $es extends S { @override String get trade_state_confirming => "Confirmando"; @override + String get bright_theme => "Brillante"; + @override String get send => "Enviar"; @override String get send_title => "Enviar"; @override String get error_text_keys => "Las llaves de billetera solo pueden contener 64 caracteres en hexadecimal"; @override + String get light_theme => "Ligera"; + @override String get settings_save_recipient_address => "Guardar dirección del destinatario"; @override String get change_exchange_provider => "Cambiar proveedor de intercambio"; @@ -6626,9 +6702,11 @@ class $nl extends S { @override String get biometric_auth_reason => "Scan uw vingerafdruk om te verifiëren"; @override + String get dark_theme => "Donker"; + @override String get transaction_sent => "Transactie verzonden!"; @override - String get send_fee => "Vergoeding:"; + String get send_fee => "Vergoeding"; @override String get password => "Wachtwoord"; @override @@ -6868,7 +6946,7 @@ class $nl extends S { @override String get trade_details_created_at => "Gemaakt bij"; @override - String get send_success => "Uw Monero is succesvol verzonden"; + String send_success(String crypto) => "Uw ${crypto} is succesvol verzonden"; @override String get settings_wallets => "Portemonnee"; @override @@ -6878,6 +6956,8 @@ class $nl extends S { @override String get filters => "Filter"; @override + String get color_theme => "Kleur thema"; + @override String get settings_current_node => "Huidige knooppunt"; @override String get copy_id => "ID kopiëren"; @@ -7118,12 +7198,16 @@ class $nl extends S { @override String get trade_state_confirming => "Bevestiging"; @override + String get bright_theme => "Helder"; + @override String get send => "Sturen"; @override String get send_title => "Stuur"; @override String get error_text_keys => "Portefeuillesleutels kunnen maximaal 64 tekens bevatten in hexadecimale volgorde"; @override + String get light_theme => "Licht"; + @override String get settings_save_recipient_address => "Adres ontvanger opslaan"; @override String get change_exchange_provider => "Wijzig Exchange Provider"; @@ -7318,9 +7402,11 @@ class $zh extends S { @override String get biometric_auth_reason => "掃描指紋以進行身份驗證"; @override + String get dark_theme => "黑暗"; + @override String get transaction_sent => "交易已发送"; @override - String get send_fee => "費用:"; + String get send_fee => "費用"; @override String get password => "密码"; @override @@ -7560,7 +7646,7 @@ class $zh extends S { @override String get trade_details_created_at => "创建于"; @override - String get send_success => "你Monero已成功發送"; + String send_success(String crypto) => "你${crypto}已成功發送"; @override String get settings_wallets => "皮夹"; @override @@ -7570,6 +7656,8 @@ class $zh extends S { @override String get filters => "過濾"; @override + String get color_theme => "顏色主題"; + @override String get settings_current_node => "当前节点"; @override String get copy_id => "复印ID"; @@ -7810,12 +7898,16 @@ class $zh extends S { @override String get trade_state_confirming => "确认中"; @override + String get bright_theme => "亮"; + @override String get send => "发送"; @override String get send_title => "發送"; @override String get error_text_keys => "钱包密钥只能包含16个字符的十六进制字符"; @override + String get light_theme => "光"; + @override String get settings_save_recipient_address => "保存收件人地址"; @override String get change_exchange_provider => "更改交易所提供商"; diff --git a/lib/main.dart b/lib/main.dart index 9cd55e054..38460fcca 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; @@ -83,7 +84,6 @@ void main() async { ))))); } } - Future initialSetup( {@required SharedPreferences sharedPreferences, @required Box nodes, @@ -123,27 +123,25 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) { final settingsStore = getIt.get().settingsStore; - - if (settingsStore.theme == null) { - settingsStore.isDarkTheme = false; - } - final statusBarColor = Colors.transparent; - final statusBarBrightness = - settingsStore.isDarkTheme ? Brightness.light : Brightness.dark; - final statusBarIconBrightness = - settingsStore.isDarkTheme ? Brightness.light : Brightness.dark; final authenticationStore = getIt.get(); final initialRoute = authenticationStore.state == AuthenticationState.denied ? Routes.disclaimer : Routes.login; - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarColor: statusBarColor, - statusBarBrightness: statusBarBrightness, - statusBarIconBrightness: statusBarIconBrightness)); - return Observer(builder: (BuildContext context) { + final currentTheme = settingsStore.currentTheme; + final statusBarBrightness = currentTheme.type == ThemeType.dark + ? Brightness.light + : Brightness.dark; + final statusBarIconBrightness = currentTheme.type == ThemeType.dark + ? Brightness.light + : Brightness.dark; + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarColor: statusBarColor, + statusBarBrightness: statusBarBrightness, + statusBarIconBrightness: statusBarIconBrightness)); + return Root( authenticationStore: authenticationStore, navigatorKey: navigatorKey, diff --git a/lib/monero/monero_balance.dart b/lib/monero/monero_balance.dart index c3cca86a7..6183e087d 100644 --- a/lib/monero/monero_balance.dart +++ b/lib/monero/monero_balance.dart @@ -1,20 +1,42 @@ +import 'package:cake_wallet/entities/balance.dart'; +import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:flutter/foundation.dart'; import 'package:cake_wallet/monero/monero_amount_format.dart'; -class MoneroBalance { +class MoneroBalance extends Balance { MoneroBalance({@required this.fullBalance, @required this.unlockedBalance}) : formattedFullBalance = moneroAmountToString(amount: fullBalance), formattedUnlockedBalance = - moneroAmountToString(amount: unlockedBalance); + moneroAmountToString(amount: unlockedBalance), + super(const [ + BalanceDisplayMode.availableBalance, + BalanceDisplayMode.fullBalance + ]); MoneroBalance.fromString( {@required this.formattedFullBalance, - @required this.formattedUnlockedBalance}) + @required this.formattedUnlockedBalance}) : fullBalance = moneroParseAmount(amount: formattedFullBalance), - unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance); + unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance), + super(const [ + BalanceDisplayMode.availableBalance, + BalanceDisplayMode.fullBalance + ]); final int fullBalance; final int unlockedBalance; final String formattedFullBalance; final String formattedUnlockedBalance; -} \ No newline at end of file + + @override + String formattedBalance(BalanceDisplayMode mode) { + switch (mode) { + case BalanceDisplayMode.fullBalance: + return formattedFullBalance; + case BalanceDisplayMode.availableBalance: + return formattedUnlockedBalance; + default: + return null; + } + } +} diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index c7e2cff44..048ca952e 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -43,7 +43,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { balance = MoneroBalance( fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), unlockedBalance: - monero_wallet.getUnlockedBalance(accountIndex: account.id)); + monero_wallet.getUnlockedBalance(accountIndex: account.id)); subaddressList.update(accountIndex: account.id); subaddress = subaddressList.subaddresses.first; address = subaddress.address; @@ -120,6 +120,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { } } + @override void close() { _listener?.stop(); _onAccountChangeReaction?.reaction?.dispose(); @@ -315,9 +316,8 @@ abstract class MoneroWalletBase extends WalletBase with Store { } } - Future _askForUpdateTransactionHistory() async { - await transactionHistory.update(); - } + Future _askForUpdateTransactionHistory() async => + await transactionHistory.update(); int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: account.id); @@ -326,13 +326,13 @@ abstract class MoneroWalletBase extends WalletBase with Store { monero_wallet.getUnlockedBalance(accountIndex: account.id); Future _afterSyncSave() async { - if (_isSavingAfterSync) { - return; - } - - _isSavingAfterSync = true; - try { + if (_isSavingAfterSync) { + return; + } + + _isSavingAfterSync = true; + final nowTimestamp = DateTime.now().millisecondsSinceEpoch; final sum = _lastAutosaveTimestamp + _autoAfterSyncSaveInterval; @@ -350,13 +350,13 @@ abstract class MoneroWalletBase extends WalletBase with Store { } Future _afterNewTransactionSave() async { - if (_isSavingAfterNewTransaction) { - return; - } - - _isSavingAfterNewTransaction = true; - try { + if (_isSavingAfterNewTransaction) { + return; + } + + _isSavingAfterNewTransaction = true; + await save(); } catch (e) { print(e.toString()); @@ -366,30 +366,38 @@ abstract class MoneroWalletBase extends WalletBase with Store { } void _onNewBlock(int height, int blocksLeft, double ptc) async { - if (walletInfo.isRecovery) { - await _askForUpdateTransactionHistory(); - _askForUpdateBalance(); - accountList.update(); - } - - if (blocksLeft < 100) { - await _askForUpdateTransactionHistory(); - _askForUpdateBalance(); - accountList.update(); - syncStatus = SyncedSyncStatus(); - await _afterSyncSave(); - + try { if (walletInfo.isRecovery) { - await setAsRecovered(); + await _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + accountList.update(); } - } else { - syncStatus = SyncingSyncStatus(blocksLeft, ptc); + + if (blocksLeft < 100) { + await _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + accountList.update(); + syncStatus = SyncedSyncStatus(); + await _afterSyncSave(); + + if (walletInfo.isRecovery) { + await setAsRecovered(); + } + } else { + syncStatus = SyncingSyncStatus(blocksLeft, ptc); + } + } catch (e) { + print(e.toString()); } } void _onNewTransaction() { - _askForUpdateTransactionHistory(); - _askForUpdateBalance(); - Timer(Duration(seconds: 1), () => _afterNewTransactionSave()); + try { + _askForUpdateTransactionHistory(); + _askForUpdateBalance(); + Timer(Duration(seconds: 1), () => _afterNewTransactionSave()); + } catch (e) { + print(e.toString()); + } } } diff --git a/lib/palette.dart b/lib/palette.dart index f9d0a1e3c..5f18ac62f 100644 --- a/lib/palette.dart +++ b/lib/palette.dart @@ -4,7 +4,7 @@ class Palette { static const Color green = Color.fromRGBO(39, 206, 80, 1.0); static const Color red = Color.fromRGBO(255, 51, 51, 1.0); static const Color darkRed = Color.fromRGBO(204, 38, 38, 1.0); - static const Color blueAlice = Color.fromRGBO(231, 240, 253, 1.0); + static const Color blueAlice = Color.fromRGBO(229, 247, 255, 1.0); static const Color lightBlue = Color.fromRGBO(172, 203, 238, 1.0); static const Color lavender = Color.fromRGBO(237, 245, 252, 1.0); static const Color oceanBlue = Color.fromRGBO(30, 52, 78, 1.0); @@ -13,7 +13,6 @@ class Palette { static const Color blue = Color.fromRGBO(88, 143, 252, 1.0); static const Color darkLavender = Color.fromRGBO(229, 238, 250, 1.0); static const Color nightBlue = Color.fromRGBO(46, 57, 96, 1.0); - static const Color moderateOrangeYellow = Color.fromRGBO(245, 134, 82, 1.0); static const Color moderateOrange = Color.fromRGBO(235, 117, 63, 1.0); static const Color shineGreen = Color.fromRGBO(76, 189, 87, 1.0); @@ -22,9 +21,8 @@ class Palette { static const Color royalBlue = Color.fromRGBO(43, 114, 221, 1.0); static const Color lightRed = Color.fromRGBO(227, 87, 87, 1.0); static const Color persianRed = Color.fromRGBO(206, 55, 55, 1.0); - - // NEW DESIGN static const Color blueCraiola = Color.fromRGBO(69, 110, 255, 1.0); + static const Color blueGreyCraiola = Color.fromRGBO(106, 177, 207, 1.0); static const Color darkBlueCraiola = Color.fromRGBO(53, 86, 136, 1.0); static const Color pinkFlamingo = Color.fromRGBO(240, 60, 243, 1.0); static const Color redHat = Color.fromRGBO(209, 68, 37, 1.0); @@ -43,26 +41,17 @@ class Palette { static const Color alizarinRed = Color.fromRGBO(233, 45, 45, 1.0); static const Color moderateSlateBlue = Color.fromRGBO(129, 93, 251, 1.0); static const Color brightOrange = Color.fromRGBO(255, 102, 0, 1.0); - - // FIXME: Rename. - static const Color eee = Color.fromRGBO(236, 239, 245, 1.0); - static const Color xxx = Color.fromRGBO(72, 89, 109, 1); + static const Color dullGray = Color.fromRGBO(98, 98, 98, 1.0); + static const Color protectiveBlue = Color.fromRGBO(33, 148, 255, 1.0); } class PaletteDark { - //static const Color distantBlue = Color.fromRGBO(70, 85, 133, 1.0); // mainBackgroundColor - static const Color lightDistantBlue = Color.fromRGBO(81, 96, 147, 1.0); // borderCardColor - static const Color gray = Color.fromRGBO(140, 153, 201, 1.0); // walletCardText - //static const Color violetBlue = Color.fromRGBO(51, 63, 104, 1.0); // walletCardAddressField - //static const Color moderateBlue = Color.fromRGBO(63, 77, 122, 1.0); // walletCardSubAddressField - //static const Color darkNightBlue = Color.fromRGBO(33, 43, 73, 1.0); // historyPanel - static const Color pigeonBlue = Color.fromRGBO(91, 112, 146, 1.0); // historyPanelText - static const Color moderateNightBlue = Color.fromRGBO(39, 53, 96, 1.0); // historyPanelButton - static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0); // menuHeader - //static const Color lightNightBlue = Color.fromRGBO(48, 59, 95, 1.0); // menuList - static const Color moderatePurpleBlue = Color.fromRGBO(57, 74, 95, 1.0); // selectButtonText - - // NEW DESIGN + static const Color lightDistantBlue = Color.fromRGBO(81, 96, 147, 1.0); + static const Color gray = Color.fromRGBO(140, 153, 201, 1.0); + static const Color pigeonBlue = Color.fromRGBO(91, 112, 146, 1.0); + static const Color moderateNightBlue = Color.fromRGBO(39, 53, 96, 1.0); + static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0); + static const Color moderatePurpleBlue = Color.fromRGBO(57, 74, 95, 1.0); static const Color backgroundColor = Color.fromRGBO(25, 35, 60, 1.0); static const Color nightBlue = Color.fromRGBO(35, 47, 79, 1.0); static const Color wildNightBlue = Color.fromRGBO(39, 53, 96, 1.0); @@ -94,8 +83,5 @@ class PaletteDark { static const Color deepVioletBlue = Color.fromRGBO(52, 66, 104, 1.0); static const Color lightPurpleBlue = Color.fromRGBO(120, 133, 170, 1.0); static const Color indicatorVioletBlue = Color.fromRGBO(59, 72, 119, 1.0); - - // FIXME: Rename. - static const Color eee = Color.fromRGBO(236, 239, 245, 1.0); - static const Color xxx = Color.fromRGBO(72, 89, 109, 1); + static const Color granite = Color.fromRGBO(48, 51, 60, 1.0); } \ No newline at end of file diff --git a/lib/reactions/fiat_rate_update.dart b/lib/reactions/fiat_rate_update.dart index 7d2f1a097..365423ef4 100644 --- a/lib/reactions/fiat_rate_update.dart +++ b/lib/reactions/fiat_rate_update.dart @@ -12,12 +12,13 @@ Future startFiatRateUpdate(AppStore appStore, SettingsStore settingsStore, return; } - fiatConversionStore.price = await FiatConversionService.fetchPrice( - appStore.wallet.currency, settingsStore.fiatCurrency); + fiatConversionStore.prices[appStore.wallet.currency] = + await FiatConversionService.fetchPrice( + appStore.wallet.currency, settingsStore.fiatCurrency); _timer = Timer.periodic( Duration(seconds: 30), - (_) async => fiatConversionStore.price = + (_) async => fiatConversionStore.prices[appStore.wallet.currency] = await FiatConversionService.fetchPrice( appStore.wallet.currency, settingsStore.fiatCurrency)); } diff --git a/lib/reactions/on_current_fiat_change.dart b/lib/reactions/on_current_fiat_change.dart index 28c6f621c..d7d9c2e82 100644 --- a/lib/reactions/on_current_fiat_change.dart +++ b/lib/reactions/on_current_fiat_change.dart @@ -7,12 +7,13 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; ReactionDisposer _onCurrentFiatCurrencyChangeDisposer; -void startCurrentFiatChangeReaction(AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { +void startCurrentFiatChangeReaction(AppStore appStore, + SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { _onCurrentFiatCurrencyChangeDisposer?.reaction?.dispose(); _onCurrentFiatCurrencyChangeDisposer = reaction( - (_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async { + (_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async { final cryptoCurrency = appStore.wallet.currency; - fiatConversionStore.price = await FiatConversionService.fetchPrice( - cryptoCurrency, fiatCurrency); + fiatConversionStore.prices[appStore.wallet.currency] = + await FiatConversionService.fetchPrice(cryptoCurrency, fiatCurrency); }); -} \ No newline at end of file +} diff --git a/lib/reactions/on_current_node_change.dart b/lib/reactions/on_current_node_change.dart index c03bbac21..2922e6ec7 100644 --- a/lib/reactions/on_current_node_change.dart +++ b/lib/reactions/on_current_node_change.dart @@ -6,10 +6,9 @@ ReactionDisposer _onCurrentNodeChangeReaction; void startOnCurrentNodeChangeReaction(AppStore appStore) { _onCurrentNodeChangeReaction?.reaction?.dispose(); - _onCurrentNodeChangeReaction = - reaction((_) => appStore.settingsStore.currentNode, (Node node) async { + appStore.settingsStore.nodes.observe((change) async { try { - await appStore.wallet.connectToNode(node: node); + await appStore.wallet.connectToNode(node: change.newValue); } catch (e) { print(e.toString()); } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index 6b9b447b6..117eafd33 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -12,6 +12,7 @@ import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; ReactionDisposer _onCurrentWalletChangeReaction; +ReactionDisposer _onCurrentWalletChangeFiatRateUpdateReaction; void startCurrentWalletChangeReaction(AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { @@ -29,8 +30,16 @@ void startCurrentWalletChangeReaction(AppStore appStore, await getIt.get().setInt( PreferencesKey.currentWalletType, serializeToInt(wallet.type)); await wallet.connectToNode(node: node); + } catch (e) { + print(e.toString()); + } + }); - fiatConversionStore.price = await FiatConversionService.fetchPrice( + _onCurrentWalletChangeFiatRateUpdateReaction = + reaction((_) => appStore.wallet, (WalletBase wallet) async { + try { + fiatConversionStore.prices[wallet.currency] = 0; + fiatConversionStore.prices[wallet.currency] = await FiatConversionService.fetchPrice( wallet.currency, settingsStore.fiatCurrency); } catch (e) { print(e.toString()); diff --git a/lib/router.dart b/lib/router.dart index ef38c97f8..d2122a031 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -62,32 +62,21 @@ Route createRoute(RouteSettings settings) { case Routes.newWalletFromWelcome: return CupertinoPageRoute( - builder: (_) => getIt.get(param1: - (PinCodeState context, dynamic _) async { - try { - context.changeProcessText( - S.current.creating_new_wallet); - final newWalletVM = - getIt.get(param1: WalletType.monero); - await newWalletVM.create( - options: 'English'); // FIXME: Unnamed constant - context.hideProgressText(); - await Navigator.of(context.context) - .pushNamed(Routes.preSeed); - } catch (e) { - context.changeProcessText('Error: ${e.toString()}'); - } - }), + builder: (_) => getIt.get( + param1: (PinCodeState context, dynamic _) => + Navigator.of(context.context) + .pushNamed(Routes.newWalletType)), fullscreenDialog: true); case Routes.newWalletType: return CupertinoPageRoute( - builder: (_) => NewWalletTypePage( - onTypeSelected: (context, type) => Navigator.of(context) - .pushNamed(Routes.newWallet, arguments: type))); + builder: (_) => getIt.get( + param1: (BuildContext context, WalletType _) => + Navigator.of(context).pushNamed(Routes.preSeed, arguments: true), + param2: true)); case Routes.newWallet: - final type = WalletType.monero; // settings.arguments as WalletType; + final type = settings.arguments as WalletType; final walletNewVM = getIt.get(param1: type); return CupertinoPageRoute( @@ -106,11 +95,11 @@ Route createRoute(RouteSettings settings) { case Routes.restoreWalletType: return CupertinoPageRoute( - builder: (_) => NewWalletTypePage( - onTypeSelected: (context, type) => Navigator.of(context) - .pushNamed(Routes.restoreWalletOptions, arguments: type), - isNewWallet: false, - )); + builder: (_) => getIt.get( + param1: (BuildContext context, WalletType type) => + Navigator.of(context) + .pushNamed(Routes.restoreWallet, arguments: type), + param2: false)); case Routes.restoreOptions: final type = settings.arguments as WalletType; @@ -148,7 +137,7 @@ Route createRoute(RouteSettings settings) { return CupertinoPageRoute( builder: (_) => getIt.get( param1: (PinCodeState context, dynamic _) => - Navigator.pushNamed(context.context, Routes.restoreWallet)), + Navigator.pushNamed(context.context, Routes.restoreWalletType)), fullscreenDialog: true); case Routes.seed: @@ -159,15 +148,10 @@ Route createRoute(RouteSettings settings) { case Routes.restoreWallet: return MaterialPageRoute( builder: (_) => - getIt.get(param1: WalletType.monero)); + getIt.get(param1: settings.arguments as WalletType)); case Routes.restoreWalletFromSeed: - // final args = settings.arguments as List; - final type = WalletType.monero; //args.first as WalletType; - // final language = type == WalletType.monero - // ? args[1] as String - // : LanguageList.english; - + final type = settings.arguments as WalletType; return CupertinoPageRoute( builder: (_) => RestoreWalletFromSeedPage(type: type)); diff --git a/lib/src/screens/base_page.dart b/lib/src/screens/base_page.dart index b34cb7310..77e0e92fe 100644 --- a/lib/src/screens/base_page.dart +++ b/lib/src/screens/base_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/palette.dart'; @@ -9,13 +10,13 @@ enum AppBarStyle { regular, withShadow, transparent } abstract class BasePage extends StatelessWidget { BasePage() - : _scaffoldKey = GlobalKey(), - _closeButtonImage = Image.asset('assets/images/close_button.png'), - _closeButtonImageDarkTheme = - Image.asset('assets/images/close_button_dark_theme.png'); + : _scaffoldKey = GlobalKey(); final GlobalKey _scaffoldKey; - final Image _closeButtonImage; - final Image _closeButtonImageDarkTheme; + + final Image closeButtonImage = + Image.asset('assets/images/close_button.png'); + final Image closeButtonImageDarkTheme = + Image.asset('assets/images/close_button_dark_theme.png'); String get title => null; @@ -37,7 +38,7 @@ abstract class BasePage extends StatelessWidget { Widget Function(BuildContext, Widget) get rootWrapper => null; - bool get isDarkTheme => getIt.get().isDarkTheme; + ThemeBase get currentTheme => getIt.get().currentTheme; void onOpenEndDrawer() => _scaffoldKey.currentState.openEndDrawer(); @@ -51,8 +52,8 @@ abstract class BasePage extends StatelessWidget { final _backButton = Icon(Icons.arrow_back_ios, color: titleColor ?? Theme.of(context).primaryTextTheme.title.color, size: 16,); - final _closeButton = - isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage; + final _closeButton = currentTheme.type == ThemeType.dark + ? closeButtonImageDarkTheme : closeButtonImage; return SizedBox( height: 37, @@ -88,8 +89,8 @@ abstract class BasePage extends StatelessWidget { Widget floatingActionButton(BuildContext context) => null; ObstructingPreferredSizeWidget appBar(BuildContext context) { - final appBarColor = - isDarkTheme ? backgroundDarkColor : backgroundLightColor; + final appBarColor = currentTheme.type == ThemeType.dark + ? backgroundDarkColor : backgroundLightColor; switch (appBarStyle) { case AppBarStyle.regular: @@ -131,10 +132,12 @@ abstract class BasePage extends StatelessWidget { @override Widget build(BuildContext context) { + final _backgroundColor = currentTheme.type == ThemeType.dark + ? backgroundDarkColor : backgroundLightColor; + final root = Scaffold( key: _scaffoldKey, - backgroundColor: - isDarkTheme ? backgroundDarkColor : backgroundLightColor, + backgroundColor: _backgroundColor, resizeToAvoidBottomPadding: resizeToAvoidBottomPadding, extendBodyBehindAppBar: extendBodyBehindAppBar, endDrawer: endDrawer, diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 0d7347d24..b88afced6 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -20,7 +21,8 @@ class DashboardPage extends BasePage { }); @override - Color get backgroundLightColor => Colors.transparent; + Color get backgroundLightColor => currentTheme.type == ThemeType.bright + ? Colors.transparent : Colors.white; @override Color get backgroundDarkColor => Colors.transparent; @@ -50,7 +52,8 @@ class DashboardPage extends BasePage { @override Widget trailing(BuildContext context) { final menuButton = - Image.asset('assets/images/menu.png', color: Colors.white); + Image.asset('assets/images/menu.png', + color: Theme.of(context).accentTextTheme.display3.backgroundColor); return Container( alignment: Alignment.centerRight, @@ -65,12 +68,6 @@ class DashboardPage extends BasePage { final DashboardViewModel walletViewModel; final WalletAddressListViewModel addressListViewModel; - final sendImage = Image.asset('assets/images/upload.png', - height: 22.24, width: 24, color: Colors.white); - final exchangeImage = Image.asset('assets/images/transfer.png', - height: 24.27, width: 22.25, color: Colors.white); - final receiveImage = Image.asset('assets/images/download.png', - height: 22.24, width: 24, color: Colors.white); final controller = PageController(initialPage: 1); var pages = []; @@ -78,6 +75,15 @@ class DashboardPage extends BasePage { @override Widget body(BuildContext context) { + final sendImage = Image.asset('assets/images/upload.png', + height: 22.24, width: 24, + color: Theme.of(context).accentTextTheme.display3.backgroundColor); + final exchangeImage = Image.asset('assets/images/transfer.png', + height: 24.27, width: 22.25, + color: Theme.of(context).accentTextTheme.display3.backgroundColor); + final receiveImage = Image.asset('assets/images/download.png', + height: 22.24, width: 24, + color: Theme.of(context).accentTextTheme.display3.backgroundColor); _setEffects(); return SafeArea( @@ -100,7 +106,8 @@ class DashboardPage extends BasePage { dotWidth: 6.0, dotHeight: 6.0, dotColor: Theme.of(context).indicatorColor, - activeDotColor: Colors.white), + activeDotColor: Theme.of(context).accentTextTheme.display1 + .backgroundColor), )), Container( padding: EdgeInsets.only(left: 45, right: 45, bottom: 24), diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index a4142d426..273cdd9bf 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -9,44 +9,53 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; // FIXME: terrible design class WalletMenu { - WalletMenu(this.context, this.reconnect); - - final List items = [ - WalletMenuItem( - title: S.current.reconnect, - image: Image.asset('assets/images/reconnect_menu.png', - height: 16, width: 16)), - WalletMenuItem( - title: S.current.rescan, - image: Image.asset('assets/images/filter_icon.png', - height: 16, width: 16)), - WalletMenuItem( - title: S.current.wallets, - image: Image.asset('assets/images/wallet_menu.png', - height: 16, width: 16)), - WalletMenuItem( - title: S.current.nodes, - image: - Image.asset('assets/images/nodes_menu.png', height: 16, width: 16)), - WalletMenuItem( - title: S.current.show_keys, - image: - Image.asset('assets/images/key_menu.png', height: 16, width: 16)), - WalletMenuItem( - title: S.current.address_book_menu, - image: Image.asset('assets/images/open_book_menu.png', - height: 16, width: 16)), - WalletMenuItem( - title: S.current.settings_title, - image: Image.asset('assets/images/settings_menu.png', - height: 16, width: 16)), - ]; + WalletMenu(this.context, this.reconnect, this.hasRescan) : items = [] { + items.addAll([ + WalletMenuItem( + title: S.current.reconnect, + image: Image.asset('assets/images/reconnect_menu.png', + height: 16, width: 16)), + if (hasRescan) + WalletMenuItem( + title: S.current.rescan, + image: Image.asset('assets/images/filter_icon.png', + height: 16, width: 16)), + WalletMenuItem( + title: S.current.wallets, + image: Image.asset('assets/images/wallet_menu.png', + height: 16, width: 16)), + WalletMenuItem( + title: S.current.nodes, + image: Image.asset('assets/images/nodes_menu.png', + height: 16, width: 16)), + WalletMenuItem( + title: S.current.show_keys, + image: + Image.asset('assets/images/key_menu.png', height: 16, width: 16)), + WalletMenuItem( + title: S.current.address_book_menu, + image: Image.asset('assets/images/open_book_menu.png', + height: 16, width: 16)), + WalletMenuItem( + title: S.current.settings_title, + image: Image.asset('assets/images/settings_menu.png', + height: 16, width: 16)), + ]); + } + final List items; final BuildContext context; final Future Function() reconnect; + final bool hasRescan; void action(int index) { - switch (index) { + var indx = index; + + if (index > 0 && !hasRescan) { + indx += 1; + } + + switch (indx) { case 0: _presentReconnectAlert(context); break; diff --git a/lib/src/screens/dashboard/widgets/action_button.dart b/lib/src/screens/dashboard/widgets/action_button.dart index 9f9fbc418..96635cbbb 100644 --- a/lib/src/screens/dashboard/widgets/action_button.dart +++ b/lib/src/screens/dashboard/widgets/action_button.dart @@ -39,7 +39,10 @@ class ActionButton extends StatelessWidget { SizedBox(height: 15), Text( title, - style: TextStyle(fontSize: 14, color: Colors.white), + style: TextStyle( + fontSize: 14, + color: Theme.of(context).accentTextTheme.display3 + .backgroundColor), ) ], ), diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index a31269a2b..c3fb5ae7c 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_v import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; class AddressPage extends StatelessWidget { AddressPage({@required this.addressListViewModel}); @@ -16,10 +17,9 @@ class AddressPage extends StatelessWidget { child: Column( children: [ Expanded( - child: Center( - child: QRWidget(addressListViewModel: addressListViewModel), - ) - ), + child: Center( + child: QRWidget(addressListViewModel: addressListViewModel), + )), GestureDetector( onTap: () => Navigator.of(context).pushNamed(Routes.receive), child: Container( @@ -27,29 +27,35 @@ class AddressPage extends StatelessWidget { padding: EdgeInsets.only(left: 24, right: 12), alignment: Alignment.center, decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(25)), - border: Border.all( - color: Theme.of(context).textTheme.subhead.color, - width: 1 - ), - color: Theme.of(context).buttonColor - ), + borderRadius: BorderRadius.all(Radius.circular(25)), + border: Border.all( + color: Theme.of(context).textTheme.subhead.color, + width: 1), + color: Theme.of(context).buttonColor), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - S.of(context).accounts_subaddresses, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Colors.white - ), - ), + Observer( + builder: (_) => Text( + addressListViewModel.hasAccounts + ? S.of(context).accounts_subaddresses + : S.of(context).addresses, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .accentTextTheme + .display3 + .backgroundColor), + )), Icon( Icons.arrow_forward_ios, size: 14, - color: Colors.white, + color: Theme.of(context) + .accentTextTheme + .display3 + .backgroundColor, ) ], ), @@ -59,4 +65,4 @@ class AddressPage extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/widgets/balance_page.dart index 5470c859d..7409a78ef 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/widgets/balance_page.dart @@ -10,59 +10,76 @@ class BalancePage extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(24), - child: GestureDetector( - onTapUp: (_) => dashboardViewModel.balanceViewModel.isReversing = false, - onTapDown: (_) => dashboardViewModel.balanceViewModel.isReversing = true, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Observer(builder: (_) { - return Text( - dashboardViewModel.balanceViewModel.currency.toString(), - style: TextStyle( - fontSize: 40, - fontWeight: FontWeight.bold, - color: Theme.of(context).indicatorColor, - height: 1), - ); - }), - Observer(builder: (_) { - return Text( - dashboardViewModel.balanceViewModel.displayMode.toString(), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Theme.of(context).indicatorColor, - height: 1), - ); - }), - SizedBox(height: 10), - Observer(builder: (_) { - return AutoSizeText(dashboardViewModel.balanceViewModel.cryptoBalance, + return GestureDetector( + onTapUp: (_) { + if (dashboardViewModel.balanceViewModel.canReverse) { + dashboardViewModel.balanceViewModel.isReversing = false; + } + }, + onTapDown: (_) { + if (dashboardViewModel.balanceViewModel.canReverse) { + dashboardViewModel.balanceViewModel.isReversing = true; + } + }, + child: Container( + color: Colors.transparent, + padding: EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Observer(builder: (_) { + return Text( + dashboardViewModel.balanceViewModel.currency.toString(), style: TextStyle( - fontSize: 54, + fontSize: 40, fontWeight: FontWeight.bold, - color: Colors.white, + color: Theme.of(context) + .accentTextTheme + .display2 + .backgroundColor, height: 1), - maxLines: 1, - textAlign: TextAlign.center); - }), - SizedBox(height: 10), - Observer(builder: (_) { - return Text(dashboardViewModel.balanceViewModel.fiatBalance, + ); + }), + Observer(builder: (_) { + return Text( + dashboardViewModel.balanceViewModel.displayMode.toString(), style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context).indicatorColor, + fontSize: 12, + fontWeight: FontWeight.w600, + color: Theme.of(context) + .accentTextTheme + .display2 + .backgroundColor, height: 1), - textAlign: TextAlign.center); - }), - ], - ), - ) - ); + ); + }), + SizedBox(height: 10), + Observer(builder: (_) { + return AutoSizeText( + dashboardViewModel.balanceViewModel.cryptoBalance, + style: TextStyle( + fontSize: 40, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .accentTextTheme + .display3 + .backgroundColor, + height: 1), + ); + }), + SizedBox(height: 10), + Observer(builder: (_) { + return Text(dashboardViewModel.balanceViewModel.fiatBalance, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context).indicatorColor, + height: 1), + textAlign: TextAlign.center); + }), + ], + ), + )); } } diff --git a/lib/src/screens/dashboard/widgets/header_row.dart b/lib/src/screens/dashboard/widgets/header_row.dart index 5b63f7a0e..2ba4cd8c9 100644 --- a/lib/src/screens/dashboard/widgets/header_row.dart +++ b/lib/src/screens/dashboard/widgets/header_row.dart @@ -27,14 +27,15 @@ class HeaderRow extends StatelessWidget { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, - color: Colors.white + color: Theme.of(context).accentTextTheme.display3.backgroundColor ), ), GestureDetector( onTap: () { showPopUp( context: context, - builder: (context) => FilterWidget(dashboardViewModel: dashboardViewModel) + builder: (context) => + FilterWidget(dashboardViewModel: dashboardViewModel) ); }, child: Container( diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index f8de5ba8d..ed90f9be1 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -66,8 +66,10 @@ class MenuWidgetState extends State { @override Widget build(BuildContext context) { - final walletMenu = - WalletMenu(context, () async => widget.dashboardViewModel.reconnect()); + final walletMenu = WalletMenu( + context, + () async => widget.dashboardViewModel.reconnect(), + widget.dashboardViewModel.hasRescan); final itemCount = walletMenu.items.length; moneroIcon = Image.asset('assets/images/monero_menu.png', @@ -148,16 +150,19 @@ class MenuWidgetState extends State { ), if (widget.dashboardViewModel.subname != null) - Observer(builder: (_) => Text( - widget.dashboardViewModel.subname, - style: TextStyle( - color: Theme.of(context) - .accentTextTheme - .overline - .decorationColor, - fontWeight: FontWeight.w500, - fontSize: 12), - )) + Observer( + builder: (_) => Text( + widget.dashboardViewModel + .subname, + style: TextStyle( + color: Theme.of(context) + .accentTextTheme + .overline + .decorationColor, + fontWeight: + FontWeight.w500, + fontSize: 12), + )) ], ), )) diff --git a/lib/src/screens/dashboard/widgets/trade_row.dart b/lib/src/screens/dashboard/widgets/trade_row.dart index 82bf2fc3e..4a4979699 100644 --- a/lib/src/screens/dashboard/widgets/trade_row.dart +++ b/lib/src/screens/dashboard/widgets/trade_row.dart @@ -48,14 +48,16 @@ class TradeRow extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Colors.white + color: Theme.of(context).accentTextTheme. + display3.backgroundColor )), formattedAmount != null ? Text(formattedAmount + ' ' + amountCrypto, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Colors.white + color: Theme.of(context).accentTextTheme. + display3.backgroundColor )) : Container() ]), diff --git a/lib/src/screens/dashboard/widgets/transaction_raw.dart b/lib/src/screens/dashboard/widgets/transaction_raw.dart index 67e13ce43..799a02ec6 100644 --- a/lib/src/screens/dashboard/widgets/transaction_raw.dart +++ b/lib/src/screens/dashboard/widgets/transaction_raw.dart @@ -60,12 +60,14 @@ class TransactionRow extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Colors.white)), + color: Theme.of(context).accentTextTheme. + display3.backgroundColor)), Text(formattedAmount, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Colors.white)) + color: Theme.of(context).accentTextTheme. + display3.backgroundColor)) ]), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/widgets/transactions_page.dart index 15b2eedaa..c8dd2bfa1 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/widgets/transactions_page.dart @@ -46,7 +46,8 @@ class TransactionsPage extends StatelessWidget { if (item is TransactionListItem) { final transaction = item.transaction; - return TransactionRow( + return Observer( + builder: (_) => TransactionRow( onTap: () => Navigator.of(context).pushNamed( Routes.transactionDetails, arguments: transaction), @@ -55,7 +56,7 @@ class TransactionsPage extends StatelessWidget { .format(transaction.date), formattedAmount: item.formattedCryptoAmount, formattedFiatAmount: item.formattedFiatAmount, - isPending: transaction.isPending); + isPending: transaction.isPending)); } if (item is TradeListItem) { diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index e3260c91a..e085d7880 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -95,9 +95,8 @@ class ExchangePage extends BasePage { return KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: isDarkTheme - ? Color.fromRGBO(48, 51, 60, 1.0) - : Color.fromRGBO(98, 98, 98, 1.0), + keyboardBarColor: Theme.of(context).accentTextTheme.body2 + .backgroundColor, nextFocus: false, actions: [ KeyboardActionsItem( diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index d74f7bcca..45f6f475c 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -166,8 +166,8 @@ class ExchangeCardState extends State { fontSize: 16, fontWeight: FontWeight.w600, color: Theme.of(context) - .textTheme - .subhead + .accentTextTheme + .display4 .decorationColor), validator: _isAmountEditable ? widget.currencyValueValidator @@ -211,8 +211,8 @@ class ExchangeCardState extends State { fontSize: 10, height: 1.2, color: Theme.of(context) - .textTheme - .subhead + .accentTextTheme + .display4 .decorationColor), ) : Offstage(), @@ -224,8 +224,8 @@ class ExchangeCardState extends State { fontSize: 10, height: 1.2, color: Theme.of(context) - .textTheme - .subhead + .accentTextTheme + .display4 .decorationColor)) : Offstage(), ]), @@ -239,7 +239,10 @@ class ExchangeCardState extends State { fontSize: 14, fontWeight: FontWeight.w500, color: - Theme.of(context).textTheme.subhead.decorationColor), + Theme.of(context) + .accentTextTheme + .display4 + .decorationColor), )) : Offstage(), _isAddressEditable @@ -263,7 +266,10 @@ class ExchangeCardState extends State { fontSize: 16, fontWeight: FontWeight.w600, color: - Theme.of(context).textTheme.subhead.decorationColor), + Theme.of(context) + .accentTextTheme + .display4 + .decorationColor), buttonColor: widget.addressButtonsColor, validator: widget.addressTextFieldValidator, ), diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index aced6304a..e43b8d8f1 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -227,16 +227,15 @@ class ExchangeTradeState extends State { final sendingState = widget.exchangeTradeViewModel.sendViewModel.state; - return trade.from == CryptoCurrency.xmr && !(sendingState is TransactionCommitted) + return widget.exchangeTradeViewModel.isSendable && + !(sendingState is TransactionCommitted) ? LoadingPrimaryButton( isDisabled: trade.inputAddress == null || trade.inputAddress.isEmpty, isLoading: sendingState is IsExecutingState, onPressed: () => widget.exchangeTradeViewModel.confirmSending(), - text: trade.provider == ExchangeProviderDescription.xmrto - ? S.of(context).confirm - : S.of(context).send_xmr, + text: S.of(context).confirm, color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white) : Offstage(); @@ -306,7 +305,11 @@ class ExchangeTradeState extends State { padding: EdgeInsets.only( top: 220, left: 24, right: 24), child: Text( - S.of(context).send_success, + S.of(context).send_success(widget + .exchangeTradeViewModel + .wallet + .currency + .toString()), textAlign: TextAlign.center, style: TextStyle( fontSize: 22, diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 33a20793e..e6e1a958d 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -1,6 +1,5 @@ -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -22,17 +21,24 @@ class NewWalletPage extends BasePage { final WalletNewVM _walletNewVM; + final walletNameImage = Image.asset('assets/images/wallet_name.png'); + final walletNameLightImage = + Image.asset('assets/images/wallet_name_light.png'); + @override String get title => S.current.new_wallet; @override - Widget body(BuildContext context) => WalletNameForm(_walletNewVM); + Widget body(BuildContext context) => WalletNameForm(_walletNewVM, + currentTheme.type == ThemeType.dark + ? walletNameImage : walletNameLightImage); } class WalletNameForm extends StatefulWidget { - WalletNameForm(this._walletNewVM); + WalletNameForm(this._walletNewVM, this.walletImage); final WalletNewVM _walletNewVM; + final Image walletImage; @override _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM); @@ -43,9 +49,6 @@ class _WalletNameFormState extends State { static const aspectRatioImage = 1.22; - final walletNameImage = Image.asset('assets/images/wallet_name.png'); - final walletNameLightImage = - Image.asset('assets/images/wallet_name_light.png'); final _formKey = GlobalKey(); final _languageSelectorKey = GlobalKey(); ReactionDisposer _stateReaction; @@ -78,10 +81,6 @@ class _WalletNameFormState extends State { @override Widget build(BuildContext context) { - final walletImage = getIt.get().isDarkTheme - ? walletNameImage - : walletNameLightImage; - return Container( padding: EdgeInsets.only(top: 24), child: ScrollableWithBottomSection( @@ -92,7 +91,7 @@ class _WalletNameFormState extends State { padding: EdgeInsets.only(left: 12, right: 12), child: AspectRatio( aspectRatio: aspectRatioImage, - child: FittedBox(child: walletImage, fit: BoxFit.fill)), + child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)), ), Padding( padding: EdgeInsets.only(top: 24), diff --git a/lib/src/screens/new_wallet/new_wallet_type_page.dart b/lib/src/screens/new_wallet/new_wallet_type_page.dart index cf3b3b896..76415f9dc 100644 --- a/lib/src/screens/new_wallet/new_wallet_type_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_type_page.dart @@ -1,6 +1,13 @@ +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cake_wallet/view_model/wallet_new_vm.dart'; +import 'package:flushbar/flushbar.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -10,25 +17,36 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; class NewWalletTypePage extends BasePage { - NewWalletTypePage({this.onTypeSelected, this.isNewWallet = true}); + NewWalletTypePage(this.walletNewVM, {this.onTypeSelected, this.isNewWallet}); final void Function(BuildContext, WalletType) onTypeSelected; final bool isNewWallet; + final WalletNewVM walletNewVM; + + final walletTypeImage = Image.asset('assets/images/wallet_type.png'); + final walletTypeLightImage = + Image.asset('assets/images/wallet_type_light.png'); @override - String get title => isNewWallet - ? S.current.new_wallet - : S.current.wallet_list_restore_wallet; + String get title => + isNewWallet ? S.current.new_wallet : S.current.wallet_list_restore_wallet; @override - Widget body(BuildContext context) => - WalletTypeForm(onTypeSelected: onTypeSelected); + Widget body(BuildContext context) => WalletTypeForm(walletNewVM, isNewWallet, + onTypeSelected: onTypeSelected, + walletImage: currentTheme.type == ThemeType.dark + ? walletTypeImage + : walletTypeLightImage); } class WalletTypeForm extends StatefulWidget { - WalletTypeForm({this.onTypeSelected}); + WalletTypeForm(this.walletNewVM, this.isNewWallet, + {this.onTypeSelected, this.walletImage}); final void Function(BuildContext, WalletType) onTypeSelected; + final WalletNewVM walletNewVM; + final bool isNewWallet; + final Image walletImage; @override WalletTypeFormState createState() => WalletTypeFormState(); @@ -42,10 +60,12 @@ class WalletTypeFormState extends State { final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); final walletTypeImage = Image.asset('assets/images/wallet_type.png'); - final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png'); + final walletTypeLightImage = + Image.asset('assets/images/wallet_type_light.png'); WalletType selected; List types; + Flushbar _progressBar; @override void initState() { @@ -55,11 +75,8 @@ class WalletTypeFormState extends State { @override Widget build(BuildContext context) { - final walletImage = getIt.get().isDarkTheme - ? walletTypeImage : walletTypeLightImage; - return Container( - padding: EdgeInsets.only(top: 24), + padding: EdgeInsets.only(top: 24, bottom: 24), child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), content: Column( @@ -69,7 +86,8 @@ class WalletTypeFormState extends State { padding: EdgeInsets.only(left: 12, right: 12), child: AspectRatio( aspectRatio: aspectRatioImage, - child: FittedBox(child: walletImage, fit: BoxFit.fill)), + child: + FittedBox(child: widget.walletImage, fit: BoxFit.fill)), ), Padding( padding: EdgeInsets.only(top: 48), @@ -86,7 +104,7 @@ class WalletTypeFormState extends State { padding: EdgeInsets.only(top: 24), child: SelectButton( image: _iconFor(type), - text: walletTypeToString(type), + text: walletTypeToDisplayName(type), isSelected: selected == type, onTap: () => setState(() => selected = type)), )) @@ -94,9 +112,9 @@ class WalletTypeFormState extends State { ), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSection: PrimaryButton( - onPressed: () => widget.onTypeSelected(context, selected), + onPressed: () => onTypeSelected(), text: S.of(context).seed_language_next, - color: Colors.green, + color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, isDisabled: selected == null, ), @@ -114,4 +132,35 @@ class WalletTypeFormState extends State { return null; } } + + Future onTypeSelected() async { + if (!widget.isNewWallet) { + widget.onTypeSelected(context, selected); + return; + } + + try { + _changeProcessText(S.of(context).creating_new_wallet); + widget.walletNewVM.type = selected; + await widget.walletNewVM + .create(options: 'English'); // FIXME: Unnamed constant + await _progressBar?.dismiss(); + final state = widget.walletNewVM.state; + + if (state is ExecutedSuccessfullyState) { + widget.onTypeSelected(context, selected); + } + + if (state is FailureState) { + _changeProcessText( + S.of(context).creating_new_wallet_error(state.error)); + } + } catch (e) { + _changeProcessText(S.of(context).creating_new_wallet_error(e.toString())); + } + } + + void _changeProcessText(String text) { + _progressBar = createBar(text, duration: null)..show(context); + } } diff --git a/lib/src/screens/new_wallet/widgets/select_button.dart b/lib/src/screens/new_wallet/widgets/select_button.dart index bd69a8015..42b99e40e 100644 --- a/lib/src/screens/new_wallet/widgets/select_button.dart +++ b/lib/src/screens/new_wallet/widgets/select_button.dart @@ -16,7 +16,7 @@ class SelectButton extends StatelessWidget { @override Widget build(BuildContext context) { final color = isSelected - ? Theme.of(context).accentTextTheme.subtitle.decorationColor + ? Colors.green : Theme.of(context).accentTextTheme.caption.color; final textColor = isSelected ? Theme.of(context).accentTextTheme.headline.decorationColor diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index 80697afad..87753e1ce 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -65,98 +65,90 @@ class NodeListPage extends BasePage { padding: EdgeInsets.only(top: 10), child: Observer( builder: (BuildContext context) { - return nodeListViewModel.nodes.isNotEmpty - ? SectionStandardList( - sectionCount: 2, - context: context, - itemCounter: (int sectionIndex) { - if (sectionIndex == 0) { - return 1; - } + return SectionStandardList( + sectionCount: 2, + context: context, + itemCounter: (int sectionIndex) { + if (sectionIndex == 0) { + return 1; + } - return nodeListViewModel.nodes.length; - }, - itemBuilder: (_, sectionIndex, index) { - if (sectionIndex == 0) { - return NodeHeaderListRow( - title: S.of(context).add_new_node, - onTap: (_) async => await Navigator.of(context) - .pushNamed(Routes.newNode)); - } + return nodeListViewModel.nodes.length; + }, + itemBuilder: (_, sectionIndex, index) { + if (sectionIndex == 0) { + return NodeHeaderListRow( + title: S.of(context).add_new_node, + onTap: (_) async => await Navigator.of(context) + .pushNamed(Routes.newNode)); + } - final node = nodeListViewModel.nodes[index]; - final isSelected = node.keyIndex == - nodeListViewModel.settingsStore.currentNode.keyIndex; - final nodeListRow = NodeListRow( - title: node.uri, - isSelected: isSelected, - isAlive: node.requestNode(), - onTap: (_) async { - if (isSelected) { - return; + final node = nodeListViewModel.nodes[index]; + final isSelected = + node.keyIndex == nodeListViewModel.currentNode?.keyIndex; + final nodeListRow = NodeListRow( + title: node.uri, + isSelected: isSelected, + isAlive: node.requestNode(), + onTap: (_) async { + if (isSelected) { + return; + } + + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: + S.of(context).change_current_node_title, + alertContent: + S.of(context).change_current_node(node.uri), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => + Navigator.of(context).pop(), + actionRightButton: () async { + await nodeListViewModel.setAsCurrent(node); + Navigator.of(context).pop(); + }); + }); + }); + + final dismissibleRow = Slidable( + key: Key('${node.keyIndex}'), + actionPane: SlidableDrawerActionPane(), + child: nodeListRow, + secondaryActions: [ + IconSlideAction( + caption: S.of(context).delete, + color: Colors.red, + icon: CupertinoIcons.delete, + onTap: () async { + final confirmed = await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).remove_node, + alertContent: + S.of(context).remove_node_message, + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, + actionRightButton: () => + Navigator.pop(context, true), + actionLeftButton: () => + Navigator.pop(context, false)); + }) ?? + false; + + if (confirmed) { + await nodeListViewModel.delete(node); } + }, + ), + ]); - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context) - .change_current_node_title, - alertContent: S - .of(context) - .change_current_node(node.uri), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).change, - actionLeftButton: () => - Navigator.of(context).pop(), - actionRightButton: () async { - await nodeListViewModel - .setAsCurrent(node); - Navigator.of(context).pop(); - }); - }); - }); - - final dismissibleRow = Slidable( - key: Key('${node.keyIndex}'), - actionPane: SlidableDrawerActionPane(), - child: nodeListRow, - secondaryActions: [ - IconSlideAction( - caption: S.of(context).delete, - color: Colors.red, - icon: CupertinoIcons.delete, - onTap: () async { - final confirmed = await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: - S.of(context).remove_node, - alertContent: S - .of(context) - .remove_node_message, - rightButtonText: - S.of(context).remove, - leftButtonText: - S.of(context).cancel, - actionRightButton: () => - Navigator.pop(context, true), - actionLeftButton: () => - Navigator.pop(context, false)); - }) ?? - false; - - if (confirmed) { - await nodeListViewModel.delete(node); - } - }, - ), - ]); - - return isSelected ? nodeListRow : dismissibleRow; - }) - : Container(); + return isSelected ? nodeListRow : dismissibleRow; + }); }, ), ); diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 5126a1527..c441bd380 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -27,16 +28,47 @@ class ReceivePage extends BasePage { String get title => S.current.receive; @override - Color get backgroundLightColor => Colors.transparent; + Color get backgroundLightColor => currentTheme.type == ThemeType.bright + ? Colors.transparent : Colors.white; @override Color get backgroundDarkColor => Colors.transparent; - @override - Color get titleColor => Colors.white; - final FocusNode _cryptoAmountFocus; + @override + Widget leading(BuildContext context) { + final _backButton = Icon(Icons.arrow_back_ios, + color: Theme.of(context).accentTextTheme.display3.backgroundColor, + size: 16,); + + return SizedBox( + height: 37, + width: 37, + child: ButtonTheme( + minWidth: double.minPositive, + child: FlatButton( + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + padding: EdgeInsets.all(0), + onPressed: () => onClose(context), + child: _backButton), + ), + ); + } + + @override + Widget middle(BuildContext context) { + return Text( + title, + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + fontFamily: 'Lato', + color: Theme.of(context).accentTextTheme.display3.backgroundColor), + ); + } + @override Widget Function(BuildContext, Widget) get rootWrapper => (BuildContext context, Widget scaffold) => Container( @@ -51,7 +83,8 @@ class ReceivePage extends BasePage { @override Widget trailing(BuildContext context) { final shareImage = - Image.asset('assets/images/share.png', color: Colors.white); + Image.asset('assets/images/share.png', + color: Theme.of(context).accentTextTheme.display3.backgroundColor); return SizedBox( height: 20.0, @@ -74,9 +107,8 @@ class ReceivePage extends BasePage { return KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: isDarkTheme - ? Color.fromRGBO(48, 51, 60, 1.0) - : Color.fromRGBO(98, 98, 98, 1.0), + keyboardBarColor: Theme.of(context).accentTextTheme.body2 + .backgroundColor, nextFocus: false, actions: [ KeyboardActionsItem( diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index e94351266..812255fa4 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -47,8 +47,8 @@ class QRWidget extends StatelessWidget { child: QrImage( data: addressListViewModel.uri.toString(), backgroundColor: Colors.transparent, - foregroundColor: Colors.white, - //Theme.of(context).textTheme.headline.color, + foregroundColor: Theme.of(context).accentTextTheme. + display3.backgroundColor, ))))), Spacer(flex: 3) ]), @@ -67,11 +67,12 @@ class QRWidget extends StatelessWidget { decimal: true), inputFormatters: [ BlacklistingTextInputFormatter( - RegExp('[\\-|\\ |\\,]')) + RegExp('[\\-|\\ ]')) ], textAlign: TextAlign.center, hintText: S.of(context).receive_amount, - textColor: Colors.white, + textColor: Theme.of(context).accentTextTheme. + display3.backgroundColor, borderColor: Theme.of(context) .textTheme .headline @@ -110,7 +111,8 @@ class QRWidget extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Colors.white), + color: Theme.of(context).accentTextTheme. + display3.backgroundColor), ), ), Padding( 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 6044531d9..e94f63bf0 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; @@ -7,12 +9,20 @@ 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, this.blockHeightFocusNode, - this.onHeightOrDateEntered}) + WalletRestoreFromSeedForm( + {Key key, + @required this.displayLanguageSelector, + @required this.displayBlockHeightSelector, + @required this.type, + this.blockHeightFocusNode, + this.onHeightOrDateEntered}) : super(key: key); + final WalletType type; + final bool displayLanguageSelector; + final bool displayBlockHeightSelector; final FocusNode blockHeightFocusNode; - final Function (bool) onHeightOrDateEntered; + final Function(bool) onHeightOrDateEntered; @override WalletRestoreFromSeedFormState createState() => @@ -41,32 +51,35 @@ class WalletRestoreFromSeedFormState extends State { return Container( padding: EdgeInsets.only(left: 25, right: 25), child: Column(children: [ - SeedWidget(key: seedWidgetStateKey, language: language), - GestureDetector( - onTap: () async { - final selected = await showPopUp( - context: context, - builder: (BuildContext context) => - SeedLanguagePicker(selected: language)); + SeedWidget( + key: seedWidgetStateKey, language: language, type: widget.type), + if (widget.displayLanguageSelector) + GestureDetector( + onTap: () async { + final selected = await showPopUp( + context: context, + builder: (BuildContext context) => + SeedLanguagePicker(selected: language)); - if (selected == null || selected.isEmpty) { - return; - } + if (selected == null || selected.isEmpty) { + return; + } - _changeLanguage(selected); - }, - child: Container( - color: Colors.transparent, - padding: EdgeInsets.only(top: 20.0), - child: IgnorePointer( - child: BaseTextFormField( - controller: languageController, - enableInteractiveSelection: false, - readOnly: true)))), - BlockchainHeightWidget( - focusNode: widget.blockHeightFocusNode, - key: blockchainHeightKey, - onHeightOrDateEntered: widget.onHeightOrDateEntered) + _changeLanguage(selected); + }, + child: Container( + color: Colors.transparent, + padding: EdgeInsets.only(top: 20.0), + child: IgnorePointer( + child: BaseTextFormField( + controller: languageController, + enableInteractiveSelection: false, + readOnly: true)))), + if (widget.displayBlockHeightSelector) + BlockchainHeightWidget( + focusNode: widget.blockHeightFocusNode, + key: blockchainHeightKey, + onHeightOrDateEntered: widget.onHeightOrDateEntered) ])); } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index 1a00639b6..adae48c89 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -25,16 +25,30 @@ class WalletRestorePage extends BasePage { _pages = [], _blockHeightFocusNode = FocusNode(), _controller = PageController(initialPage: 0) { - _pages.addAll([ - WalletRestoreFromSeedForm( - key: walletRestoreFromSeedFormKey, - blockHeightFocusNode: _blockHeightFocusNode, - onHeightOrDateEntered: (value) - => walletRestoreViewModel.isButtonEnabled = value), - WalletRestoreFromKeysFrom(key: walletRestoreFromKeysFormKey, - onHeightOrDateEntered: (value) - => walletRestoreViewModel.isButtonEnabled = value) - ]); + walletRestoreViewModel.availableModes.forEach((mode) { + switch (mode) { + case WalletRestoreMode.seed: + _pages.add(WalletRestoreFromSeedForm( + displayBlockHeightSelector: + walletRestoreViewModel.hasBlockchainHeightLanguageSelector, + displayLanguageSelector: + walletRestoreViewModel.hasSeedLanguageSelector, + type: walletRestoreViewModel.type, + key: walletRestoreFromSeedFormKey, + blockHeightFocusNode: _blockHeightFocusNode, + onHeightOrDateEntered: (value) => + walletRestoreViewModel.isButtonEnabled = value)); + break; + case WalletRestoreMode.keys: + _pages.add(WalletRestoreFromKeysFrom( + key: walletRestoreFromKeysFormKey, + onHeightOrDateEntered: (value) => + walletRestoreViewModel.isButtonEnabled = value)); + break; + default: + break; + } + }); } @override @@ -76,20 +90,19 @@ class WalletRestorePage extends BasePage { } }); - reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) - { - walletRestoreViewModel.isButtonEnabled = false; + reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) { + walletRestoreViewModel.isButtonEnabled = false; - walletRestoreFromSeedFormKey.currentState.blockchainHeightKey - .currentState.restoreHeightController.text = ''; - walletRestoreFromSeedFormKey.currentState.blockchainHeightKey - .currentState.dateController.text = ''; + walletRestoreFromSeedFormKey.currentState.blockchainHeightKey.currentState + .restoreHeightController.text = ''; + walletRestoreFromSeedFormKey.currentState.blockchainHeightKey.currentState + .dateController.text = ''; - walletRestoreFromKeysFormKey.currentState.blockchainHeightKey - .currentState.restoreHeightController.text = ''; - walletRestoreFromKeysFormKey.currentState.blockchainHeightKey - .currentState.dateController.text = ''; - }); + walletRestoreFromKeysFormKey.currentState.blockchainHeightKey.currentState + .restoreHeightController.text = ''; + walletRestoreFromKeysFormKey.currentState.blockchainHeightKey.currentState + .dateController.text = ''; + }); return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( @@ -100,40 +113,37 @@ class WalletRestorePage extends BasePage { }, controller: _controller, itemCount: _pages.length, - itemBuilder: (_, index) => SingleChildScrollView(child: _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), - )), + itemBuilder: (_, index) => + SingleChildScrollView(child: _pages[index]))), + if (_pages.length > 1) + 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: Observer( builder: (context) { return LoadingPrimaryButton( - onPressed: () => - walletRestoreViewModel.create(options: _credentials()), - text: S.of(context).restore_recover, - color: Theme - .of(context) - .accentTextTheme - .subtitle - .decorationColor, - textColor: Theme - .of(context) - .accentTextTheme - .headline - .decorationColor, - isLoading: walletRestoreViewModel.state is IsExecutingState, - isDisabled: !walletRestoreViewModel.isButtonEnabled,); + onPressed: () => + walletRestoreViewModel.create(options: _credentials()), + text: S.of(context).restore_recover, + color: + Theme.of(context).accentTextTheme.subtitle.decorationColor, + textColor: + Theme.of(context).accentTextTheme.headline.decorationColor, + isLoading: walletRestoreViewModel.state is IsExecutingState, + isDisabled: !walletRestoreViewModel.isButtonEnabled, + ); }, )) ]); @@ -145,8 +155,11 @@ class WalletRestorePage extends BasePage { if (walletRestoreViewModel.mode == WalletRestoreMode.seed) { credentials['seed'] = walletRestoreFromSeedFormKey .currentState.seedWidgetStateKey.currentState.text; - credentials['height'] = walletRestoreFromSeedFormKey - .currentState.blockchainHeightKey.currentState.height; + + if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) { + credentials['height'] = walletRestoreFromSeedFormKey + .currentState.blockchainHeightKey.currentState.height; + } } else { credentials['address'] = walletRestoreFromKeysFormKey.currentState.addressController.text; diff --git a/lib/src/screens/seed/pre_seed_page.dart b/lib/src/screens/seed/pre_seed_page.dart index b9ea48eae..0a2bc0161 100644 --- a/lib/src/screens/seed/pre_seed_page.dart +++ b/lib/src/screens/seed/pre_seed_page.dart @@ -1,6 +1,5 @@ -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -8,8 +7,8 @@ import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; class PreSeedPage extends BasePage { - static final imageLight = Image.asset('assets/images/pre_seed_light.png'); - static final imageDark = Image.asset('assets/images/pre_seed_dark.png'); + final imageLight = Image.asset('assets/images/pre_seed_light.png'); + final imageDark = Image.asset('assets/images/pre_seed_dark.png'); @override Widget leading(BuildContext context) => null; @@ -19,8 +18,7 @@ class PreSeedPage extends BasePage { @override Widget body(BuildContext context) { - final image = - getIt.get().isDarkTheme ? imageDark : imageLight; + final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight; return WillPopScope( onWillPop: () async => false, diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index 0e47ee676..5bdd07be6 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -1,7 +1,6 @@ -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/cupertino.dart'; @@ -17,8 +16,8 @@ import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; class WalletSeedPage extends BasePage { WalletSeedPage(this.walletSeedViewModel, {@required this.isNewWalletCreated}); - static final imageLight = Image.asset('assets/images/crypto_lock_light.png'); - static final imageDark = Image.asset('assets/images/crypto_lock.png'); + final imageLight = Image.asset('assets/images/crypto_lock_light.png'); + final imageDark = Image.asset('assets/images/crypto_lock.png'); @override String get title => S.current.seed_title; @@ -83,8 +82,7 @@ class WalletSeedPage extends BasePage { @override Widget body(BuildContext context) { - final image = - getIt.get().isDarkTheme ? imageDark : imageLight; + final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight; return WillPopScope(onWillPop: () async => false, child: Container( padding: EdgeInsets.all(24), diff --git a/lib/src/screens/seed_language/seed_language_page.dart b/lib/src/screens/seed_language/seed_language_page.dart index 701b5dccc..541c9b8c1 100644 --- a/lib/src/screens/seed_language/seed_language_page.dart +++ b/lib/src/screens/seed_language/seed_language_page.dart @@ -1,6 +1,5 @@ -import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/src/widgets/seed_language_selector.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -15,17 +14,26 @@ class SeedLanguage extends BasePage { final Function(BuildContext, String) onConfirm; + final walletNameImage = Image.asset('assets/images/wallet_name.png'); + final walletNameLightImage = + Image.asset('assets/images/wallet_name_light.png'); + @override String get title => S.current.wallet_list_restore_wallet; @override - Widget body(BuildContext context) => SeedLanguageForm(onConfirm: onConfirm); + Widget body(BuildContext context) => + SeedLanguageForm( + onConfirm: onConfirm, + walletImage: currentTheme.type == ThemeType.dark + ? walletNameImage : walletNameLightImage); } class SeedLanguageForm extends StatefulWidget { - SeedLanguageForm({this.onConfirm}); + SeedLanguageForm({this.onConfirm, this.walletImage}); final Function(BuildContext, String) onConfirm; + final Image walletImage; @override SeedLanguageFormState createState() => SeedLanguageFormState(); @@ -33,18 +41,10 @@ class SeedLanguageForm extends StatefulWidget { class SeedLanguageFormState extends State { static const aspectRatioImage = 1.22; - - final walletNameImage = Image.asset('assets/images/wallet_name.png'); - final walletNameLightImage = - Image.asset('assets/images/wallet_name_light.png'); final _languageSelectorKey = GlobalKey(); @override Widget build(BuildContext context) { - final walletImage = getIt.get().isDarkTheme - ? walletNameImage - : walletNameLightImage; - return Container( padding: EdgeInsets.only(top: 24), child: ScrollableWithBottomSection( @@ -55,7 +55,8 @@ class SeedLanguageFormState extends State { padding: EdgeInsets.only(left: 12, right: 12), child: AspectRatio( aspectRatio: aspectRatioImage, - child: FittedBox(child: walletImage, fit: BoxFit.fill)), + child: FittedBox(child: widget.walletImage, + fit: BoxFit.fill)), ), Padding( padding: EdgeInsets.only(top: 40), diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index cd4c5b06d..1a18b5ba6 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -83,9 +83,8 @@ class SendPage extends BasePage { return KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: isDarkTheme - ? Color.fromRGBO(48, 51, 60, 1.0) - : Color.fromRGBO(98, 98, 98, 1.0), + keyboardBarColor: Theme.of(context).accentTextTheme.body2 + .backgroundColor, nextFocus: false, actions: [ KeyboardActionsItem( @@ -133,7 +132,8 @@ class SendPage extends BasePage { if (uri != null) { address = uri.path; - amount = uri.queryParameters['tx_amount']; + amount = uri.queryParameters['tx_amount'] ?? + uri.queryParameters['amount']; } else { address = uri.toString(); } @@ -497,14 +497,8 @@ class SendPage extends BasePage { } }, text: S.of(context).send, - color: Theme.of(context) - .accentTextTheme - .subtitle - .decorationColor, - textColor: Theme.of(context) - .accentTextTheme - .headline - .decorationColor, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, isLoading: sendViewModel.state is IsExecutingState || sendViewModel.state is TransactionCommitting, isDisabled: @@ -634,7 +628,9 @@ class SendPage extends BasePage { padding: EdgeInsets.only( top: 220, left: 24, right: 24), child: Text( - S.of(context).send_success, + S.of(context).send_success( + sendViewModel.currency + .toString()), textAlign: TextAlign.center, style: TextStyle( fontSize: 22, @@ -748,7 +744,7 @@ class SendPage extends BasePage { } Future _setTransactionPriority(BuildContext context) async { - final items = TransactionPriority.all; + final items = TransactionPriority.forWalletType(sendViewModel.walletType); final selectedItem = items.indexOf(sendViewModel.transactionPriority); await showPopUp( diff --git a/lib/src/screens/send/send_template_page.dart b/lib/src/screens/send/send_template_page.dart index b53117309..6d3ac7132 100644 --- a/lib/src/screens/send/send_template_page.dart +++ b/lib/src/screens/send/send_template_page.dart @@ -49,9 +49,8 @@ class SendTemplatePage extends BasePage { return KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: isDarkTheme - ? Color.fromRGBO(48, 51, 60, 1.0) - : Color.fromRGBO(98, 98, 98, 1.0), + keyboardBarColor: Theme.of(context).accentTextTheme.body2 + .backgroundColor, nextFocus: false, actions: [ KeyboardActionsItem( @@ -113,7 +112,8 @@ class SendTemplatePage extends BasePage { if (uri != null) { address = uri.path; - amount = uri.queryParameters['tx_amount']; + amount = uri.queryParameters['tx_amount'] ?? + uri.queryParameters['amount']; } else { address = uri.toString(); } @@ -246,9 +246,8 @@ class SendTemplatePage extends BasePage { } }, text: S.of(context).save, - color: Theme.of(context).accentTextTheme.subtitle.decorationColor, - textColor: - Theme.of(context).accentTextTheme.headline.decorationColor, + color: Colors.green, + textColor: Colors.white, ), ), )); diff --git a/lib/src/screens/transaction_details/transaction_details_page.dart b/lib/src/screens/transaction_details/transaction_details_page.dart index 6745e05a7..3a7c7e591 100644 --- a/lib/src/screens/transaction_details/transaction_details_page.dart +++ b/lib/src/screens/transaction_details/transaction_details_page.dart @@ -13,7 +13,8 @@ import 'package:cake_wallet/utils/date_formatter.dart'; import 'package:hive/hive.dart'; class TransactionDetailsPage extends BasePage { - TransactionDetailsPage(this.transactionInfo, bool showRecipientAddress, Box transactionDescriptionBox) + TransactionDetailsPage(this.transactionInfo, bool showRecipientAddress, + Box transactionDescriptionBox) : _items = [] { final dateFormat = DateFormatter.withCurrentLocal(); final tx = transactionInfo; @@ -30,21 +31,9 @@ class TransactionDetailsPage extends BasePage { StandartListItem( title: S.current.transaction_details_amount, value: tx.amountFormatted()), - StandartListItem( - title: S.current.send_fee, - value: tx.feeFormatted()) + StandartListItem(title: S.current.send_fee, value: tx.feeFormatted()) ]; - if (showRecipientAddress) { - final recipientAddress = transactionDescriptionBox.values.firstWhere((val) => val.id == transactionInfo.id, orElse: () => null)?.recipientAddress; - - if (recipientAddress?.isNotEmpty ?? false) { - items.add(StandartListItem( - title: S.current.transaction_details_recipient_address, - value: recipientAddress)); - } - } - if (tx.key?.isNotEmpty ?? null) { // FIXME: add translation items.add(StandartListItem(title: 'Transaction Key', value: tx.key)); @@ -66,11 +55,25 @@ class TransactionDetailsPage extends BasePage { title: S.current.transaction_details_height, value: '${tx.height}'), StandartListItem( title: S.current.transaction_details_amount, - value: tx.amountFormatted()) + value: tx.amountFormatted()), + if (tx.feeFormatted()?.isNotEmpty) + StandartListItem(title: S.current.send_fee, value: tx.feeFormatted()) ]; _items.addAll(items); } + + if (showRecipientAddress) { + final recipientAddress = transactionDescriptionBox.values + .firstWhere((val) => val.id == transactionInfo.id, orElse: () => null) + ?.recipientAddress; + + if (recipientAddress?.isNotEmpty ?? false) { + _items.add(StandartListItem( + title: S.current.transaction_details_recipient_address, + value: recipientAddress)); + } + } } @override diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 1b15a8e0e..de0addd1d 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart'; -import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_menu_alert.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; @@ -15,7 +14,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; class WalletListPage extends BasePage { @@ -51,7 +49,7 @@ class WalletListBodyState extends State { final newWalletImage = Image.asset('assets/images/new_wallet.png', height: 12, width: 12, - color: Theme.of(context).accentTextTheme.headline.decorationColor); + color: Colors.white); final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', height: 12, width: 12, @@ -165,17 +163,16 @@ class WalletListBodyState extends State { ), bottomSection: Column(children: [ PrimaryImageButton( - onPressed: () => _generateNewWallet(), + onPressed: () => Navigator.of(context).pushNamed(Routes.newWalletType), image: newWalletImage, text: S.of(context).wallet_list_create_new_wallet, - color: Theme.of(context).accentTextTheme.subtitle.decorationColor, - textColor: - Theme.of(context).accentTextTheme.headline.decorationColor, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, ), SizedBox(height: 10.0), PrimaryImageButton( onPressed: () => - Navigator.of(context).pushNamed(Routes.restoreWallet), + Navigator.of(context).pushNamed(Routes.restoreWalletType), image: restoreWalletImage, text: S.of(context).wallet_list_restore_wallet, color: Theme.of(context).accentTextTheme.caption.color, diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index 7a5b65516..35934f64f 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -1,5 +1,4 @@ -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; @@ -23,9 +22,7 @@ class WelcomePage extends BasePage { @override Widget body(BuildContext context) { - final welcomeImage = getIt - .get() - .isDarkTheme + final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight; final newWalletImage = Image.asset('assets/images/new_wallet.png', diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 6074319e7..8c18d84a1 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -204,7 +204,7 @@ class AddressTextField extends StatelessWidget { onURIScanned(uri); } } catch (e) { - print('Error $e'); + print(e.toString()); } } diff --git a/lib/src/widgets/alert_with_one_action.dart b/lib/src/widgets/alert_with_one_action.dart index 5a39adbc5..5aedbe9da 100644 --- a/lib/src/widgets/alert_with_one_action.dart +++ b/lib/src/widgets/alert_with_one_action.dart @@ -31,7 +31,7 @@ class AlertWithOneAction extends BaseAlertDialog { width: 300, height: 52, padding: EdgeInsets.only(left: 12, right: 12), - color: Theme.of(context).accentTextTheme.body2.color, + color: Theme.of(context).accentTextTheme.body1.backgroundColor, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( @@ -44,7 +44,8 @@ class AlertWithOneAction extends BaseAlertDialog { style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: Colors.white, + color: Theme.of(context).primaryTextTheme.body1 + .backgroundColor, decoration: TextDecoration.none, ), )), diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index 069c2bcf9..19c00ca43 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -61,18 +61,24 @@ class BaseAlertDialog extends StatelessWidget { fontSize: 15, fontFamily: 'Lato', fontWeight: FontWeight.w600, - color: Colors.white, + color: Theme.of(context).primaryTextTheme.body2 + .backgroundColor, decoration: TextDecoration.none, ), )), ), ) ), + Container( + width: 1, + height: 52, + color: Theme.of(context).dividerColor, + ), Flexible( child: Container( height: 52, padding: EdgeInsets.only(left: 6, right: 6), - color: Theme.of(context).accentTextTheme.body2.color, + color: Theme.of(context).accentTextTheme.body1.backgroundColor, child: ButtonTheme( minWidth: double.infinity, child: FlatButton( @@ -86,7 +92,8 @@ class BaseAlertDialog extends StatelessWidget { fontSize: 15, fontFamily: 'Lato', fontWeight: FontWeight.w600, - color: Colors.white, + color: Theme.of(context).primaryTextTheme.body1 + .backgroundColor, decoration: TextDecoration.none, ), )), @@ -133,6 +140,10 @@ class BaseAlertDialog extends StatelessWidget { ], ), ), + Container( + height: 1, + color: Theme.of(context).dividerColor, + ), actionButtons(context) ], ), diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index ddebbffa0..b53dd8644 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -12,20 +12,21 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:flutter/widgets.dart'; class SeedWidget extends StatefulWidget { - SeedWidget({Key key, this.language}) : super(key: key); + SeedWidget({Key key, this.language, this.type}) : super(key: key); final String language; + final WalletType type; @override - SeedWidgetState createState() => SeedWidgetState(language); + SeedWidgetState createState() => SeedWidgetState(language, type); } class SeedWidgetState extends State { - SeedWidgetState(String language) + SeedWidgetState(String language, this.type) : controller = TextEditingController(), focusNode = FocusNode(), words = SeedValidator.getWordList( - type: WalletType.monero, language: language) { + type:type, language: language) { focusNode.addListener(() { setState(() { if (!focusNode.hasFocus && controller.text.isEmpty) { @@ -41,6 +42,7 @@ class SeedWidgetState extends State { final TextEditingController controller; final FocusNode focusNode; + final WalletType type; List words; bool _showPlaceholder; @@ -55,7 +57,7 @@ class SeedWidgetState extends State { void changeSeedLanguage(String language) { setState(() { words = SeedValidator.getWordList( - type: WalletType.monero, language: language); + type: type, language: language); }); } diff --git a/lib/src/widgets/trail_button.dart b/lib/src/widgets/trail_button.dart index a7c4caecb..8fa1bf5e5 100644 --- a/lib/src/widgets/trail_button.dart +++ b/lib/src/widgets/trail_button.dart @@ -21,7 +21,10 @@ class TrailButton extends StatelessWidget { child: Text( caption, style: TextStyle( - color: Theme.of(context).textTheme.subhead.decorationColor, + color: Theme.of(context) + .accentTextTheme + .display4 + .decorationColor, fontWeight: FontWeight.w500, fontSize: 14), ), diff --git a/lib/store/app_store.dart b/lib/store/app_store.dart index 7421fa54e..6c95ebafe 100644 --- a/lib/store/app_store.dart +++ b/lib/store/app_store.dart @@ -26,4 +26,10 @@ abstract class AppStoreBase with Store { SettingsStore settingsStore; NodeListStore nodeListStore; + + @action + void changeCurrentWallet(WalletBase wallet) { + this.wallet?.close(); + this.wallet = wallet; + } } diff --git a/lib/store/dashboard/fiat_conversion_store.dart b/lib/store/dashboard/fiat_conversion_store.dart index 48aed82b7..386ad6455 100644 --- a/lib/store/dashboard/fiat_conversion_store.dart +++ b/lib/store/dashboard/fiat_conversion_store.dart @@ -1,13 +1,13 @@ +import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:mobx/mobx.dart'; part 'fiat_conversion_store.g.dart'; -class FiatConversionStore = FiatConversionStoreBase - with _$FiatConversionStore; +class FiatConversionStore = FiatConversionStoreBase with _$FiatConversionStore; abstract class FiatConversionStoreBase with Store { - FiatConversionStoreBase() : price = 0.0; + FiatConversionStoreBase() : prices = ObservableMap(); @observable - double price; + ObservableMap prices; } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 321bbe23e..f4c80b883 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/entities/preferences_key.dart'; -import 'package:cake_wallet/themes.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/themes/theme_list.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -28,7 +29,7 @@ abstract class SettingsStoreBase with Store { @required BalanceDisplayMode initialBalanceDisplayMode, @required bool initialSaveRecipientAddress, @required bool initialAllowBiometricalAuthentication, - @required bool initialDarkTheme, + @required ThemeBase initialTheme, @required int initialPinLength, @required String initialLanguageCode, // @required String initialCurrentLocale, @@ -40,10 +41,9 @@ abstract class SettingsStoreBase with Store { balanceDisplayMode = initialBalanceDisplayMode; shouldSaveRecipientAddress = initialSaveRecipientAddress; allowBiometricalAuthentication = initialAllowBiometricalAuthentication; - isDarkTheme = initialDarkTheme; + currentTheme = initialTheme; pinCodeLength = initialPinLength; languageCode = initialLanguageCode; - currentNode = nodes[WalletType.monero]; this.nodes = ObservableMap.of(nodes); _sharedPreferences = sharedPreferences; @@ -65,9 +65,9 @@ abstract class SettingsStoreBase with Store { shouldSaveRecipientAddress)); reaction( - (_) => isDarkTheme, - (bool isDarkTheme) => sharedPreferences.setBool( - PreferencesKey.currentDarkTheme, isDarkTheme)); + (_) => currentTheme, + (ThemeBase theme) => sharedPreferences.setInt( + PreferencesKey.currentTheme, theme.raw)); reaction( (_) => allowBiometricalAuthentication, @@ -80,14 +80,15 @@ abstract class SettingsStoreBase with Store { (int pinLength) => sharedPreferences.setInt( PreferencesKey.currentPinLength, pinLength)); - reaction((_) => currentNode, - (Node node) => _saveCurrentNode(node, WalletType.monero)); - reaction( (_) => languageCode, (String languageCode) => sharedPreferences.setString( PreferencesKey.currentLanguageCode, languageCode)); + this + .nodes + .observe((change) => _saveCurrentNode(change.newValue, change.key)); + reaction((_) => balanceDisplayMode, (BalanceDisplayMode mode) => sharedPreferences.setInt( PreferencesKey.currentBalanceDisplayModeKey, @@ -116,16 +117,13 @@ abstract class SettingsStoreBase with Store { bool allowBiometricalAuthentication; @observable - bool isDarkTheme; + ThemeBase currentTheme; @observable int pinCodeLength; - @observable - Node currentNode; - @computed - ThemeData get theme => isDarkTheme ? Themes.darkTheme : Themes.lightTheme; + ThemeData get theme => currentTheme.themeData; @observable String languageCode; @@ -159,8 +157,8 @@ abstract class SettingsStoreBase with Store { final allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; - final savedDarkTheme = - sharedPreferences.getBool(PreferencesKey.currentDarkTheme) ?? false; + final savedTheme = ThemeList.deserialize( + raw: sharedPreferences.getInt(PreferencesKey.currentTheme) ?? 0); final actionListDisplayMode = ObservableList(); actionListDisplayMode.addAll(deserializeActionlistDisplayModes( sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ?? @@ -193,7 +191,7 @@ abstract class SettingsStoreBase with Store { initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, - initialDarkTheme: savedDarkTheme, + initialTheme: savedTheme, actionlistDisplayMode: actionListDisplayMode, initialPinLength: pinLength, initialLanguageCode: savedLanguageCode); diff --git a/lib/themes.dart b/lib/themes.dart deleted file mode 100644 index aa7af6cf2..000000000 --- a/lib/themes.dart +++ /dev/null @@ -1,353 +0,0 @@ -import 'package:flutter/material.dart'; -import 'palette.dart'; - -class Themes { - - static final ThemeData lightTheme = ThemeData( - fontFamily: 'Lato', - brightness: Brightness.light, - backgroundColor: Colors.white, - accentColor: Palette.blueCraiola, // first gradient color - scaffoldBackgroundColor: Palette.pinkFlamingo, // second gradient color - primaryColor: Palette.redHat, // third gradient color - buttonColor: Colors.white.withOpacity(0.2), // action buttons on dashboard page - indicatorColor: Colors.white.withOpacity(0.5), // page indicator - hoverColor: Colors.white, // amount hint text (receive page) - dividerColor: Palette.paleBlue, - hintColor: Palette.gray, - textTheme: TextTheme( - title: TextStyle( - color: Colors.white, // sync_indicator text - backgroundColor: Colors.white.withOpacity(0.2), // synced sync_indicator - decorationColor: Colors.white.withOpacity(0.15), // not synced sync_indicator - ), - caption: TextStyle( - color: Palette.shineOrange, // not synced light - decorationColor: Colors.white, // filter icon - ), - overline: TextStyle( - color: Colors.white.withOpacity(0.2), // filter button - backgroundColor: Colors.white.withOpacity(0.5), // date section row - decorationColor: Colors.white.withOpacity(0.2) // icons (transaction and trade rows) - ), - subhead: TextStyle( - color: Colors.white.withOpacity(0.2), // address button border - decorationColor: Colors.white.withOpacity(0.4), // copy button (qr widget) - ), - headline: TextStyle( - color: Colors.white, // qr code - decorationColor: Colors.white.withOpacity(0.5), // bottom border of amount (receive page) - ), - display1: TextStyle( - color: PaletteDark.lightBlueGrey, // icons color (receive page) - decorationColor: Palette.lavender, // icons background (receive page) - ), - display2: TextStyle( - color: Palette.darkBlueCraiola, // text color of tiles (receive page) - decorationColor: Colors.white // background of tiles (receive page) - ), - display3: TextStyle( - color: Colors.white, // text color of current tile (receive page), - //decorationColor: Palette.blueCraiola // background of current tile (receive page) - decorationColor: Palette.moderateSlateBlue // background of current tile (receive page) - ), - display4: TextStyle( - color: Palette.violetBlue, // text color of tiles (account list) - decorationColor: Colors.white // background of tiles (account list) - ), - subtitle: TextStyle( - color: Palette.moderateSlateBlue, // text color of current tile (account list) - decorationColor: Colors.white // background of current tile (account list) - ), - body1: TextStyle( - color: Palette.moderatePurpleBlue, // scrollbar thumb - decorationColor: Palette.periwinkleCraiola // scrollbar background - ), - body2: TextStyle( - color: Palette.moderateLavender, // menu header - decorationColor: Colors.white, // menu background - ) - ), - primaryTextTheme: TextTheme( - title: TextStyle( - color: Palette.darkBlueCraiola, // title color - backgroundColor: Palette.wildPeriwinkle // textfield underline - ), - caption: TextStyle( - color: PaletteDark.pigeonBlue, // secondary text - decorationColor: Palette.wildLavender // menu divider - ), - overline: TextStyle( - color: Palette.darkGray, // transaction/trade details titles - decorationColor: Colors.white.withOpacity(0.5), // placeholder - ), - subhead: TextStyle( - color: Palette.blueCraiola, // first gradient color (send page) - decorationColor: Palette.pinkFlamingo // second gradient color (send page) - ), - headline: TextStyle( - color: Colors.white.withOpacity(0.5), // text field border color (send page) - decorationColor: Colors.white.withOpacity(0.5), // text field hint color (send page) - ), - display1: TextStyle( - color: Colors.white.withOpacity(0.2), // text field button color (send page) - decorationColor: Colors.white // text field button icon color (send page) - ), - display2: TextStyle( - color: Colors.white.withOpacity(0.5), // estimated fee (send page) - decorationColor: Palette.shadowWhite // template dotted border (send page) - ), - display3: TextStyle( - color: Palette.darkBlueCraiola, // template new text (send page) - decorationColor: Palette.shadowWhite // template background color (send page) - ), - display4: TextStyle( - color: Palette.darkBlueCraiola, // template title (send page) - decorationColor: Palette.niagara // receive amount text (exchange page) - ), - subtitle: TextStyle( - color: Palette.blueCraiola, // first gradient color top panel (exchange page) - decorationColor: Palette.pinkFlamingo // second gradient color top panel (exchange page) - ), - body1: TextStyle( - color: Palette.blueCraiola.withOpacity(0.7), // first gradient color bottom panel (exchange page) - decorationColor: Palette.pinkFlamingo.withOpacity(0.7) // second gradient color bottom panel (exchange page) - ), - body2: TextStyle( - color: Colors.white.withOpacity(0.5), // text field border on top panel (exchange page) - decorationColor: Colors.white.withOpacity(0.5), // text field border on bottom panel (exchange page) - ) - ), - focusColor: Colors.white.withOpacity(0.2), // text field button (exchange page) - accentTextTheme: TextTheme( - title: TextStyle( - color: Colors.white, // picker background - backgroundColor: Palette.periwinkleCraiola, // picker divider - decorationColor: Colors.white // dialog background - ), - caption: TextStyle( - color: Palette.moderateLavender, // container (confirm exchange) - backgroundColor: Palette.moderateLavender, // button background (confirm exchange) - decorationColor: Palette.darkBlueCraiola, // text color (information page) - ), - subtitle: TextStyle( - color: Palette.darkBlueCraiola, // QR code (exchange trade page) - backgroundColor: Palette.wildPeriwinkle, // divider (exchange trade page) - //decorationColor: Palette.blueCraiola // crete new wallet button background (wallet list page) - decorationColor: Palette.moderateSlateBlue // crete new wallet button background (wallet list page) - ), - headline: TextStyle( - color: Palette.moderateLavender, // first gradient color of wallet action buttons (wallet list page) - backgroundColor: Palette.moderateLavender, // second gradient color of wallet action buttons (wallet list page) - decorationColor: Colors.white // restore wallet button text color (wallet list page) - ), - subhead: TextStyle( - color: Palette.darkGray, // titles color (filter widget) - backgroundColor: Palette.periwinkle, // divider color (filter widget) - decorationColor: Colors.white // checkbox background (filter widget) - ), - overline: TextStyle( - color: Palette.wildPeriwinkle, // checkbox bounds (filter widget) - decorationColor: Colors.white, // menu subname - ), - display1: TextStyle( - color: Palette.blueCraiola, // first gradient color (menu header) - decorationColor: Palette.pinkFlamingo // second gradient color(menu header) - ), - display2: TextStyle( - color: Palette.shadowWhite, // action button color (address text field) - decorationColor: Palette.darkGray // hint text (seed widget) - ), - display3: TextStyle( - color: Palette.darkGray, // hint text (new wallet page) - decorationColor: Palette.periwinkleCraiola // underline (new wallet page) - ), - display4: TextStyle( - color: Palette.darkGray, // switch background (settings page) - ), - body1: TextStyle( - color: Palette.darkGray, // indicators (PIN code) - decorationColor: Palette.darkGray // switch (PIN code) - ), - body2: TextStyle( - color: Palette.moderateSlateBlue, // primary buttons, alert right buttons - decorationColor: Palette.brightOrange // alert left button - ), - ), - cardColor: Palette.moderateSlateBlue // bottom button (action list) - ); - - - static final ThemeData darkTheme = ThemeData( - fontFamily: 'Lato', - brightness: Brightness.dark, - backgroundColor: PaletteDark.backgroundColor, - accentColor: PaletteDark.backgroundColor, // first gradient color - scaffoldBackgroundColor: PaletteDark.backgroundColor, // second gradient color - primaryColor: PaletteDark.backgroundColor, // third gradient color - buttonColor: PaletteDark.nightBlue, // action buttons on dashboard page - indicatorColor: PaletteDark.cyanBlue, // page indicator - hoverColor: PaletteDark.cyanBlue, // amount hint text (receive page) - dividerColor: PaletteDark.dividerColor, - hintColor: PaletteDark.pigeonBlue, // menu - textTheme: TextTheme( - title: TextStyle( - color: PaletteDark.wildBlue, // sync_indicator text - backgroundColor: PaletteDark.lightNightBlue, // synced sync_indicator - decorationColor: PaletteDark.oceanBlue // not synced sync_indicator - ), - caption: TextStyle( - color: PaletteDark.orangeYellow, // not synced light - decorationColor: PaletteDark.wildBlue, // filter icon - ), - overline: TextStyle( - color: PaletteDark.oceanBlue, // filter button - backgroundColor: PaletteDark.darkCyanBlue, // date section row - decorationColor: PaletteDark.wildNightBlue // icons (transaction and trade rows) - ), - subhead: TextStyle( - color: PaletteDark.nightBlue, // address button border - decorationColor: PaletteDark.lightBlueGrey, // copy button (qr widget) - ), - headline: TextStyle( - color: PaletteDark.lightBlueGrey, // qr code - decorationColor: PaletteDark.darkGrey, // bottom border of amount (receive page) - ), - display1: TextStyle( - color: Colors.white, // icons color (receive page) - decorationColor: PaletteDark.distantNightBlue, // icons background (receive page) - ), - display2: TextStyle( - color: Colors.white, // text color of tiles (receive page) - decorationColor: PaletteDark.nightBlue // background of tiles (receive page) - ), - display3: TextStyle( - color: Palette.blueCraiola, // text color of current tile (receive page) - decorationColor: PaletteDark.lightOceanBlue // background of current tile (receive page) - ), - display4: TextStyle( - color: Colors.white, // text color of tiles (account list) - decorationColor: PaletteDark.darkOceanBlue // background of tiles (account list) - ), - subtitle: TextStyle( - color: Palette.blueCraiola, // text color of current tile (account list) - decorationColor: PaletteDark.darkNightBlue // background of current tile (account list) - ), - body1: TextStyle( - color: PaletteDark.wildBlueGrey, // scrollbar thumb - decorationColor: PaletteDark.violetBlue // scrollbar background - ), - body2: TextStyle( - color: PaletteDark.deepPurpleBlue, // menu header - decorationColor: PaletteDark.deepPurpleBlue, // menu background - ) - ), - primaryTextTheme: TextTheme( - title: TextStyle( - color: Colors.white, // title color - backgroundColor: PaletteDark.darkOceanBlue // textfield underline - ), - caption: TextStyle( - color: PaletteDark.darkCyanBlue, // secondary text - decorationColor: PaletteDark.darkOceanBlue // menu divider - ), - overline: TextStyle( - color: PaletteDark.lightBlueGrey, // transaction/trade details titles - decorationColor: Colors.grey, // placeholder - ), - subhead: TextStyle( - color: PaletteDark.darkNightBlue, // first gradient color (send page) - decorationColor: PaletteDark.darkNightBlue // second gradient color (send page) - ), - headline: TextStyle( - color: PaletteDark.lightVioletBlue, // text field border color (send page) - decorationColor: PaletteDark.darkCyanBlue, // text field hint color (send page) - ), - display1: TextStyle( - color: PaletteDark.buttonNightBlue, // text field button color (send page) - decorationColor: PaletteDark.gray // text field button icon color (send page) - ), - display2: TextStyle( - color: Colors.white, // estimated fee (send page) - decorationColor: PaletteDark.darkCyanBlue // template dotted border (send page) - ), - display3: TextStyle( - color: PaletteDark.darkCyanBlue, // template new text (send page) - decorationColor: PaletteDark.darkVioletBlue // template background color (send page) - ), - display4: TextStyle( - color: PaletteDark.cyanBlue, // template title (send page) - decorationColor: PaletteDark.darkCyanBlue // receive amount text (exchange page) - ), - subtitle: TextStyle( - color: PaletteDark.wildVioletBlue, // first gradient color top panel (exchange page) - decorationColor: PaletteDark.wildVioletBlue // second gradient color top panel (exchange page) - ), - body1: TextStyle( - color: PaletteDark.darkNightBlue, // first gradient color bottom panel (exchange page) - decorationColor: PaletteDark.darkNightBlue // second gradient color bottom panel (exchange page) - ), - body2: TextStyle( - color: PaletteDark.blueGrey, // text field border on top panel (exchange page) - decorationColor: PaletteDark.moderateVioletBlue, // text field border on bottom panel (exchange page) - ) - ), - focusColor: PaletteDark.moderateBlue, // text field button (exchange page) - accentTextTheme: TextTheme( - title: TextStyle( - color: PaletteDark.nightBlue, // picker background - backgroundColor: PaletteDark.dividerColor, // picker divider - decorationColor: PaletteDark.darkNightBlue // dialog background - ), - caption: TextStyle( - color: PaletteDark.nightBlue, // container (confirm exchange) - backgroundColor: PaletteDark.deepVioletBlue, // button background (confirm exchange) - decorationColor: Palette.darkLavender, // text color (information page) - ), - subtitle: TextStyle( - //color: PaletteDark.lightBlueGrey, // QR code (exchange trade page) - color: Colors.white, // QR code (exchange trade page) - backgroundColor: PaletteDark.deepVioletBlue, // divider (exchange trade page) - decorationColor: Colors.white // crete new wallet button background (wallet list page) - ), - headline: TextStyle( - color: PaletteDark.distantBlue, // first gradient color of wallet action buttons (wallet list page) - backgroundColor: PaletteDark.distantNightBlue, // second gradient color of wallet action buttons (wallet list page) - decorationColor: Palette.darkBlueCraiola // restore wallet button text color (wallet list page) - ), - subhead: TextStyle( - color: Colors.white, // titles color (filter widget) - backgroundColor: PaletteDark.darkOceanBlue, // divider color (filter widget) - decorationColor: PaletteDark.wildVioletBlue.withOpacity(0.3) // checkbox background (filter widget) - ), - overline: TextStyle( - color: PaletteDark.wildVioletBlue, // checkbox bounds (filter widget) - decorationColor: PaletteDark.darkCyanBlue, // menu subname - ), - display1: TextStyle( - color: PaletteDark.deepPurpleBlue, // first gradient color (menu header) - decorationColor: PaletteDark.deepPurpleBlue // second gradient color(menu header) - ), - display2: TextStyle( - color: PaletteDark.nightBlue, // action button color (address text field) - decorationColor: PaletteDark.darkCyanBlue // hint text (seed widget) - ), - display3: TextStyle( - color: PaletteDark.cyanBlue, // hint text (new wallet page) - decorationColor: PaletteDark.darkGrey // underline (new wallet page) - ), - display4: TextStyle( - color: PaletteDark.deepVioletBlue, // switch background (settings page) - ), - body1: TextStyle( - color: PaletteDark.indicatorVioletBlue, // indicators (PIN code) - decorationColor: PaletteDark.lightPurpleBlue // switch (PIN code) - ), - body2: TextStyle( - color: Palette.blueCraiola, // primary buttons, alert right buttons - decorationColor: Palette.alizarinRed // alert left button - ), - ), - cardColor: PaletteDark.darkNightBlue // bottom button (action list) - ); -} \ No newline at end of file diff --git a/lib/themes/bright_theme.dart b/lib/themes/bright_theme.dart new file mode 100644 index 000000000..39c33f8f1 --- /dev/null +++ b/lib/themes/bright_theme.dart @@ -0,0 +1,197 @@ +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:flutter/material.dart'; + +class BrightTheme extends ThemeBase { + BrightTheme({@required int raw}) : super(raw: raw); + + @override + String get title => S.current.bright_theme; + + @override + ThemeType get type => ThemeType.bright; + + @override + ThemeData get themeData => ThemeData( + fontFamily: 'Lato', + brightness: Brightness.light, + backgroundColor: Colors.white, + accentColor: Palette.blueCraiola, // first gradient color + scaffoldBackgroundColor: Palette.pinkFlamingo, // second gradient color + primaryColor: Palette.redHat, // third gradient color + buttonColor: Colors.white.withOpacity(0.2), // action buttons on dashboard page + indicatorColor: Colors.white.withOpacity(0.5), // page indicator + hoverColor: Colors.white, // amount hint text (receive page) + dividerColor: Palette.paleBlue, + hintColor: Palette.gray, + textTheme: TextTheme( + title: TextStyle( + color: Colors.white, // sync_indicator text + backgroundColor: Colors.white.withOpacity(0.2), // synced sync_indicator + decorationColor: Colors.white.withOpacity(0.15), // not synced sync_indicator + ), + caption: TextStyle( + color: Palette.shineOrange, // not synced light + decorationColor: Colors.white, // filter icon + ), + overline: TextStyle( + color: Colors.white.withOpacity(0.2), // filter button + backgroundColor: Colors.white.withOpacity(0.5), // date section row + decorationColor: Colors.white.withOpacity(0.2) // icons (transaction and trade rows) + ), + subhead: TextStyle( + color: Colors.white.withOpacity(0.2), // address button border + decorationColor: Colors.white.withOpacity(0.4), // copy button (qr widget) + ), + headline: TextStyle( + color: Colors.white, // qr code + decorationColor: Colors.white.withOpacity(0.5), // bottom border of amount (receive page) + ), + display1: TextStyle( + color: PaletteDark.lightBlueGrey, // icons color (receive page) + decorationColor: Palette.lavender, // icons background (receive page) + ), + display2: TextStyle( + color: Palette.darkBlueCraiola, // text color of tiles (receive page) + decorationColor: Colors.white // background of tiles (receive page) + ), + display3: TextStyle( + color: Colors.white, // text color of current tile (receive page), + //decorationColor: Palette.blueCraiola // background of current tile (receive page) + decorationColor: Palette.moderateSlateBlue // background of current tile (receive page) + ), + display4: TextStyle( + color: Palette.violetBlue, // text color of tiles (account list) + decorationColor: Colors.white // background of tiles (account list) + ), + subtitle: TextStyle( + color: Palette.moderateSlateBlue, // text color of current tile (account list) + decorationColor: Colors.white // background of current tile (account list) + ), + body1: TextStyle( + color: Palette.moderatePurpleBlue, // scrollbar thumb + decorationColor: Palette.periwinkleCraiola // scrollbar background + ), + body2: TextStyle( + color: Palette.moderateLavender, // menu header + decorationColor: Colors.white, // menu background + ) + ), + primaryTextTheme: TextTheme( + title: TextStyle( + color: Palette.darkBlueCraiola, // title color + backgroundColor: Palette.wildPeriwinkle // textfield underline + ), + caption: TextStyle( + color: PaletteDark.pigeonBlue, // secondary text + decorationColor: Palette.wildLavender // menu divider + ), + overline: TextStyle( + color: Palette.darkGray, // transaction/trade details titles + decorationColor: Colors.white.withOpacity(0.5), // placeholder + ), + subhead: TextStyle( + color: Palette.blueCraiola, // first gradient color (send page) + decorationColor: Palette.pinkFlamingo // second gradient color (send page) + ), + headline: TextStyle( + color: Colors.white.withOpacity(0.5), // text field border color (send page) + decorationColor: Colors.white.withOpacity(0.5), // text field hint color (send page) + ), + display1: TextStyle( + color: Colors.white.withOpacity(0.2), // text field button color (send page) + decorationColor: Colors.white // text field button icon color (send page) + ), + display2: TextStyle( + color: Colors.white.withOpacity(0.5), // estimated fee (send page) + decorationColor: Palette.shadowWhite // template dotted border (send page) + ), + display3: TextStyle( + color: Palette.darkBlueCraiola, // template new text (send page) + decorationColor: Palette.shadowWhite // template background color (send page) + ), + display4: TextStyle( + color: Palette.darkBlueCraiola, // template title (send page) + decorationColor: Palette.niagara // receive amount text (exchange page) + ), + subtitle: TextStyle( + color: Palette.blueCraiola, // first gradient color top panel (exchange page) + decorationColor: Palette.pinkFlamingo // second gradient color top panel (exchange page) + ), + body1: TextStyle( + color: Palette.blueCraiola.withOpacity(0.7), // first gradient color bottom panel (exchange page) + decorationColor: Palette.pinkFlamingo.withOpacity(0.7), // second gradient color bottom panel (exchange page) + backgroundColor: Palette.moderateSlateBlue // alert right button text + ), + body2: TextStyle( + color: Colors.white.withOpacity(0.5), // text field border on top panel (exchange page) + decorationColor: Colors.white.withOpacity(0.5), // text field border on bottom panel (exchange page) + backgroundColor: Palette.brightOrange // alert left button text + ) + ), + focusColor: Colors.white.withOpacity(0.2), // text field button (exchange page) + accentTextTheme: TextTheme( + title: TextStyle( + color: Colors.white, // picker background + backgroundColor: Palette.periwinkleCraiola, // picker divider + decorationColor: Colors.white // dialog background + ), + caption: TextStyle( + color: Palette.moderateLavender, // container (confirm exchange) + backgroundColor: Palette.moderateLavender, // button background (confirm exchange) + decorationColor: Palette.darkBlueCraiola, // text color (information page) + ), + subtitle: TextStyle( + color: Palette.darkBlueCraiola, // QR code (exchange trade page) + backgroundColor: Palette.wildPeriwinkle, // divider (exchange trade page) + //decorationColor: Palette.blueCraiola // crete new wallet button background (wallet list page) + decorationColor: Palette.moderateSlateBlue // crete new wallet button background (wallet list page) + ), + headline: TextStyle( + color: Palette.moderateLavender, // first gradient color of wallet action buttons (wallet list page) + backgroundColor: Palette.moderateLavender, // second gradient color of wallet action buttons (wallet list page) + decorationColor: Colors.white // restore wallet button text color (wallet list page) + ), + subhead: TextStyle( + color: Palette.darkGray, // titles color (filter widget) + backgroundColor: Palette.periwinkle, // divider color (filter widget) + decorationColor: Colors.white // checkbox background (filter widget) + ), + overline: TextStyle( + color: Palette.wildPeriwinkle, // checkbox bounds (filter widget) + decorationColor: Colors.white, // menu subname + ), + display1: TextStyle( + color: Palette.blueCraiola, // first gradient color (menu header) + decorationColor: Palette.pinkFlamingo, // second gradient color(menu header) + backgroundColor: Colors.white // active dot color + ), + display2: TextStyle( + color: Palette.shadowWhite, // action button color (address text field) + decorationColor: Palette.darkGray, // hint text (seed widget) + backgroundColor: Colors.white.withOpacity(0.5) // text on balance page + ), + display3: TextStyle( + color: Palette.darkGray, // hint text (new wallet page) + decorationColor: Palette.periwinkleCraiola, // underline (new wallet page) + backgroundColor: Colors.white // menu, icons, balance (dashboard page) + ), + display4: TextStyle( + color: Palette.darkGray, // switch background (settings page) + decorationColor: Colors.white.withOpacity(0.4) // hint text (exchange page) + ), + body1: TextStyle( + color: Palette.darkGray, // indicators (PIN code) + decorationColor: Palette.darkGray, // switch (PIN code) + backgroundColor: Colors.white // alert right button + ), + body2: TextStyle( + color: Palette.moderateSlateBlue, // primary buttons + decorationColor: Colors.white, // alert left button, + backgroundColor: Palette.dullGray // keyboard bar color + ), + ), + cardColor: Palette.moderateSlateBlue // bottom button (action list) + ); +} \ No newline at end of file diff --git a/lib/themes/dark_theme.dart b/lib/themes/dark_theme.dart new file mode 100644 index 000000000..09fb5526f --- /dev/null +++ b/lib/themes/dark_theme.dart @@ -0,0 +1,196 @@ +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:flutter/material.dart'; + +class DarkTheme extends ThemeBase { + DarkTheme({@required int raw}) : super(raw: raw); + + @override + String get title => S.current.dark_theme; + + @override + ThemeType get type => ThemeType.dark; + + @override + ThemeData get themeData => ThemeData( + fontFamily: 'Lato', + brightness: Brightness.dark, + backgroundColor: PaletteDark.backgroundColor, + accentColor: PaletteDark.backgroundColor, // first gradient color + scaffoldBackgroundColor: PaletteDark.backgroundColor, // second gradient color + primaryColor: PaletteDark.backgroundColor, // third gradient color + buttonColor: PaletteDark.nightBlue, // action buttons on dashboard page + indicatorColor: PaletteDark.cyanBlue, // page indicator + hoverColor: PaletteDark.cyanBlue, // amount hint text (receive page) + dividerColor: PaletteDark.dividerColor, + hintColor: PaletteDark.pigeonBlue, // menu + textTheme: TextTheme( + title: TextStyle( + color: PaletteDark.wildBlue, // sync_indicator text + backgroundColor: PaletteDark.lightNightBlue, // synced sync_indicator + decorationColor: PaletteDark.oceanBlue // not synced sync_indicator + ), + caption: TextStyle( + color: PaletteDark.orangeYellow, // not synced light + decorationColor: PaletteDark.wildBlue, // filter icon + ), + overline: TextStyle( + color: PaletteDark.oceanBlue, // filter button + backgroundColor: PaletteDark.darkCyanBlue, // date section row + decorationColor: PaletteDark.wildNightBlue // icons (transaction and trade rows) + ), + subhead: TextStyle( + color: PaletteDark.nightBlue, // address button border + decorationColor: PaletteDark.lightBlueGrey, // copy button (qr widget) + ), + headline: TextStyle( + color: PaletteDark.lightBlueGrey, // qr code + decorationColor: PaletteDark.darkGrey, // bottom border of amount (receive page) + ), + display1: TextStyle( + color: Colors.white, // icons color (receive page) + decorationColor: PaletteDark.distantNightBlue, // icons background (receive page) + ), + display2: TextStyle( + color: Colors.white, // text color of tiles (receive page) + decorationColor: PaletteDark.nightBlue // background of tiles (receive page) + ), + display3: TextStyle( + color: Palette.blueCraiola, // text color of current tile (receive page) + decorationColor: PaletteDark.lightOceanBlue // background of current tile (receive page) + ), + display4: TextStyle( + color: Colors.white, // text color of tiles (account list) + decorationColor: PaletteDark.darkOceanBlue // background of tiles (account list) + ), + subtitle: TextStyle( + color: Palette.blueCraiola, // text color of current tile (account list) + decorationColor: PaletteDark.darkNightBlue // background of current tile (account list) + ), + body1: TextStyle( + color: PaletteDark.wildBlueGrey, // scrollbar thumb + decorationColor: PaletteDark.violetBlue // scrollbar background + ), + body2: TextStyle( + color: PaletteDark.deepPurpleBlue, // menu header + decorationColor: PaletteDark.deepPurpleBlue, // menu background + ) + ), + primaryTextTheme: TextTheme( + title: TextStyle( + color: Colors.white, // title color + backgroundColor: PaletteDark.darkOceanBlue // textfield underline + ), + caption: TextStyle( + color: PaletteDark.darkCyanBlue, // secondary text + decorationColor: PaletteDark.darkOceanBlue // menu divider + ), + overline: TextStyle( + color: PaletteDark.lightBlueGrey, // transaction/trade details titles + decorationColor: Colors.grey, // placeholder + ), + subhead: TextStyle( + color: PaletteDark.darkNightBlue, // first gradient color (send page) + decorationColor: PaletteDark.darkNightBlue // second gradient color (send page) + ), + headline: TextStyle( + color: PaletteDark.lightVioletBlue, // text field border color (send page) + decorationColor: PaletteDark.darkCyanBlue, // text field hint color (send page) + ), + display1: TextStyle( + color: PaletteDark.buttonNightBlue, // text field button color (send page) + decorationColor: PaletteDark.gray // text field button icon color (send page) + ), + display2: TextStyle( + color: Colors.white, // estimated fee (send page) + decorationColor: PaletteDark.darkCyanBlue // template dotted border (send page) + ), + display3: TextStyle( + color: PaletteDark.darkCyanBlue, // template new text (send page) + decorationColor: PaletteDark.darkVioletBlue // template background color (send page) + ), + display4: TextStyle( + color: PaletteDark.cyanBlue, // template title (send page) + decorationColor: PaletteDark.darkCyanBlue // receive amount text (exchange page) + ), + subtitle: TextStyle( + color: PaletteDark.wildVioletBlue, // first gradient color top panel (exchange page) + decorationColor: PaletteDark.wildVioletBlue // second gradient color top panel (exchange page) + ), + body1: TextStyle( + color: PaletteDark.darkNightBlue, // first gradient color bottom panel (exchange page) + decorationColor: PaletteDark.darkNightBlue, // second gradient color bottom panel (exchange page) + backgroundColor: Palette.blueCraiola // alert right button text + ), + body2: TextStyle( + color: PaletteDark.blueGrey, // text field border on top panel (exchange page) + decorationColor: PaletteDark.moderateVioletBlue, // text field border on bottom panel (exchange page) + backgroundColor: Palette.alizarinRed // alert left button text + ) + ), + focusColor: PaletteDark.moderateBlue, // text field button (exchange page) + accentTextTheme: TextTheme( + title: TextStyle( + color: PaletteDark.nightBlue, // picker background + backgroundColor: PaletteDark.dividerColor, // picker divider + decorationColor: PaletteDark.darkNightBlue // dialog background + ), + caption: TextStyle( + color: PaletteDark.nightBlue, // container (confirm exchange) + backgroundColor: PaletteDark.deepVioletBlue, // button background (confirm exchange) + decorationColor: Palette.darkLavender, // text color (information page) + ), + subtitle: TextStyle( + //color: PaletteDark.lightBlueGrey, // QR code (exchange trade page) + color: Colors.white, // QR code (exchange trade page) + backgroundColor: PaletteDark.deepVioletBlue, // divider (exchange trade page) + decorationColor: Colors.white // crete new wallet button background (wallet list page) + ), + headline: TextStyle( + color: PaletteDark.distantBlue, // first gradient color of wallet action buttons (wallet list page) + backgroundColor: PaletteDark.distantNightBlue, // second gradient color of wallet action buttons (wallet list page) + decorationColor: Palette.darkBlueCraiola // restore wallet button text color (wallet list page) + ), + subhead: TextStyle( + color: Colors.white, // titles color (filter widget) + backgroundColor: PaletteDark.darkOceanBlue, // divider color (filter widget) + decorationColor: PaletteDark.wildVioletBlue.withOpacity(0.3) // checkbox background (filter widget) + ), + overline: TextStyle( + color: PaletteDark.wildVioletBlue, // checkbox bounds (filter widget) + decorationColor: PaletteDark.darkCyanBlue, // menu subname + ), + display1: TextStyle( + color: PaletteDark.deepPurpleBlue, // first gradient color (menu header) + decorationColor: PaletteDark.deepPurpleBlue, // second gradient color(menu header) + backgroundColor: Colors.white // active dot color + ), + display2: TextStyle( + color: PaletteDark.nightBlue, // action button color (address text field) + decorationColor: PaletteDark.darkCyanBlue, // hint text (seed widget) + backgroundColor: PaletteDark.cyanBlue // text on balance page + ), + display3: TextStyle( + color: PaletteDark.cyanBlue, // hint text (new wallet page) + decorationColor: PaletteDark.darkGrey, // underline (new wallet page) + backgroundColor: Colors.white // menu, icons, balance (dashboard page) + ), + display4: TextStyle( + color: PaletteDark.deepVioletBlue, // switch background (settings page) + decorationColor: PaletteDark.lightBlueGrey // hint text (exchange page) + ), + body1: TextStyle( + color: PaletteDark.indicatorVioletBlue, // indicators (PIN code) + decorationColor: PaletteDark.lightPurpleBlue, // switch (PIN code) + backgroundColor: PaletteDark.darkNightBlue // alert right button + ), + body2: TextStyle( + color: Palette.blueCraiola, // primary buttons + decorationColor: PaletteDark.darkNightBlue, // alert left button + backgroundColor: PaletteDark.granite // keyboard bar color + ), + ), + cardColor: PaletteDark.darkNightBlue // bottom button (action list) + ); +} \ No newline at end of file diff --git a/lib/themes/light_theme.dart b/lib/themes/light_theme.dart new file mode 100644 index 000000000..7d1486a91 --- /dev/null +++ b/lib/themes/light_theme.dart @@ -0,0 +1,196 @@ +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:flutter/material.dart'; + +class LightTheme extends ThemeBase { + LightTheme({@required int raw}) : super(raw: raw); + + @override + String get title => S.current.light_theme; + + @override + ThemeType get type => ThemeType.light; + + @override + ThemeData get themeData => ThemeData( + fontFamily: 'Lato', + brightness: Brightness.light, + backgroundColor: Colors.white, + accentColor: Colors.white, // first gradient color + scaffoldBackgroundColor: Colors.white, // second gradient color + primaryColor: Colors.white, // third gradient color + buttonColor: Palette.blueAlice, // action buttons on dashboard page + indicatorColor: PaletteDark.darkCyanBlue.withOpacity(0.67), // page indicator + hoverColor: Palette.darkBlueCraiola, // amount hint text (receive page) + dividerColor: Palette.paleBlue, + hintColor: Palette.gray, + textTheme: TextTheme( + title: TextStyle( + color: Palette.darkBlueCraiola, // sync_indicator text + backgroundColor: Palette.blueAlice, // synced sync_indicator + decorationColor: Palette.blueAlice.withOpacity(0.75), // not synced sync_indicator + ), + caption: TextStyle( + color: Palette.shineOrange, // not synced light + decorationColor: PaletteDark.wildBlue, // filter icon + ), + overline: TextStyle( + color: Palette.blueAlice, // filter button + backgroundColor: PaletteDark.darkCyanBlue, // date section row + decorationColor: Palette.blueAlice // icons (transaction and trade rows) + ), + subhead: TextStyle( + color: Palette.blueAlice, // address button border + decorationColor: PaletteDark.lightBlueGrey, // copy button (qr widget) + ), + headline: TextStyle( + color: Colors.white, // qr code + decorationColor: Palette.darkBlueCraiola, // bottom border of amount (receive page) + ), + display1: TextStyle( + color: PaletteDark.lightBlueGrey, // icons color (receive page) + decorationColor: Palette.moderateLavender, // icons background (receive page) + ), + display2: TextStyle( + color: Palette.darkBlueCraiola, // text color of tiles (receive page) + decorationColor: Palette.blueAlice // background of tiles (receive page) + ), + display3: TextStyle( + color: Colors.white, // text color of current tile (receive page), + //decorationColor: Palette.blueCraiola // background of current tile (receive page) + decorationColor: Palette.blueCraiola // background of current tile (receive page) + ), + display4: TextStyle( + color: Palette.violetBlue, // text color of tiles (account list) + decorationColor: Colors.white // background of tiles (account list) + ), + subtitle: TextStyle( + color: Palette.protectiveBlue, // text color of current tile (account list) + decorationColor: Colors.white // background of current tile (account list) + ), + body1: TextStyle( + color: Palette.moderatePurpleBlue, // scrollbar thumb + decorationColor: Palette.periwinkleCraiola // scrollbar background + ), + body2: TextStyle( + color: Palette.moderateLavender, // menu header + decorationColor: Colors.white, // menu background + ) + ), + primaryTextTheme: TextTheme( + title: TextStyle( + color: Palette.darkBlueCraiola, // title color + backgroundColor: Palette.wildPeriwinkle // textfield underline + ), + caption: TextStyle( + color: PaletteDark.pigeonBlue, // secondary text + decorationColor: Palette.wildLavender // menu divider + ), + overline: TextStyle( + color: Palette.darkGray, // transaction/trade details titles + decorationColor: PaletteDark.darkCyanBlue, // placeholder + ), + subhead: TextStyle( + color: Palette.blueCraiola, // first gradient color (send page) + decorationColor: Palette.blueGreyCraiola // second gradient color (send page) + ), + headline: TextStyle( + color: Colors.white.withOpacity(0.5), // text field border color (send page) + decorationColor: Colors.white.withOpacity(0.5), // text field hint color (send page) + ), + display1: TextStyle( + color: Colors.white.withOpacity(0.2), // text field button color (send page) + decorationColor: Colors.white // text field button icon color (send page) + ), + display2: TextStyle( + color: Colors.white.withOpacity(0.5), // estimated fee (send page) + decorationColor: Palette.moderateLavender // template dotted border (send page) + ), + display3: TextStyle( + color: Palette.darkBlueCraiola, // template new text (send page) + decorationColor: Palette.blueAlice // template background color (send page) + ), + display4: TextStyle( + color: Palette.darkBlueCraiola, // template title (send page) + decorationColor: Palette.niagara // receive amount text (exchange page) + ), + subtitle: TextStyle( + color: Palette.blueCraiola, // first gradient color top panel (exchange page) + decorationColor: Palette.blueGreyCraiola // second gradient color top panel (exchange page) + ), + body1: TextStyle( + color: Palette.blueCraiola.withOpacity(0.7), // first gradient color bottom panel (exchange page) + decorationColor: Palette.blueGreyCraiola.withOpacity(0.7), // second gradient color bottom panel (exchange page) + backgroundColor: Palette.protectiveBlue // alert right button text + ), + body2: TextStyle( + color: Colors.white.withOpacity(0.5), // text field border on top panel (exchange page) + decorationColor: Colors.white.withOpacity(0.5), // text field border on bottom panel (exchange page) + backgroundColor: Palette.brightOrange // alert left button text + ) + ), + focusColor: Colors.white.withOpacity(0.2), // text field button (exchange page) + accentTextTheme: TextTheme( + title: TextStyle( + color: Colors.white, // picker background + backgroundColor: Palette.periwinkleCraiola, // picker divider + decorationColor: Colors.white // dialog background + ), + caption: TextStyle( + color: Palette.blueAlice, // container (confirm exchange) + backgroundColor: Palette.blueAlice, // button background (confirm exchange) + decorationColor: Palette.darkBlueCraiola, // text color (information page) + ), + subtitle: TextStyle( + color: Palette.darkBlueCraiola, // QR code (exchange trade page) + backgroundColor: Palette.wildPeriwinkle, // divider (exchange trade page) + decorationColor: Palette.protectiveBlue // crete new wallet button background (wallet list page) + ), + headline: TextStyle( + color: Palette.moderateLavender, // first gradient color of wallet action buttons (wallet list page) + backgroundColor: Palette.moderateLavender, // second gradient color of wallet action buttons (wallet list page) + decorationColor: Colors.white // restore wallet button text color (wallet list page) + ), + subhead: TextStyle( + color: Palette.darkGray, // titles color (filter widget) + backgroundColor: Palette.periwinkle, // divider color (filter widget) + decorationColor: Colors.white // checkbox background (filter widget) + ), + overline: TextStyle( + color: Palette.wildPeriwinkle, // checkbox bounds (filter widget) + decorationColor: Colors.white, // menu subname + ), + display1: TextStyle( + color: Palette.blueCraiola, // first gradient color (menu header) + decorationColor: Palette.blueGreyCraiola, // second gradient color(menu header) + backgroundColor: PaletteDark.darkNightBlue // active dot color + ), + display2: TextStyle( + color: Palette.shadowWhite, // action button color (address text field) + decorationColor: Palette.darkGray, // hint text (seed widget) + backgroundColor: Palette.darkBlueCraiola.withOpacity(0.67) // text on balance page + ), + display3: TextStyle( + color: Palette.darkGray, // hint text (new wallet page) + decorationColor: Palette.periwinkleCraiola, // underline (new wallet page) + backgroundColor: Palette.darkBlueCraiola // menu, icons, balance (dashboard page) + ), + display4: TextStyle( + color: Palette.darkGray, // switch background (settings page) + decorationColor: Colors.white.withOpacity(0.4) // hint text (exchange page) + ), + body1: TextStyle( + color: Palette.darkGray, // indicators (PIN code) + decorationColor: Palette.darkGray, // switch (PIN code) + backgroundColor: Colors.white // alert right button + ), + body2: TextStyle( + color: Palette.protectiveBlue, // primary buttons + decorationColor: Colors.white, // alert left button, + backgroundColor: Palette.dullGray // keyboard bar color + ), + ), + cardColor: Palette.protectiveBlue // bottom button (action list) + ); +} \ No newline at end of file diff --git a/lib/themes/theme_base.dart b/lib/themes/theme_base.dart new file mode 100644 index 000000000..bc7baf70c --- /dev/null +++ b/lib/themes/theme_base.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +enum ThemeType {light, bright, dark} + +abstract class ThemeBase { + ThemeBase({@required this.raw}); + + final int raw; + String get title; + ThemeData get themeData; + ThemeType get type; + + @override + String toString() { + return title; + } +} \ No newline at end of file diff --git a/lib/themes/theme_list.dart b/lib/themes/theme_list.dart new file mode 100644 index 000000000..b4cea1391 --- /dev/null +++ b/lib/themes/theme_list.dart @@ -0,0 +1,25 @@ +import 'package:cake_wallet/themes/bright_theme.dart'; +import 'package:cake_wallet/themes/dark_theme.dart'; +import 'package:cake_wallet/themes/light_theme.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; + +class ThemeList { + static final all = [lightTheme, brightTheme, darkTheme]; + + static final lightTheme = LightTheme(raw: 0); + static final brightTheme = BrightTheme(raw: 1); + static final darkTheme = DarkTheme(raw: 2); + + static ThemeBase deserialize({int raw}) { + switch (raw) { + case 0: + return lightTheme; + case 1: + return brightTheme; + case 2: + return darkTheme; + default: + return null; + } + } +} \ No newline at end of file diff --git a/lib/utils/mobx.dart b/lib/utils/mobx.dart index 7ae97d56f..a5bfb2220 100644 --- a/lib/utils/mobx.dart +++ b/lib/utils/mobx.dart @@ -57,7 +57,8 @@ extension MobxBindable on Box { Filter filter, }) { if (initialFire) { - dest.addAll(values); + final res = filter != null ? values.where(filter) : values; + dest.addAll(res); } return watch().listen((event) { diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index ace6a470b..66d719771 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -17,11 +17,11 @@ part 'balance_view_model.g.dart'; class BalanceViewModel = BalanceViewModelBase with _$BalanceViewModel; abstract class BalanceViewModelBase with Store { - BalanceViewModelBase({ - @required this.appStore, - @required this.settingsStore, - @required this.fiatConvertationStore - }) { + BalanceViewModelBase( + {@required this.appStore, + @required this.settingsStore, + @required this.fiatConvertationStore}){ + isReversing = false; wallet ??= appStore.wallet; @@ -42,6 +42,9 @@ abstract class BalanceViewModelBase with Store { final SettingsStore settingsStore; final FiatConversionStore fiatConvertationStore; + bool get canReverse => + (appStore.wallet.balance.availableModes as List).length > 1; + @observable bool isReversing; @@ -51,18 +54,18 @@ abstract class BalanceViewModelBase with Store { @observable WalletBase wallet; + @computed + double get price => fiatConvertationStore.prices[appStore.wallet.currency]; + @computed BalanceDisplayMode get savedDisplayMode => settingsStore.balanceDisplayMode; @computed BalanceDisplayMode get displayMode => isReversing - ? (savedDisplayMode == BalanceDisplayMode.availableBalance - ? BalanceDisplayMode.fullBalance - : BalanceDisplayMode.availableBalance) - : savedDisplayMode; - - @computed - double get price => fiatConvertationStore.price; + ? (savedDisplayMode == BalanceDisplayMode.availableBalance + ? BalanceDisplayMode.fullBalance + : BalanceDisplayMode.availableBalance) + : savedDisplayMode; @computed String get cryptoBalance { @@ -86,15 +89,11 @@ abstract class BalanceViewModelBase with Store { final fiatCurrency = settingsStore.fiatCurrency; var balance = '---'; - final totalBalance = _getFiatBalance( - price: price, - cryptoAmount: walletBalance.totalBalance - ); + final totalBalance = + _getFiatBalance(price: price, cryptoAmount: walletBalance.totalBalance); final unlockedBalance = _getFiatBalance( - price: price, - cryptoAmount: walletBalance.unlockedBalance - ); + price: price, cryptoAmount: walletBalance.unlockedBalance); if (displayMode == BalanceDisplayMode.availableBalance) { balance = fiatCurrency.toString() + ' ' + unlockedBalance ?? '0.00'; @@ -119,7 +118,7 @@ abstract class BalanceViewModelBase with Store { if (_wallet is BitcoinWallet) { return WalletBalance( - unlockedBalance: _wallet.balance.totalFormatted, + unlockedBalance: _wallet.balance.availableBalanceFormatted, totalBalance: _wallet.balance.totalFormatted); } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 51db6c556..420586997 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -186,6 +186,8 @@ abstract class DashboardViewModelBase with Store { @observable WalletBase wallet; + bool get hasRescan => wallet.type == WalletType.monero; + BalanceViewModel balanceViewModel; AppStore appStore; @@ -212,6 +214,7 @@ abstract class DashboardViewModelBase with Store { @action void _onWalletChange(WalletBase wallet) { this.wallet = wallet; + type = wallet.type; name = wallet.name; if (wallet is MoneroWallet) { @@ -236,6 +239,21 @@ abstract class DashboardViewModelBase with Store { balanceViewModel: balanceViewModel, settingsStore: appStore.settingsStore))); } + + connectMapToListWithTransform( + appStore.wallet.transactionHistory.transactions, + transactions, + (TransactionInfo val) => TransactionListItem( + transaction: val, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore), + filter: (TransactionInfo tx) { + if (tx is MoneroTransactionInfo && wallet is MoneroWallet) { + return tx.accountIndex == wallet.account.id; + } + + return true; + }); } @action diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index 3bb1d09ba..9cf800705 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -13,16 +13,18 @@ part 'node_list_view_model.g.dart'; class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel; abstract class NodeListViewModelBase with Store { - NodeListViewModelBase(this._nodeSource, this._wallet, this.settingsStore) + NodeListViewModelBase(this._nodeSource, this.wallet, this.settingsStore) : nodes = ObservableList() { _nodeSource.bindToList(nodes, - filter: (Node val) => val?.type == _wallet.type, initialFire: true); + filter: (Node val) => val?.type == wallet.type, initialFire: true); } + @computed + Node get currentNode => settingsStore.nodes[wallet.type]; + final ObservableList nodes; final SettingsStore settingsStore; - - final WalletBase _wallet; + final WalletBase wallet; final Box _nodeSource; Future reset() async { @@ -30,14 +32,12 @@ abstract class NodeListViewModelBase with Store { Node node; - switch (_wallet.type) { + switch (wallet.type) { case WalletType.bitcoin: node = getBitcoinDefaultElectrumServer(nodes: _nodeSource); break; case WalletType.monero: - node = getMoneroDefaultNode( - nodes: _nodeSource, - ); + node = getMoneroDefaultNode(nodes: _nodeSource); break; default: break; @@ -50,5 +50,5 @@ abstract class NodeListViewModelBase with Store { Future delete(Node node) async => node.delete(); Future setAsCurrent(Node node) async => - settingsStore.currentNode = node; + settingsStore.nodes[wallet.type] = node; } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 483d40131..f3d46ccb3 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:hive/hive.dart'; import 'package:intl/intl.dart'; @@ -77,19 +78,9 @@ abstract class SendViewModelBase with Store { PendingTransaction pendingTransaction; @computed - String get balance { - String balance = '0.0'; - - if (_wallet is MoneroWallet) { - balance = _wallet.balance.formattedUnlockedBalance as String ?? ''; - } - - if (_wallet is BitcoinWallet) { - balance = _wallet.balance.confirmedFormatted as String ?? ''; - } - - return balance; - } + String get balance => + _wallet.balance.formattedBalance(BalanceDisplayMode.availableBalance) + as String ?? '0.0'; @computed bool get isReadyForSend => _wallet.syncStatus is SyncedSyncStatus; @@ -97,6 +88,7 @@ abstract class SendViewModelBase with Store { @computed ObservableList