Cw 1539 lightning enhancements (#3400)

* feat: add Lightning transaction URL support in transaction details view model * fix: update currency selection logic in WalletAddressListViewModel and ReceivePage * feat: integrate computed transaction amount and fee getters in TransactionDetailsViewModel, refactor CopyWrapper logic in TransactionDetailsModal * refactor: apply lint rules * refactor: apply lint rules and improve LNURL handling with new methods for withdrawal requests and error checks * auto-reformat * fix: receive_page regression * fix: Withdraw lightning to other btc wallet gives LN address * chore: update .lock files [skip ci] --------- Co-authored-by: Robert Malikowski <malikowskirobert@gmail.com> Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

Konstantin Ullrich committed Jul 17, 2026 at 16:49 UTC 03edd658a525912e1980a617daf06d6ee00c33b2
18 files changed +1200 -1010
cw_bitcoin/pubspec.lock
+5 -5
@@ -634,10 +634,10 @@ packages:
634 dependency: transitive
635 description:
636 name: meta
637 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
637 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
638 url: "https://pub.dev"
639 source: hosted
640 - version: "1.18.0"
640 + version: "1.17.0"
641 mime:
642 dependency: transitive
643 description:
@@ -1100,10 +1100,10 @@ packages:
1100 dependency: transitive
1101 description:
1102 name: test_api
1103 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
1103 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
1104 url: "https://pub.dev"
1105 source: hosted
1106 - version: "0.7.11"
1106 + version: "0.7.10"
1107 torch_dart:
1108 dependency: transitive
1109 description:
@@ -1322,5 +1322,5 @@ packages:
1322 source: hosted
1323 version: "2.2.2"
1324 sdks:
1325 - dart: ">=3.10.0-0 <4.0.0"
1325 + dart: ">=3.9.0 <4.0.0"
1326 flutter: ">=3.29.0"
cw_core/lib/lnurl.dart
+140 -37
@@ -1,19 +1,20 @@
1 -import 'dart:convert';
1 +import "dart:convert";
2
3 -import 'package:bech32/bech32.dart';
4 -import 'package:cw_core/amount/money.dart';
5 -import 'package:cw_core/crypto_currency.dart';
6 -import 'package:cw_core/utils/proxy_wrapper.dart';
3 +import "package:bech32/bech32.dart";
4 +import "package:cw_core/amount/money.dart";
5 +import "package:cw_core/crypto_currency.dart";
6 +import "package:cw_core/utils/proxy_wrapper.dart";
7
8 const _BOLT_PREFIXES = ["lnbcrt", "lntbs", "lnbc", "lntb"];
9 -const _LUD17_PREFIXES = ['lnurlw', 'lnurlc', 'lnurlp', 'keyauth'];
9 +const _LUD17_PREFIXES = ["lnurlw", "lnurlc", "lnurlp", "keyauth"];
10
11 bool isBolt11ZeroInvoice(String invoice) {
12 try {
13 - final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
13 + final request =
14 + const Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
15
16 final prefix =
16 - _BOLT_PREFIXES.firstWhere((prefix) => request.hrp.startsWith(prefix), orElse: () => "");
17 + _BOLT_PREFIXES.firstWhere(request.hrp.startsWith, orElse: () => "");
18
19 return request.hrp.length == prefix.length;
20 } catch (e) {
@@ -23,48 +24,56 @@ bool isBolt11ZeroInvoice(String invoice) {
24
25 /// Get the amount of a Bolt 11 Invoice in
26 int? _getAmountBolt11Msat(String invoice) {
26 - final request = Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
27 + final request =
28 + const Bech32Codec().decode(invoice.replaceFirst("lightning:", ""), invoice.length);
29
30 final prefix = _BOLT_PREFIXES.firstWhere(
29 - (prefix) => request.hrp.startsWith(prefix),
30 - orElse: () => throw FormatException('Invalid BOLT11 invoice: unknown HRP prefix.'),
31 + request.hrp.startsWith,
32 + orElse: () => throw const FormatException("Invalid BOLT11 invoice: unknown HRP prefix."),
33 );
34
35 final amountPart = request.hrp.substring(prefix.length);
34 - if (amountPart.isEmpty) return null; // zero-amount invoice
36 + if (amountPart.isEmpty) {
37 + return null; // zero-amount invoice
38 + }
39
36 - final hasMultiplier = RegExp(r'[munp]$').hasMatch(amountPart);
37 - final multiplier = hasMultiplier ? amountPart[amountPart.length - 1] : '';
40 + final hasMultiplier = RegExp(r"[munp]$").hasMatch(amountPart);
41 + final multiplier = hasMultiplier ? amountPart[amountPart.length - 1] : "";
42 final numberStr = hasMultiplier ? amountPart.substring(0, amountPart.length - 1) : amountPart;
43
40 - if (numberStr.isEmpty || !RegExp(r'^\d+$').hasMatch(numberStr)) {
41 - throw FormatException('Invalid BOLT11 invoice: invalid amount number in HRP.');
44 + if (numberStr.isEmpty || !RegExp(r"^\d+$").hasMatch(numberStr)) {
45 + throw const FormatException("Invalid BOLT11 invoice: invalid amount number in HRP.");
46 }
47
48 final amount = int.parse(numberStr);
49
50 switch (multiplier) {
47 - case '': // bitcoin
51 + case "": // bitcoin
52 return amount * 100000000000;
49 - case 'm': // milli-bitcoin
53 + case "m": // milli-bitcoin
54 return amount * 100000000;
51 - case 'u': // micro-bitcoin
55 + case "u": // micro-bitcoin
56 return amount * 100000;
53 - case 'n': // nano-bitcoin
57 + case "n": // nano-bitcoin
58 return amount * 100;
55 - case 'p':
59 + case "p":
60 if (amount % 10 != 0) {
57 - throw FormatException('Invalid BOLT11 invoice: amount not representable in whole msat.');
61 + throw const FormatException(
62 + "Invalid BOLT11 invoice: amount not representable in whole msat.",
63 + );
64 }
65 return amount ~/ 10;
66 default:
61 - throw FormatException('Invalid BOLT11 invoice: unknown amount multiplier.');
67 + throw const FormatException("Invalid BOLT11 invoice: unknown amount multiplier.");
68 }
69 }
70
71 Money? getBolt11Amount(String invoice) {
72 final msat = _getAmountBolt11Msat(invoice);
67 - if (msat == null || msat % 1000 != 0) return null;
73 + if (msat == null || msat % 1000 != 0) {
74 + return null;
75 + }
76 +
77 return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln);
78 }
79
@@ -73,30 +82,99 @@ class LNURL {
82 final url = decode(lnurl);
83 final response = await ProxyWrapper().get(clearnetUri: url);
84
76 - if (response.statusCode != 200) return null;
85 + if (response.statusCode != 200) {
86 + return null;
87 + }
88
89 try {
90 final body = jsonDecode(response.body) as Map;
91 final tag = body["tag"] as String?;
92
82 - if (tag != "payRequest") return null;
93 + if (tag != "payRequest") {
94 + return null;
95 + }
96
97 final msat = body["minSendable"] as int?;
98 final maxSendable = body["maxSendable"] as int?;
99
100 // if minSendable and maxSendable are the same we assume a specific payment request
88 - if (msat != maxSendable) return null;
101 + if (msat != maxSendable) {
102 + return null;
103 + }
104
90 - if (msat == null || msat % 1000 != 0) return null;
105 + if (msat == null || msat % 1000 != 0) {
106 + return null;
107 + }
108 return Money.fromInt(msat ~/ 1000, CryptoCurrency.btcln);
109 } catch (_) {
110 return null;
111 }
112 }
113
114 + static Future<LNURLWithdrawRequest?> getWithdrawRequest(String lnurl) async {
115 + final url = decode(lnurl);
116 + final response = await ProxyWrapper().get(clearnetUri: url);
117 +
118 + if (response.statusCode != 200) {
119 + return null;
120 + }
121 +
122 + try {
123 + final body = jsonDecode(response.body) as Map;
124 + final tag = body["tag"] as String?;
125 +
126 + if (tag != "withdrawRequest") {
127 + return null;
128 + }
129 +
130 + final minWithdrawable = body["minWithdrawable"] as int?;
131 + final maxWithdrawable = body["maxWithdrawable"] as int?;
132 +
133 + if ((minWithdrawable == null || minWithdrawable % 1000 != 0) ||
134 + (maxWithdrawable == null || maxWithdrawable % 1000 != 0)) {
135 + return null;
136 + }
137 +
138 + return LNURLWithdrawRequest(
139 + callback: body["callback"] as String,
140 + k1: body["k1"] as String,
141 + description: body["defaultDescription"] as String,
142 + minAmount: Money.fromInt(minWithdrawable ~/ 1000, CryptoCurrency.btcln),
143 + maxAmount: Money.fromInt(maxWithdrawable ~/ 1000, CryptoCurrency.btcln),
144 + );
145 + } catch (_) {
146 + return null;
147 + }
148 + }
149 +
150 + Future<bool> commitWithdrawRequest(LNURLWithdrawRequest request, String invoice) async {
151 + try {
152 + final url = Uri.parse(request.callback);
153 + url.queryParameters["k1"] = request.k1;
154 + url.queryParameters["pr"] = invoice;
155 +
156 + final response = await ProxyWrapper().get(clearnetUri: url);
157 +
158 + if (response.statusCode != 200) {
159 + return false;
160 + }
161 +
162 + final body = jsonDecode(response.body) as Map;
163 + final status = body["status"] as String?;
164 +
165 + if (status == "OK") {
166 + return true;
167 + }
168 +
169 + return false;
170 + } catch (_) {
171 + return false;
172 + }
173 + }
174 +
175 static String encode(String url) {
176 final raw = _convert(utf8.encode(url), 8, 5, true);
99 - return const Bech32Codec().encode(Bech32('lnurl', raw), 999);
177 + return const Bech32Codec().encode(Bech32("lnurl", raw), 999);
178 }
179
180 static Uri decode(String encodedUrl) {
@@ -114,9 +192,10 @@ class LNURL {
192 if (_LUD17_PREFIXES.contains(decodedUri.scheme)) {
193 /// If the non-bech32 LNURL is a Tor address, the port has to be http instead of https for the clearnet LNURL so check if the host ends with '.onion' or '.onion.'
194 decodedUri = decodedUri.replace(
117 - scheme: decodedUri.host.endsWith('onion') || decodedUri.host.endsWith('onion.')
118 - ? 'http'
119 - : 'https');
195 + scheme: decodedUri.host.endsWith("onion") || decodedUri.host.endsWith("onion.")
196 + ? "http"
197 + : "https",
198 + );
199 } else {
200 /// Try to parse the input as a lnUrl. Will throw an error if it fails.
201 final lnUrl = _findLnUrl(encodedUrl);
@@ -129,12 +208,30 @@ class LNURL {
208 }
209 }
210
211 +class LNURLWithdrawRequest {
212 + const LNURLWithdrawRequest({
213 + required this.callback,
214 + required this.k1,
215 + required this.description,
216 + required this.minAmount,
217 + required this.maxAmount,
218 + });
219 +
220 + final String callback;
221 + final String k1;
222 + final String description;
223 + final Money minAmount;
224 + final Money maxAmount;
225 +}
226 +
227 /// Parse and return a given lnurl string if it's valid. Will remove
228 /// `lightning:` from the beginning of it if present.
229 String _findLnUrl(String input) {
135 - final res = RegExp(r',*?((lnurl)([0-9]+[a-z0-9]+))').allMatches(input.toLowerCase());
230 + final res = RegExp(r",*?((lnurl)([0-9]+[a-z0-9]+))").allMatches(input.toLowerCase());
231
137 - if (res.length != 1) throw ArgumentError('Not a valid lnurl string');
232 + if (res.length != 1) {
233 + throw ArgumentError("Not a valid lnurl string");
234 + }
235 return res.first.group(0)!;
236 }
237
@@ -156,10 +253,16 @@ List<int> _convert(List<int> data, int inBits, int outBits, bool pad) {
253 }
254
255 if (pad) {
159 - if (bits > 0) result.add((value << (outBits - bits)) & maxV);
256 + if (bits > 0) {
257 + result.add((value << (outBits - bits)) & maxV);
258 + }
259 } else {
161 - if (bits >= inBits) throw Exception('[BECH32] Excess padding');
162 - if ((value << (outBits - bits)) & maxV > 0) throw Exception('[BECH32] Non-zero padding');
260 + if (bits >= inBits) {
261 + throw Exception("[BECH32] Excess padding");
262 + }
263 + if ((value << (outBits - bits)) & maxV > 0) {
264 + throw Exception("[BECH32] Non-zero padding");
265 + }
266 }
267
268 return result;
cw_core/pubspec.lock
+5 -5
@@ -394,10 +394,10 @@ packages:
394 dependency: transitive
395 description:
396 name: meta
397 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
397 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
398 url: "https://pub.dev"
399 source: hosted
400 - version: "1.18.0"
400 + version: "1.17.0"
401 mime:
402 dependency: transitive
403 description:
@@ -738,10 +738,10 @@ packages:
738 dependency: transitive
739 description:
740 name: test_api
741 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
741 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
742 url: "https://pub.dev"
743 source: hosted
744 - version: "0.7.11"
744 + version: "0.7.10"
745 torch_dart:
746 dependency: "direct main"
747 description:
@@ -838,5 +838,5 @@ packages:
838 source: hosted
839 version: "3.1.3"
840 sdks:
841 - dart: ">=3.10.0-0 <4.0.0"
841 + dart: ">=3.9.0 <4.0.0"
842 flutter: ">=3.27.0"
cw_decred/pubspec.lock
+4 -4
@@ -417,10 +417,10 @@ packages:
417 dependency: transitive
418 description:
419 name: meta
420 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
420 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
421 url: "https://pub.dev"
422 source: hosted
423 - version: "1.18.0"
423 + version: "1.17.0"
424 mime:
425 dependency: transitive
426 description:
@@ -769,10 +769,10 @@ packages:
769 dependency: transitive
770 description:
771 name: test_api
772 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
772 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
773 url: "https://pub.dev"
774 source: hosted
775 - version: "0.7.11"
775 + version: "0.7.10"
776 torch_dart:
777 dependency: transitive
778 description:
cw_monero/pubspec.lock
+5 -5
@@ -505,10 +505,10 @@ packages:
505 dependency: transitive
506 description:
507 name: meta
508 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
508 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
509 url: "https://pub.dev"
510 source: hosted
511 - version: "1.18.0"
511 + version: "1.17.0"
512 mime:
513 dependency: transitive
514 description:
@@ -906,10 +906,10 @@ packages:
906 dependency: transitive
907 description:
908 name: test_api
909 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
909 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
910 url: "https://pub.dev"
911 source: hosted
912 - version: "0.7.11"
912 + version: "0.7.10"
913 torch_dart:
914 dependency: transitive
915 description:
@@ -1048,5 +1048,5 @@ packages:
1048 source: hosted
1049 version: "3.1.3"
1050 sdks:
1051 - dart: ">=3.10.0-0 <4.0.0"
1051 + dart: ">=3.9.0 <4.0.0"
1052 flutter: ">=3.24.0"
cw_nano/pubspec.lock
+4 -4
@@ -462,10 +462,10 @@ packages:
462 dependency: transitive
463 description:
464 name: meta
465 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
465 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
466 url: "https://pub.dev"
467 source: hosted
468 - version: "1.18.0"
468 + version: "1.17.0"
469 mime:
470 dependency: transitive
471 description:
@@ -887,10 +887,10 @@ packages:
887 dependency: transitive
888 description:
889 name: test_api
890 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
890 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
891 url: "https://pub.dev"
892 source: hosted
893 - version: "0.7.11"
893 + version: "0.7.10"
894 torch_dart:
895 dependency: transitive
896 description:
cw_wownero/pubspec.lock
+4 -4
@@ -417,10 +417,10 @@ packages:
417 dependency: transitive
418 description:
419 name: meta
420 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
420 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
421 url: "https://pub.dev"
422 source: hosted
423 - version: "1.18.0"
423 + version: "1.17.0"
424 mime:
425 dependency: transitive
426 description:
@@ -786,10 +786,10 @@ packages:
786 dependency: transitive
787 description:
788 name: test_api
789 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
789 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
790 url: "https://pub.dev"
791 source: hosted
792 - version: "0.7.11"
792 + version: "0.7.10"
793 torch_dart:
794 dependency: transitive
795 description:
cw_zano/pubspec.lock
+4 -4
@@ -422,10 +422,10 @@ packages:
422 dependency: transitive
423 description:
424 name: meta
425 - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
425 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
426 url: "https://pub.dev"
427 source: hosted
428 - version: "1.18.0"
428 + version: "1.17.0"
429 mime:
430 dependency: transitive
431 description:
@@ -783,10 +783,10 @@ packages:
783 dependency: transitive
784 description:
785 name: test_api
786 - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
786 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
787 url: "https://pub.dev"
788 source: hosted
789 - version: "0.7.11"
789 + version: "0.7.10"
790 torch_dart:
791 dependency: transitive
792 description:
lib/cake_pay/src/widgets/rounded_overlay_cards_widget.dart
+8 -8
@@ -15,14 +15,14 @@ class RoundedOverlayCards extends StatelessWidget {
15
16 return ClipRRect(
17 borderRadius:
18 - BorderRadius.only(bottomLeft: Radius.circular(25.0), bottomRight: Radius.circular(25.0)),
18 + BorderRadius.only(bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25)),
19 child: Container(
20 decoration: BoxDecoration(
21 - borderRadius: const BorderRadius.only(
22 - bottomLeft: Radius.circular(24),
23 - bottomRight: Radius.circular(24),
24 - ),
25 - color: Theme.of(context).colorScheme.surfaceContainer,
21 + borderRadius: const BorderRadius.only(
22 + bottomLeft: Radius.circular(24),
23 + bottomRight: Radius.circular(24),
24 + ),
25 + color: Theme.of(context).colorScheme.surfaceContainer,
26 ),
27 child: Column(
28 mainAxisSize: MainAxisSize.min,
@@ -30,8 +30,8 @@ class RoundedOverlayCards extends StatelessWidget {
30 Flexible(
31 child: ClipRRect(
32 borderRadius: const BorderRadius.only(
33 - bottomLeft: Radius.circular(25.0),
34 - bottomRight: Radius.circular(25.0),
33 + bottomLeft: Radius.circular(25),
34 + bottomRight: Radius.circular(25),
35 ),
36 child: Container(
37 decoration: BoxDecoration(
lib/core/lightning_invoice_service.dart
+21 -15
@@ -1,6 +1,6 @@
1 -import 'dart:convert';
1 +import "dart:convert";
2
3 -import 'package:cw_core/utils/proxy_wrapper.dart';
3 +import "package:cw_core/utils/proxy_wrapper.dart";
4
5 Future<String?> getBolt11FromLightingAddress(String lightningAddress, {int amount = 0}) async {
6 try {
@@ -31,28 +31,34 @@ Uri getURlOfLightningAddress(String lightningAddress) {
31 }
32
33 class _LNURLPResponseDTO {
34 + const _LNURLPResponseDTO(
35 + this.callback,
36 + this.maxSendable,
37 + this.minSendable,
38 + this.tag,
39 + this.metadata,
40 + );
41 +
42 + factory _LNURLPResponseDTO.fromJson(Map<String, dynamic> map) => _LNURLPResponseDTO(
43 + map["callback"] as String,
44 + map["maxSendable"] as int,
45 + map["minSendable"] as int,
46 + map["tag"] as String,
47 + map["metadata"] as String,
48 + );
49 +
50 final String callback;
51 final int maxSendable;
52 final int minSendable;
53 final String tag;
54 final String metadata;
39 -
40 - const _LNURLPResponseDTO(
41 - this.callback, this.maxSendable, this.minSendable, this.tag, this.metadata);
42 -
43 - static _LNURLPResponseDTO fromJson(Map<String, dynamic> map) => _LNURLPResponseDTO(
44 - map["callback"] as String,
45 - map["maxSendable"] as int,
46 - map["minSendable"] as int,
47 - map["tag"] as String,
48 - map["metadata"] as String);
55 }
56
57 class _LNURLPCallbackResponseDTO {
52 - final String pr;
53 -
58 const _LNURLPCallbackResponseDTO(this.pr);
59
56 - static _LNURLPCallbackResponseDTO fromJson(Map<String, dynamic> map) =>
60 + factory _LNURLPCallbackResponseDTO.fromJson(Map<String, dynamic> map) =>
61 _LNURLPCallbackResponseDTO(map["pr"] as String);
62 +
63 + final String pr;
64 }
lib/new-ui/pages/receive_page.dart
+121 -115
@@ -1,54 +1,54 @@
1 -import 'package:cake_wallet/core/utilities.dart';
2 -import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
3 -import 'package:cake_wallet/generated/i18n.dart';
4 -import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
5 -import 'package:cake_wallet/new-ui/widgets/receive_page/payjoin_copy_modal.dart';
6 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_address_type.dart';
7 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_address_widget.dart';
8 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_amount_display.dart';
9 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_amount_modal.dart';
10 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_bottom_buttons.dart';
11 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_info_box.dart';
12 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_label_modal.dart';
13 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_label_widget.dart';
14 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_large_amount_preview.dart';
15 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_qr_code.dart';
16 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_token_display.dart';
17 -import 'package:cake_wallet/utils/share_util.dart';
18 -import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
19 -import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
20 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
21 -import 'package:cake_wallet/zcash/zcash.dart';
22 -import 'package:cw_core/crypto_currency.dart';
23 -import 'package:cw_core/payment_uris.dart';
24 -import 'package:cw_core/receive_page_option.dart';
25 -import 'package:cw_core/utils/print_verbose.dart';
26 -import 'package:flutter/cupertino.dart';
27 -import 'package:flutter_mobx/flutter_mobx.dart';
28 -import 'package:mobx/mobx.dart';
29 -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
30 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
31 -import 'package:flutter/material.dart';
32 -import 'package:flutter/services.dart';
33 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
34 -import 'package:cake_wallet/di.dart';
35 -import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
36 -import 'package:cake_wallet/entities/preferences_key.dart';
37 -import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
38 -import 'package:cw_core/wallet_type.dart';
39 -import 'package:cake_wallet/routes.dart';
40 -import 'package:shared_preferences/shared_preferences.dart';
41 -
42 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
1 +import "package:cake_wallet/anonpay/anonpay_donation_link_info.dart";
2 +import "package:cake_wallet/bitcoin/bitcoin.dart";
3 +import "package:cake_wallet/core/utilities.dart";
4 +import "package:cake_wallet/di.dart";
5 +import "package:cake_wallet/entities/auto_generate_subaddress_status.dart";
6 +import "package:cake_wallet/entities/preferences_key.dart";
7 +import "package:cake_wallet/generated/i18n.dart";
8 +import "package:cake_wallet/new-ui/widgets/modern_button.dart";
9 +import "package:cake_wallet/new-ui/widgets/receive_page/payjoin_copy_modal.dart";
10 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_address_type.dart";
11 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_address_widget.dart";
12 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_amount_display.dart";
13 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_amount_modal.dart";
14 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_bottom_buttons.dart";
15 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_info_box.dart";
16 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_label_modal.dart";
17 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_label_widget.dart";
18 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_large_amount_preview.dart";
19 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_qr_code.dart";
20 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_token_display.dart";
21 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
22 +import "package:cake_wallet/routes.dart";
23 +import "package:cake_wallet/src/screens/receive/anonpay_receive_page.dart";
24 +import "package:cake_wallet/utils/share_util.dart";
25 +import "package:cake_wallet/view_model/dashboard/dashboard_view_model.dart";
26 +import "package:cake_wallet/view_model/dashboard/receive_option_view_model.dart";
27 +import "package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart";
28 +import "package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart";
29 +import "package:cake_wallet/zcash/zcash.dart";
30 +import "package:cw_core/crypto_currency.dart";
31 +import "package:cw_core/payment_uris.dart";
32 +import "package:cw_core/receive_page_option.dart";
33 +import "package:cw_core/utils/print_verbose.dart";
34 +import "package:cw_core/wallet_type.dart";
35 +import "package:flutter/cupertino.dart";
36 +import "package:flutter/material.dart";
37 +import "package:flutter/services.dart";
38 +import "package:flutter_mobx/flutter_mobx.dart";
39 +import "package:mobx/mobx.dart";
40 +import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
41 +import "package:shared_preferences/shared_preferences.dart";
42
43 class NewReceivePage extends StatefulWidget {
45 - NewReceivePage(
46 - {super.key,
47 - required this.addressListViewModel,
48 - required this.receiveOptionViewModel,
49 - required this.dashboardViewModel,
50 - required this.lightningMode,
51 - CryptoCurrency? initialCurrency}) {
44 + NewReceivePage({
45 + required this.addressListViewModel,
46 + required this.receiveOptionViewModel,
47 + required this.dashboardViewModel,
48 + required this.lightningMode,
49 + super.key,
50 + CryptoCurrency? initialCurrency,
51 + }) {
52 if (initialCurrency != null && initialCurrency != addressListViewModel.selectedCurrency) {
53 addressListViewModel.setTokenCurrency(initialCurrency);
54 }
@@ -78,31 +78,33 @@ class _NewReceivePageState extends State<NewReceivePage> {
78 .firstWhereOrNull((item) => item.value.contains("Lightning")) ??
79 ReceivePageOption.mainnet,
80 );
81 - widget.addressListViewModel.setTokenCurrency(CryptoCurrency.btcln);
81 + widget.addressListViewModel.selectedCurrency = CryptoCurrency.btcln;
82 } else if (widget.addressListViewModel.wallet.type == WalletType.bitcoin) {
83 widget.receiveOptionViewModel.selectReceiveOption(
84 widget.receiveOptionViewModel.options
85 .firstWhereOrNull((item) => item.value.contains("Standard")) ??
86 ReceivePageOption.mainnet,
87 );
88 + widget.addressListViewModel.selectedCurrency = CryptoCurrency.btc;
89 }
90 });
91
91 - reaction((_) => widget.addressListViewModel.uri, (newAddress) {
92 - _reloadAddressWithLabel(newAddress);
93 - });
92 + reaction((_) => widget.addressListViewModel.uri, _reloadAddressWithLabel);
93
95 - _addressItemWithLabel =
96 - widget.addressListViewModel.forceRecomputeItems.firstWhereOrNull((item) {
97 - return (item is WalletAddressListItem &&
98 - item.address == widget.addressListViewModel.uri.address);
99 - }) as WalletAddressListItem?;
94 + _addressItemWithLabel = widget.addressListViewModel.forceRecomputeItems.firstWhereOrNull(
95 + (item) =>
96 + item is WalletAddressListItem && item.address == widget.addressListViewModel.uri.address,
97 + ) as WalletAddressListItem?;
98
101 - reaction((_) => widget.receiveOptionViewModel.selectedReceiveOption,
102 - (ReceivePageOption option) {
99 + reaction((_) => widget.receiveOptionViewModel.selectedReceiveOption, (option) {
100 if (widget.dashboardViewModel.type == WalletType.bitcoin &&
101 bitcoin!.isBitcoinReceivePageOption(option)) {
102 widget.addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
103 + if (option.value.contains("Lightning")) {
104 + widget.addressListViewModel.selectedCurrency = CryptoCurrency.btcln;
105 + } else {
106 + widget.addressListViewModel.selectedCurrency = CryptoCurrency.btc;
107 + }
108 return;
109 }
110 if (widget.dashboardViewModel.type == WalletType.zcash) {
@@ -166,16 +168,18 @@ class _NewReceivePageState extends State<NewReceivePage> {
168 final hasAddressTypeSelector = widget.receiveOptionViewModel.options.length > 1;
169 final hasLabel = _addressItemWithLabel?.name != null && _addressItemWithLabel!.name!.isNotEmpty;
170 final infoboxDismissed = widget.addressListViewModel.wallet.walletInfo.receiveInfoboxDismissed;
169 - final infobox = ReceiveInfoBox.forWalletType(widget.addressListViewModel.type,
170 - supportedCurrencies: widget.addressListViewModel.tokenCurrencies
171 - .whereType<CryptoCurrency>()
172 - .toList(), onDismissed: () {
173 - widget.addressListViewModel.dismissInfobox();
174 - setState(() {});
175 - },
176 - autoGenerateSubaddressStatus: widget.lightningMode
177 - ? AutoGenerateSubaddressStatus.disabled
178 - : widget.dashboardViewModel.settingsStore.autoGenerateSubaddressStatus);
171 + final infobox = ReceiveInfoBox.forWalletType(
172 + widget.addressListViewModel.type,
173 + supportedCurrencies:
174 + widget.addressListViewModel.tokenCurrencies.whereType<CryptoCurrency>().toList(),
175 + onDismissed: () {
176 + widget.addressListViewModel.dismissInfobox();
177 + setState(() {});
178 + },
179 + autoGenerateSubaddressStatus: widget.lightningMode
180 + ? AutoGenerateSubaddressStatus.disabled
181 + : widget.dashboardViewModel.settingsStore.autoGenerateSubaddressStatus,
182 + );
183
184 return Container(
185 decoration: BoxDecoration(
@@ -187,9 +191,7 @@ class _NewReceivePageState extends State<NewReceivePage> {
191 begin: Alignment.topCenter,
192 end: Alignment.bottomCenter,
193 ),
190 - borderRadius: BorderRadius.vertical(
191 - top: Radius.circular(24),
192 - ),
194 + borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
195 ),
196 child: SafeArea(
197 child: Column(
@@ -198,13 +200,13 @@ class _NewReceivePageState extends State<NewReceivePage> {
200 children: [
201 ModalTopBar(
202 title: _largeQrMode ? "" : S.of(context).receive,
201 - leadingIcon: Icon(Icons.close),
203 + leadingIcon: const Icon(Icons.close),
204 onLeadingPressed: () {
205 Navigator.of(context, rootNavigator: true).pop();
206 },
207 trailingWidget: Observer(
208 builder: (_) => AnimatedSwitcher(
207 - duration: Duration(milliseconds: 300),
209 + duration: const Duration(milliseconds: 300),
210 child: _largeQrMode ||
211 widget.addressListViewModel.hasAddressRotation
212 /* TODO rotating is broken on mweb, disabling for now, fix after mvp*/
@@ -217,10 +219,10 @@ class _NewReceivePageState extends State<NewReceivePage> {
219 key: ValueKey(_largeQrMode),
220 size: 36,
221 icon: _largeQrMode
220 - ? Icon(Icons.share)
222 + ? const Icon(Icons.share)
223 : widget.addressListViewModel.isRotatingAddress
222 - ? CupertinoActivityIndicator()
223 - : Icon(Icons.refresh),
224 + ? const CupertinoActivityIndicator()
225 + : const Icon(Icons.refresh),
226 onPressed: () {
227 if (_largeQrMode) {
228 ShareUtil.share(
@@ -232,8 +234,9 @@ class _NewReceivePageState extends State<NewReceivePage> {
234 widget.addressListViewModel.rotateAddress();
235 }
236 }
235 - })
236 - : SizedBox.shrink(),
237 + },
238 + )
239 + : const SizedBox.shrink(),
240 ),
241 ),
242 ),
@@ -269,11 +272,12 @@ class _NewReceivePageState extends State<NewReceivePage> {
272 addressListViewModel: widget.addressListViewModel,
273 ),
274 GestureDetector(
272 - onTap: _showLabelModal,
273 - child: ReceiveLabelWidget(
274 - name: _addressItemWithLabel?.name ?? "",
275 - largeQrMode: _largeQrMode,
276 - )),
275 + onTap: _showLabelModal,
276 + child: ReceiveLabelWidget(
277 + name: _addressItemWithLabel?.name ?? "",
278 + largeQrMode: _largeQrMode,
279 + ),
280 + ),
281 Observer(
282 builder: (_) => ReceiveBottomButtons(
283 key: const ValueKey(0),
@@ -285,14 +289,16 @@ class _NewReceivePageState extends State<NewReceivePage> {
289 : ClipboardData(
290 text: widget.addressListViewModel.displayAmount.isEmpty
291 ? widget.addressListViewModel.uri.address
288 - : widget.addressListViewModel.uri.toString()),
292 + : widget.addressListViewModel.uri.toString(),
293 + ),
294 onCopyButtonPressed: () {
295 if (widget.addressListViewModel.hasPayjoin) {
296 showModalBottomSheet(
292 - isScrollControlled: true,
293 - context: context,
294 - builder: (context) =>
295 - PayjoinCopyModal(uri: widget.addressListViewModel.uri));
297 + isScrollControlled: true,
298 + context: context,
299 + builder: (context) =>
300 + PayjoinCopyModal(uri: widget.addressListViewModel.uri),
301 + );
302 }
303 },
304 onAmountButtonPressed: () {
@@ -300,12 +306,10 @@ class _NewReceivePageState extends State<NewReceivePage> {
306 context: context,
307 backgroundColor: Colors.transparent,
308 barrierColor: Colors.black.withAlpha(80),
303 - builder: (context) {
304 - return ReceiveAmountModal(
305 - walletAddressListViewModel: widget.addressListViewModel,
306 - onSubmitted: (amount) {},
307 - );
308 - },
309 + builder: (context) => ReceiveAmountModal(
310 + walletAddressListViewModel: widget.addressListViewModel,
311 + onSubmitted: (amount) {},
312 + ),
313 );
314 },
315 onLabelButtonPressed: _showLabelModal,
@@ -318,22 +322,25 @@ class _NewReceivePageState extends State<NewReceivePage> {
322 ),
323 ),
324 ReceiveLargeAmountPreview(
321 - amount: widget.addressListViewModel.displayAmount,
322 - currency: widget.addressListViewModel.cryptoCurrencySymbol,
323 - largeQrMode: _largeQrMode),
325 + amount: widget.addressListViewModel.displayAmount,
326 + currency: widget.addressListViewModel.cryptoCurrencySymbol,
327 + largeQrMode: _largeQrMode,
328 + ),
329 if (infobox != null && !widget.addressListViewModel.isLightning)
330 ClipRect(
326 - child: AnimatedAlign(
327 - duration: const Duration(milliseconds: 200),
328 - curve: Curves.easeOutCubic,
329 - heightFactor: infoboxDismissed ? 0 : 1,
330 - alignment: Alignment.center,
331 - child: AnimatedOpacity(
331 + child: AnimatedAlign(
332 + duration: const Duration(milliseconds: 200),
333 + curve: Curves.easeOutCubic,
334 + heightFactor: infoboxDismissed ? 0 : 1,
335 + alignment: Alignment.center,
336 + child: AnimatedOpacity(
337 duration: const Duration(milliseconds: 200),
338 opacity: infoboxDismissed ? 0 : 1,
339 curve: Curves.easeOutCubic,
335 - child: infobox),
336 - ))
340 + child: infobox,
341 + ),
342 + ),
343 + ),
344 ],
345 ),
346 ),
@@ -345,12 +352,11 @@ class _NewReceivePageState extends State<NewReceivePage> {
352
353 void _showLabelModal() {
354 showMaterialModalBottomSheet(
348 - context: context,
349 - backgroundColor: Colors.transparent,
350 - barrierColor: Colors.black.withAlpha(80),
351 - builder: (context) {
352 - return getIt.get<ReceiveLabelModal>(param1: _addressItemWithLabel);
353 - }).then((value) {
355 + context: context,
356 + backgroundColor: Colors.transparent,
357 + barrierColor: Colors.black.withAlpha(80),
358 + builder: (_) => getIt.get<ReceiveLabelModal>(param1: _addressItemWithLabel),
359 + ).then((value) {
360 _reloadAddressWithLabel(widget.addressListViewModel.uri);
361 });
362 }
@@ -359,8 +365,8 @@ class _NewReceivePageState extends State<NewReceivePage> {
365 // FIXME: viewmodel doesn't want to load address name here, so we make it. investigate why later
366 setState(() {
367 _addressItemWithLabel = widget.addressListViewModel.forceRecomputeItems.firstWhereOrNull(
362 - (item) => (item is WalletAddressListItem && item.address == newAddress.address))
363 - as WalletAddressListItem?;
368 + (item) => item is WalletAddressListItem && item.address == newAddress.address,
369 + ) as WalletAddressListItem?;
370 });
371 }
372 }
lib/new-ui/pages/send_page.dart
+2 -2
@@ -594,7 +594,7 @@ class _NewSendPageState extends State<NewSendPage> {
594 builder: (context) => Material(
595 child: L2ActionWalletSelector(
596 showOtherWallets: false,
597 - action: l2actions.deposit,
597 + action: L2Actions.deposit,
598 sendViewModel: widget.sendViewModel,
599 contactListViewModel:
600 widget.contactListViewModel,
@@ -609,7 +609,7 @@ class _NewSendPageState extends State<NewSendPage> {
609 builder: (context) => Material(
610 child: L2ActionWalletSelector(
611 showOtherWallets: false,
612 - action: l2actions.withdraw,
612 + action: L2Actions.withdraw,
613 sendViewModel: widget.sendViewModel,
614 contactListViewModel:
615 widget.contactListViewModel,
lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart
+4 -5
@@ -59,14 +59,13 @@ class _AssetsHistorySectionState extends State<AssetsHistorySection> {
59 AssetsSection(
60 dashboardViewModel: widget.dashboardViewModel,
61 ),
62 - hasAssetsButton ?
63 - AssetsHistorySectionActionButton(S.current.tokens, "assets/new-ui/options_slider.svg",
64 - () {
65 - Navigator.of(context).pushNamed(
62 + hasAssetsButton ? AssetsHistorySectionActionButton(S.current.tokens, "assets/new-ui/options_slider.svg",
63 +
64 + () {Navigator.of(context).pushNamed(
65 Routes.homeSettings,
66 arguments: widget.dashboardViewModel.balanceViewModel,
67 );
69 - }) : null),
68 + }): null),
69 AssetsHistorySectionTab(
70 S.current.history,
71 HistorySection(
lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart
+191 -169
@@ -1,24 +1,28 @@
1 -import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart';
2 -import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart';
3 -import 'package:cake_wallet/generated/i18n.dart';
4 -import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
5 -import 'package:cake_wallet/new-ui/widgets/copy_wrapper.dart';
6 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
7 -import 'package:cake_wallet/routes.dart';
8 -import 'package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart';
9 -import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
10 -import 'package:cake_wallet/src/screens/transaction_details/address_list_item.dart';
11 -import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart';
12 -import 'package:cake_wallet/utils/address_formatter.dart';
13 -import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
14 -import 'package:flutter/material.dart';
15 -import 'package:flutter/services.dart';
16 -import 'package:flutter_mobx/flutter_mobx.dart';
17 -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
1 +import "package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart";
2 +import "package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart";
3 +import "package:cake_wallet/generated/i18n.dart";
4 +import "package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart";
5 +import "package:cake_wallet/new-ui/widgets/copy_wrapper.dart";
6 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
7 +import "package:cake_wallet/routes.dart";
8 +import "package:cake_wallet/src/screens/transaction_details/address_list_item.dart";
9 +import "package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart";
10 +import "package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart";
11 +import "package:cake_wallet/src/widgets/new_list_row/new_list_section.dart";
12 +import "package:cake_wallet/utils/address_formatter.dart";
13 +import "package:cake_wallet/view_model/transaction_details_view_model.dart";
14 +import "package:flutter/material.dart";
15 +import "package:flutter/services.dart";
16 +import "package:flutter_mobx/flutter_mobx.dart";
17 +import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
18
19 class TransactionDetailsModal extends StatefulWidget {
20 const TransactionDetailsModal(
21 - {super.key, required this.transactionDetailsViewModel, this.highlightNoteField = false});
21 + {
22 + required this.transactionDetailsViewModel,
23 + this.highlightNoteField = false,
24 + super.key,
25 + });
26
27 final TransactionDetailsViewModel transactionDetailsViewModel;
28 final bool highlightNoteField;
@@ -48,177 +52,195 @@ class _TransactionDetailsModalState extends State<TransactionDetailsModal> {
52 }
53
54 @override
51 - Widget build(BuildContext context) {
52 - final transactionInfoAmount = widget.transactionDetailsViewModel.transactionInfo.amount;
53 -
54 - return SafeArea(
55 - bottom: false,
56 - child: Padding(
57 - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
58 - child: GestureDetector(
59 - onTap: FocusScope.of(context).unfocus,
60 - child: Container(
61 - decoration: BoxDecoration(
55 + Widget build(BuildContext context) => SafeArea(
56 + bottom: false,
57 + child: Padding(
58 + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
59 + child: GestureDetector(
60 + onTap: FocusScope.of(context).unfocus,
61 + child: Container(
62 + decoration: BoxDecoration(
63 color: Theme.of(context).colorScheme.surface,
63 - borderRadius: BorderRadius.vertical(top: Radius.circular(25))),
64 - child: Column(
65 - children: [
66 - ModalTopBar(
67 - title: S.of(context).transaction,
68 - leadingIcon: Icon(Icons.close),
69 - onLeadingPressed: Navigator.of(context).pop,
70 - ),
71 - Expanded(
72 - child: SingleChildScrollView(
73 - controller: ModalScrollController.of(context),
74 - child: Column(
75 - children: [
76 - TokenImageWidget(
77 - imageUrl:
78 - widget.transactionDetailsViewModel.transactionAsset.iconPath ?? "",
79 - size: 64,
80 - ),
81 - SizedBox(height: 10),
82 - Text(
83 - widget.transactionDetailsViewModel.formattedTitle +
84 - widget.transactionDetailsViewModel.formattedStatus,
85 - style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
86 - ),
87 - CopyWrapper(
88 - requireLongPress: true,
89 - data: ClipboardData(text: transactionInfoAmount.toString()),
90 - builder: (context, copied) => AnimatedSwitcher(
91 - duration: Duration(milliseconds: 300),
92 - child: Text(
93 - key: ValueKey(copied),
94 - copied
95 - ? S.of(context).copied
96 - : transactionInfoAmount.toStringWithSymbol(),
97 - style: TextStyle(
98 - fontSize: 28,
99 - color: copied
100 - ? Theme.of(context).colorScheme.primary
101 - : Theme.of(context).colorScheme.onSurface),
64 + borderRadius: const BorderRadius.vertical(top: Radius.circular(25)),
65 + ),
66 + child: Column(
67 + children: [
68 + ModalTopBar(
69 + title: S.of(context).transaction,
70 + leadingIcon: const Icon(Icons.close),
71 + onLeadingPressed: Navigator.of(context).pop,
72 + ),
73 + Expanded(
74 + child: SingleChildScrollView(
75 + controller: ModalScrollController.of(context),
76 + child: Column(
77 + children: [
78 + TokenImageWidget(
79 + imageUrl:
80 + widget.transactionDetailsViewModel.transactionAsset.iconPath ?? "",
81 + size: 64,
82 + ),
83 + const SizedBox(height: 10),
84 + Text(
85 + widget.transactionDetailsViewModel.formattedTitle +
86 + widget.transactionDetailsViewModel.formattedStatus,
87 + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
88 + ),
89 + Observer(
90 + builder: (_) => CopyWrapper(
91 + requireLongPress: true,
92 + data: ClipboardData(
93 + text: widget.transactionDetailsViewModel.transactionCopyAmount,
94 + ),
95 + builder: (context, copied) => AnimatedSwitcher(
96 + duration: const Duration(milliseconds: 300),
97 + child: Text(
98 + key: ValueKey(copied),
99 + copied
100 + ? S.of(context).copied
101 + : widget.transactionDetailsViewModel.transactionAmount,
102 + style: TextStyle(
103 + fontSize: 28,
104 + color: copied
105 + ? Theme.of(context).colorScheme.primary
106 + : Theme.of(context).colorScheme.onSurface,
107 + ),
108 + ),
109 + ),
110 ),
111 ),
104 - ),
105 - Padding(
106 - padding: const EdgeInsets.all(16.0),
107 - child: Column(
108 - spacing: 12,
109 - children: [
110 - NewListSections(sections: {
111 - "": widget.transactionDetailsViewModel.items
112 - .map((item) {
113 - if (item.value.isEmpty) return null;
112 + Padding(
113 + padding: const EdgeInsets.all(16),
114 + child: Column(
115 + spacing: 12,
116 + children: [
117 + NewListSections(
118 + sections: {
119 + "": widget.transactionDetailsViewModel.items
120 + .map((item) {
121 + if (item.value.isEmpty) {
122 + return null;
123 + }
124
115 - final shouldBuildBottomWidget = item.value.length > 25;
125 + final shouldBuildBottomWidget = item.value.length > 25;
126
117 - return ListItemRegularRow(
118 - copyableText: item.value,
119 - showArrow: false,
120 - keyValue: ((item.key as ValueKey?)?.value as String?) ??
121 - item.title,
122 - label: item.title,
123 - trailingWidget: shouldBuildBottomWidget
124 - ? null
125 - : _buildTrailingWIdget(item),
126 - bottomWidget: shouldBuildBottomWidget
127 - ? _buildBottomWidget(item)
128 - : null);
129 - })
130 - .whereType<ListItem>()
131 - .toList(),
132 - }),
133 - Container(
134 - decoration: BoxDecoration(
127 + return ListItemRegularRow(
128 + copyableText: item.value,
129 + showArrow: false,
130 + keyValue: ((item.key as ValueKey?)?.value as String?) ??
131 + item.title,
132 + label: item.title,
133 + trailingWidget: shouldBuildBottomWidget
134 + ? null
135 + : _buildTrailingWidget(item),
136 + bottomWidget: shouldBuildBottomWidget
137 + ? _buildBottomWidget(item)
138 + : null,
139 + );
140 + })
141 + .whereType<ListItem>()
142 + .toList(),
143 + },
144 + ),
145 + Container(
146 + decoration: BoxDecoration(
147 borderRadius: BorderRadius.circular(20),
136 - color: Theme.of(context).colorScheme.surfaceContainer),
137 - child: Padding(
138 - padding: const EdgeInsets.all(12.0),
139 - child: Column(
140 - spacing: 8,
141 - crossAxisAlignment: CrossAxisAlignment.start,
142 - children: [
143 - Text(S.of(context).note),
144 - TextField(
145 - focusNode: noteFocusNode,
146 - controller: noteController,
147 - decoration: InputDecoration(
148 + color: Theme.of(context).colorScheme.surfaceContainer,
149 + ),
150 + child: Padding(
151 + padding: const EdgeInsets.all(12),
152 + child: Column(
153 + spacing: 8,
154 + crossAxisAlignment: CrossAxisAlignment.start,
155 + children: [
156 + Text(S.of(context).note),
157 + TextField(
158 + focusNode: noteFocusNode,
159 + controller: noteController,
160 + decoration: InputDecoration(
161 hintText: S.of(context).add_a_note,
162 border: InputBorder.none,
163 focusedBorder: InputBorder.none,
164 enabledBorder: InputBorder.none,
165 contentPadding: EdgeInsets.zero,
153 - isDense: true),
154 - )
155 - ],
166 + isDense: true,
167 + ),
168 + ),
169 + ],
170 + ),
171 ),
172 ),
158 - ),
159 - Observer(
160 - builder: (_) => NewListSections(sections: {
161 - "view tx": [
162 - ListItemRegularRow(
163 - keyValue: "view tx on",
164 - label:
165 - widget.transactionDetailsViewModel.explorerDescription,
166 - onTap: widget.transactionDetailsViewModel.launchExplorer,
167 - foregroundColor: Theme.of(context).colorScheme.primary,
168 - trailingIconPath: "assets/new-ui/link_arrow.svg",
169 - trailingIconSize: 8)
170 - ],
171 - if (widget.transactionDetailsViewModel.canReplaceByFee)
172 - "rbf": [
173 - ListItemRegularRow(
174 - keyValue: "replace by fee",
175 - label: S.of(context).bump_fee,
176 - onTap: () {
177 - Navigator.of(context)
178 - .pushNamed(Routes.bumpFeePage, arguments: [
179 - widget.transactionDetailsViewModel.transactionInfo,
180 - widget.transactionDetailsViewModel.rawTransaction
181 - ]);
182 - })
183 - ]
184 - }),
185 - )
186 - ],
173 + Observer(
174 + builder: (_) => NewListSections(
175 + sections: {
176 + "view tx": [
177 + ListItemRegularRow(
178 + keyValue: "view tx on",
179 + label: widget
180 + .transactionDetailsViewModel.explorerDescription,
181 + onTap: widget.transactionDetailsViewModel.launchExplorer,
182 + foregroundColor: Theme.of(context).colorScheme.primary,
183 + trailingIconPath: "assets/new-ui/link_arrow.svg",
184 + trailingIconSize: 8,
185 + ),
186 + ],
187 + if (widget.transactionDetailsViewModel.canReplaceByFee)
188 + "rbf": [
189 + ListItemRegularRow(
190 + keyValue: "replace by fee",
191 + label: S.of(context).bump_fee,
192 + onTap: () {
193 + Navigator.of(context).pushNamed(
194 + Routes.bumpFeePage,
195 + arguments: [
196 + widget
197 + .transactionDetailsViewModel.transactionInfo,
198 + widget.transactionDetailsViewModel.rawTransaction,
199 + ],
200 + );
201 + },
202 + ),
203 + ],
204 + },
205 + ),
206 + ),
207 + ],
208 + ),
209 ),
188 - ),
189 - SizedBox(height: MediaQuery.of(context).viewPadding.bottom)
190 - ],
210 + SizedBox(height: MediaQuery.of(context).viewPadding.bottom),
211 + ],
212 + ),
213 ),
214 ),
193 - )
194 - ],
215 + ],
216 + ),
217 ),
218 ),
219 ),
198 - ),
199 - );
200 - }
220 + );
221
202 - Widget _buildTrailingWIdget(TransactionDetailsListItem item) {
203 - return Padding(
204 - padding: const EdgeInsets.symmetric(vertical: 4.0),
205 - child: switch (item.runtimeType) {
206 - ConfirmationsListItem => Row(
207 - children: [
208 - Text((item as ConfirmationsListItem).current.toString(),
209 - style: TextStyle(color: Theme.of(context).colorScheme.primary)),
210 - if (item.needed > 0)
211 - Text("/${item.needed}",
212 - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant))
213 - ],
214 - ),
215 - _ => Text(
216 - item.value,
217 - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
218 - )
219 - },
220 - );
221 - }
222 + Widget _buildTrailingWidget(TransactionDetailsListItem item) => Padding(
223 + padding: const EdgeInsets.symmetric(vertical: 4),
224 + child: switch (item.runtimeType) {
225 + ConfirmationsListItem => Row(
226 + children: [
227 + Text(
228 + (item as ConfirmationsListItem).current.toString(),
229 + style: TextStyle(color: Theme.of(context).colorScheme.primary),
230 + ),
231 + if (item.needed > 0)
232 + Text(
233 + "/${item.needed}",
234 + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
235 + ),
236 + ],
237 + ),
238 + _ => Text(
239 + item.value,
240 + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
241 + )
242 + },
243 + );
244
245 Widget _buildBottomWidget(TransactionDetailsListItem item) {
246 return switch (item.runtimeType) {
lib/new-ui/widgets/send_page/l2_action_wallet_selector.dart
+55 -58
@@ -1,41 +1,41 @@
1 -import 'dart:async';
1 +import "dart:async";
2
3 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
4 -import 'package:cake_wallet/generated/i18n.dart';
5 -import 'package:cake_wallet/main.dart';
6 -import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
7 -import 'package:cake_wallet/new-ui/widgets/new_primary_button.dart';
8 -import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
9 -import 'package:cake_wallet/new-ui/widgets/send_page/l2_send_external_modal.dart';
10 -import 'package:cake_wallet/new-ui/widgets/send_page/send_address_input.dart';
11 -import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
12 -import 'package:cake_wallet/src/widgets/new_list_row/new_simple_checkbox.dart';
13 -import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
14 -import 'package:cake_wallet/view_model/send/send_view_model.dart';
15 -import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
16 -import 'package:cw_core/currency_for_wallet_type.dart';
17 -import 'package:cw_core/wallet_info.dart';
18 -import 'package:cw_core/wallet_type.dart';
19 -import 'package:flutter/cupertino.dart';
20 -import 'package:flutter/material.dart';
21 -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
3 +import "package:cake_wallet/bitcoin/bitcoin.dart";
4 +import "package:cake_wallet/generated/i18n.dart";
5 +import "package:cake_wallet/main.dart";
6 +import "package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart";
7 +import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
8 +import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
9 +import "package:cake_wallet/new-ui/widgets/send_page/l2_send_external_modal.dart";
10 +import "package:cake_wallet/new-ui/widgets/send_page/send_address_input.dart";
11 +import "package:cake_wallet/src/widgets/cake_image_widget.dart";
12 +import "package:cake_wallet/src/widgets/new_list_row/new_simple_checkbox.dart";
13 +import "package:cake_wallet/view_model/contact_list/contact_list_view_model.dart";
14 +import "package:cake_wallet/view_model/send/send_view_model.dart";
15 +import "package:cake_wallet/view_model/wallet_switcher_view_model.dart";
16 +import "package:cw_core/currency_for_wallet_type.dart";
17 +import "package:cw_core/wallet_info.dart";
18 +import "package:cw_core/wallet_type.dart";
19 +import "package:flutter/cupertino.dart";
20 +import "package:flutter/material.dart";
21 +import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
22
23 -enum l2actions { deposit, withdraw }
23 +enum L2Actions { deposit, withdraw }
24
25 class L2ActionWalletSelector extends StatefulWidget {
26 const L2ActionWalletSelector({
27 - super.key,
27 required this.showOtherWallets,
28 required this.sendViewModel,
29 required this.action,
30 required this.onSendInitiated,
31 required this.contactListViewModel,
32 required this.walletSwitcherViewModel,
33 + super.key,
34 });
35
36 final bool showOtherWallets;
37 final SendViewModel sendViewModel;
38 - final l2actions action;
38 + final L2Actions action;
39 final VoidCallback onSendInitiated;
40 final ContactListViewModel contactListViewModel;
41 final WalletSwitcherViewModel walletSwitcherViewModel;
@@ -56,12 +56,18 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
56 super.initState();
57 if (widget.showOtherWallets) {
58 () async {
59 - items.addAll((await WalletInfo.getAll()).where((item) =>
60 - item.type == widget.sendViewModel.walletType && item.hardwareWalletType == null));
59 + items.addAll(
60 + (await WalletInfo.getAll()).where(
61 + (item) =>
62 + item.type == widget.sendViewModel.walletType && item.hardwareWalletType == null,
63 + ),
64 + );
65 items.sort((a, b) {
62 - if (a.name == widget.sendViewModel.wallet.name)
66 + if (a.name == widget.sendViewModel.wallet.name) {
67 return -1;
64 - else if (b.name == widget.sendViewModel.wallet.name) return 1;
68 + } else if (b.name == widget.sendViewModel.wallet.name) {
69 + return 1;
70 + }
71 return 0;
72 });
73 setState(() {});
@@ -80,16 +86,16 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
86 mainAxisSize: MainAxisSize.min,
87 children: [
88 ModalTopBar(
83 - title: widget.action == l2actions.deposit
89 + title: widget.action == L2Actions.deposit
90 ? "${S.of(context).send_from}..."
91 : "${S.of(context).receive_to}...",
86 - leadingIcon: Icon(Icons.arrow_back_ios_new),
92 + leadingIcon: const Icon(Icons.arrow_back_ios_new),
93 onLeadingPressed: Navigator.of(context).pop,
94 ),
95 Flexible(
96 child: SafeArea(
97 child: Padding(
92 - padding: const EdgeInsets.symmetric(horizontal: 16.0),
98 + padding: const EdgeInsets.symmetric(horizontal: 16),
99 child: Column(
100 spacing: 12,
101 mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -123,29 +129,17 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
129 itemBuilder: (context, index) {
130 final item = items[index];
131 return Padding(
126 - padding: const EdgeInsets.symmetric(vertical: 4.0),
132 + padding: const EdgeInsets.symmetric(vertical: 4),
133 child: WalletRow(
134 currencyIconPath: getCryptoCurrencyIconForWalletListItem(item.type),
135 walletName: item.name,
136 isCurrent: item.name == widget.sendViewModel.wallet.name,
137 isSelected: _selectedWalletIndex == index && !textEntered,
132 - onTap: () async {
138 + onTap: () {
139 setState(() {
140 addressController.text = "";
141 _selectedWalletIndex = index;
142 });
137 - // if (widget.action == l2actions.withdraw) {
138 - // widget.sendViewModel.outputs.first.address = item.address;
139 - // widget.onSendInitiated();
140 - // } else if (widget.action == l2actions.deposit) {
141 - // setState(() {
142 - // loadingWalletName = item.name;
143 - // });
144 - // await _handleChangeWallet(item);
145 - // widget.onSendInitiated();
146 - // setState(() {
147 - // loadingWalletName = null;
148 - // });
143 },
144 ),
145 );
@@ -198,7 +192,7 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
192 // ),
193 // ),
194 // ),
201 - if (widget.action == l2actions.withdraw)
195 + if (widget.action == L2Actions.withdraw)
196 Row(
197 children: [
198 Flexible(
@@ -215,12 +209,12 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
209 Column(
210 spacing: 8,
211 children: [
218 - if (widget.action == l2actions.deposit && widget.showOtherWallets) ...[
212 + if (widget.action == L2Actions.deposit && widget.showOtherWallets) ...[
213 Container(
214 height: 1,
215 color: Theme.of(context).colorScheme.onSurfaceVariant.withAlpha(64),
216 ),
223 - SizedBox(),
217 + const SizedBox(),
218 Container(
219 height: 52,
220 decoration: BoxDecoration(
@@ -232,7 +226,7 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
226 color: Colors.transparent,
227 child: InkWell(
228 borderRadius: BorderRadius.circular(16),
235 - onTap: () async {
229 + onTap: () {
230 Navigator.of(context, rootNavigator: true).pop();
231 showCupertinoModalBottomSheet(
232 context: navigatorKey.currentContext ?? context,
@@ -246,7 +240,7 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
240 );
241 },
242 child: Padding(
249 - padding: const EdgeInsets.symmetric(horizontal: 12.0),
243 + padding: const EdgeInsets.symmetric(horizontal: 12),
244 child: Row(
245 mainAxisAlignment: MainAxisAlignment.center,
246 spacing: 10,
@@ -275,19 +269,22 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
269 onPressed: () async {
270 if (widget.sendViewModel.wallet.type == WalletType.bitcoin ||
271 widget.sendViewModel.wallet.type == WalletType.litecoin) {
278 - if (widget.action == l2actions.withdraw) {
272 + if (widget.action == L2Actions.withdraw) {
273 widget.sendViewModel.outputs.first.address =
274 bitcoin!.getUnusedSegwitAddress(widget.sendViewModel.wallet)!;
275 }
276
277 if (widget.showOtherWallets) {
284 - if (widget.action == l2actions.deposit) {
278 + if (widget.action == L2Actions.deposit) {
279 await _handleChangeWallet(items[_selectedWalletIndex]);
280 } else {
287 - widget.sendViewModel.outputs.first.address =
288 - addressController.text.isNotEmpty
289 - ? addressController.text
290 - : items[_selectedWalletIndex].address;
281 + if (items[_selectedWalletIndex].name !=
282 + widget.sendViewModel.wallet.name) {
283 + widget.sendViewModel.outputs.first.address =
284 + addressController.text.isNotEmpty
285 + ? addressController.text
286 + : items[_selectedWalletIndex].address;
287 + }
288 }
289 }
290 widget.onSendInitiated();
@@ -298,7 +295,7 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
295 textColor: Theme.of(context).colorScheme.onPrimary,
296 isLoading: _isLoading,
297 ),
301 - SizedBox()
298 + const SizedBox()
299 ],
300 ),
301 ],
@@ -339,13 +336,13 @@ class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
336
337 class WalletRow extends StatelessWidget {
338 const WalletRow({
342 - super.key,
339 required this.currencyIconPath,
340 required this.walletName,
341 required this.onTap,
342 this.isLoading = false,
343 this.isCurrent = false,
344 this.isSelected,
345 + super.key,
346 });
347
348 final String currencyIconPath;
@@ -370,7 +367,7 @@ class WalletRow extends StatelessWidget {
367 borderRadius: BorderRadius.circular(16),
368 onTap: onTap,
369 child: Padding(
373 - padding: EdgeInsets.symmetric(horizontal: 12),
370 + padding: const EdgeInsets.symmetric(horizontal: 12),
371 child: Row(
372 mainAxisAlignment: MainAxisAlignment.spaceBetween,
373 children: [
lib/src/screens/transaction_details/rbf_details_page.dart
+158 -156
@@ -1,34 +1,36 @@
1 -import 'package:cake_wallet/core/execution_state.dart';
2 -import 'package:cake_wallet/generated/i18n.dart';
3 -import 'package:cake_wallet/src/screens/base_page.dart';
4 -import 'package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart';
5 -import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
6 -import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
7 -import 'package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart';
8 -import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
9 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
10 -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
11 -import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
12 -import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart';
13 -import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
14 -import 'package:cake_wallet/src/widgets/list_row.dart';
15 -import 'package:cake_wallet/src/widgets/primary_button.dart';
16 -import 'package:cake_wallet/src/widgets/standard_expandable_list.dart';
17 -import 'package:cake_wallet/src/widgets/standard_list.dart';
18 -import 'package:cake_wallet/src/widgets/standard_picker_list.dart';
19 -import 'package:cake_wallet/utils/show_bar.dart';
20 -import 'package:cake_wallet/utils/show_pop_up.dart';
21 -import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
22 -import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
23 -import 'package:flutter/material.dart';
24 -import 'package:flutter/services.dart';
25 -import 'package:flutter_mobx/flutter_mobx.dart';
26 -import 'package:mobx/mobx.dart';
1 +import "package:cake_wallet/core/execution_state.dart";
2 +import "package:cake_wallet/generated/i18n.dart";
3 +import "package:cake_wallet/src/screens/base_page.dart";
4 +import "package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart";
5 +import "package:cake_wallet/src/screens/transaction_details/standart_list_item.dart";
6 +import "package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart";
7 +import "package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart";
8 +import "package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart";
9 +import "package:cake_wallet/src/widgets/alert_with_one_action.dart";
10 +import "package:cake_wallet/src/widgets/alert_with_two_actions.dart";
11 +import "package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart";
12 +import "package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart";
13 +import "package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart";
14 +import "package:cake_wallet/src/widgets/list_row.dart";
15 +import "package:cake_wallet/src/widgets/primary_button.dart";
16 +import "package:cake_wallet/src/widgets/standard_expandable_list.dart";
17 +import "package:cake_wallet/src/widgets/standard_list.dart";
18 +import "package:cake_wallet/src/widgets/standard_picker_list.dart";
19 +import "package:cake_wallet/utils/show_bar.dart";
20 +import "package:cake_wallet/utils/show_pop_up.dart";
21 +import "package:cake_wallet/view_model/send/send_view_model_state.dart";
22 +import "package:cake_wallet/view_model/transaction_details_view_model.dart";
23 +import "package:flutter/material.dart";
24 +import "package:flutter/services.dart";
25 +import "package:flutter_mobx/flutter_mobx.dart";
26 +import "package:mobx/mobx.dart";
27
28 class RBFDetailsPage extends BasePage {
29 RBFDetailsPage({required this.transactionDetailsViewModel, required this.rawTransaction}) {
30 transactionDetailsViewModel.addBumpFeesListItems(
31 - transactionDetailsViewModel.transactionInfo, rawTransaction);
31 + transactionDetailsViewModel.transactionInfo,
32 + rawTransaction,
33 + );
34 }
35
36 @override
@@ -46,70 +48,70 @@ class RBFDetailsPage extends BasePage {
48 children: [
49 Expanded(
50 child: SectionStandardList(
49 - sectionCount: 1,
50 - itemCounter: (int _) => transactionDetailsViewModel.RBFListItems.length,
51 - itemBuilder: (__, index) {
52 - final item = transactionDetailsViewModel.RBFListItems[index];
53 -
54 - if (item is StandartListItem) {
55 - return GestureDetector(
56 - onTap: () {
57 - Clipboard.setData(ClipboardData(text: item.value));
58 - showBar<void>(context, S.of(context).transaction_details_copied(item.title));
59 - },
60 - child: ListRow(title: '${item.title}:', value: item.value),
61 - );
62 - }
63 -
64 - if (item is StandardExpandableListItem) {
65 - return StandardExpandableList(
66 - title: '${item.title}: ${item.expandableItems.length}',
67 - expandableItems: item.expandableItems,
68 - );
69 - }
70 -
71 - if (item is StandardPickerListItem) {
72 - return StandardPickerList(
73 - title: item.title,
74 - value: item.value,
75 - items: item.items,
76 - displayItem: item.displayItem,
77 - onSliderChanged: item.onSliderChanged,
78 - onItemSelected: item.onItemSelected,
79 - selectedIdx: item.selectedIdx,
80 - customItemIndex: item.customItemIndex,
81 - customValue: item.customValue,
82 - maxValue: item.maxValue,
83 - );
84 - }
85 -
86 - if (item is TextFieldListItem) {
87 - return TextFieldListRow(
88 - title: item.title,
89 - value: item.value,
90 - onSubmitted: item.onSubmitted,
91 - );
92 - }
93 -
94 - return Container();
95 - }),
51 + sectionCount: 1,
52 + itemCounter: (_) => transactionDetailsViewModel.rbfListItems.length,
53 + itemBuilder: (__, index) {
54 + final item = transactionDetailsViewModel.rbfListItems[index];
55 +
56 + if (item is StandartListItem) {
57 + return GestureDetector(
58 + onTap: () {
59 + Clipboard.setData(ClipboardData(text: item.value));
60 + showBar<void>(context, S.of(context).transaction_details_copied(item.title));
61 + },
62 + child: ListRow(title: "${item.title}:", value: item.value),
63 + );
64 + }
65 +
66 + if (item is StandardExpandableListItem) {
67 + return StandardExpandableList(
68 + title: "${item.title}: ${item.expandableItems.length}",
69 + expandableItems: item.expandableItems,
70 + );
71 + }
72 +
73 + if (item is StandardPickerListItem) {
74 + return StandardPickerList(
75 + title: item.title,
76 + value: item.value,
77 + items: item.items,
78 + displayItem: item.displayItem,
79 + onSliderChanged: item.onSliderChanged,
80 + onItemSelected: item.onItemSelected,
81 + selectedIdx: item.selectedIdx,
82 + customItemIndex: item.customItemIndex,
83 + customValue: item.customValue,
84 + maxValue: item.maxValue,
85 + );
86 + }
87 +
88 + if (item is TextFieldListItem) {
89 + return TextFieldListRow(
90 + title: item.title,
91 + value: item.value,
92 + onSubmitted: item.onSubmitted,
93 + );
94 + }
95 +
96 + return Container();
97 + },
98 + ),
99 ),
100 Padding(
98 - padding: const EdgeInsets.all(24),
99 - child: Observer(
100 - builder: (_) => LoadingPrimaryButton(
101 - onPressed: () async {
102 - transactionDetailsViewModel
103 - .replaceByFee(transactionDetailsViewModel.newFee.toString());
104 - },
105 - text: S.of(context).send,
106 - isLoading:
107 - transactionDetailsViewModel.sendViewModel.state is IsExecutingState,
108 - isDisabled: transactionDetailsViewModel.sendViewModel.state
109 - is ExecutedSuccessfullyState,
110 - color: Theme.of(context).colorScheme.primary,
111 - textColor: Theme.of(context).colorScheme.onPrimary,
112 - ))),
101 + padding: const EdgeInsets.all(24),
102 + child: Observer(
103 + builder: (_) => LoadingPrimaryButton(
104 + onPressed: () => transactionDetailsViewModel
105 + .replaceByFee(transactionDetailsViewModel.newFee.toString()),
106 + text: S.of(context).send,
107 + isLoading: transactionDetailsViewModel.sendViewModel.state is IsExecutingState,
108 + isDisabled:
109 + transactionDetailsViewModel.sendViewModel.state is ExecutedSuccessfullyState,
110 + color: Theme.of(context).colorScheme.primary,
111 + textColor: Theme.of(context).colorScheme.onPrimary,
112 + ),
113 + ),
114 + ),
115 ],
116 );
117 }
@@ -121,7 +123,7 @@ class RBFDetailsPage extends BasePage {
123 return;
124 }
125
124 - reaction((_) => transactionDetailsViewModel.sendViewModel.state, (ExecutionState state) {
126 + reaction((_) => transactionDetailsViewModel.sendViewModel.state, (state) {
127 if (state is! IsExecutingState &&
128 loadingBottomSheetContext != null &&
129 loadingBottomSheetContext!.mounted) {
@@ -131,35 +133,35 @@ class RBFDetailsPage extends BasePage {
133 if (state is FailureState) {
134 WidgetsBinding.instance.addPostFrameCallback((_) {
135 showPopUp<void>(
134 - context: context,
135 - builder: (BuildContext popupContext) {
136 - return AlertWithOneAction(
137 - alertTitle: S.of(popupContext).error,
138 - alertContent: state.error,
139 - buttonText: S.of(popupContext).ok,
140 - buttonAction: () => Navigator.of(popupContext).pop());
141 - });
136 + context: context,
137 + builder: (popupContext) => AlertWithOneAction(
138 + alertTitle: S.of(popupContext).error,
139 + alertContent: state.error,
140 + buttonText: S.of(popupContext).ok,
141 + buttonAction: () => Navigator.of(popupContext).pop(),
142 + ),
143 + );
144 });
145 }
146 if (state is AwaitingConfirmationState) {
147 WidgetsBinding.instance.addPostFrameCallback((_) {
148 showPopUp<void>(
147 - context: context,
148 - builder: (BuildContext popupContext) {
149 - return AlertWithTwoActions(
150 - alertTitle: state.title ?? '',
151 - alertContent: state.message ?? '',
152 - rightButtonText: S.of(context).ok,
153 - leftButtonText: S.of(context).cancel,
154 - actionRightButton: () {
155 - state.onConfirm?.call();
156 - Navigator.of(popupContext).pop();
157 - },
158 - actionLeftButton: () {
159 - state.onCancel?.call();
160 - Navigator.of(popupContext).pop();
161 - });
162 - });
149 + context: context,
150 + builder: (popupContext) => AlertWithTwoActions(
151 + alertTitle: state.title ?? "",
152 + alertContent: state.message ?? "",
153 + rightButtonText: S.of(context).ok,
154 + leftButtonText: S.of(context).cancel,
155 + actionRightButton: () {
156 + state.onConfirm?.call();
157 + Navigator.of(popupContext).pop();
158 + },
159 + actionLeftButton: () {
160 + state.onCancel?.call();
161 + Navigator.of(popupContext).pop();
162 + },
163 + ),
164 + );
165 });
166 }
167
@@ -169,7 +171,7 @@ class RBFDetailsPage extends BasePage {
171 showModalBottomSheet<void>(
172 context: context,
173 isDismissible: false,
172 - builder: (BuildContext context) {
174 + builder: (context) {
175 loadingBottomSheetContext = context;
176 return LoadingBottomSheet(
177 titleText: S.of(context).generating_transaction,
@@ -187,42 +189,42 @@ class RBFDetailsPage extends BasePage {
189 context: context,
190 isDismissible: false,
191 isScrollControlled: true,
190 - builder: (BuildContext bottomSheetContext) {
191 - return ConfirmSendingBottomSheet(
192 - key: ValueKey('rbf_confirm_sending_bottom_sheet'),
193 - titleText: S.of(bottomSheetContext).confirm_transaction,
194 - isSlideActionEnabled: transactionDetailsViewModel.sendViewModel.isReadyForSend,
195 - walletType: transactionDetailsViewModel.sendViewModel.walletType,
196 - titleIconPath:
197 - transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
198 - currency: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency,
199 - amount: S.of(bottomSheetContext).send_amount,
200 - amountValue:
201 - transactionDetailsViewModel.sendViewModel.pendingTransaction!.amountFormatted,
202 - fiatAmountValue: transactionDetailsViewModel
203 - .sendViewModel.pendingTransactionFiatAmountFormatted,
204 - fee: S.of(bottomSheetContext).send_fee,
205 - feeValue:
206 - transactionDetailsViewModel.sendViewModel.pendingTransaction!.feeFormatted,
207 - feeFiatAmount: transactionDetailsViewModel
208 - .sendViewModel.pendingTransactionFeeFiatAmountFormatted,
209 - outputs: transactionDetailsViewModel.sendViewModel.outputs,
210 - footerType: FooterType.slideActionButton,
211 - accessibleNavigationModeSlideActionButtonText: S.of(context).send,
212 - onSlideActionComplete: () async {
213 - Navigator.of(bottomSheetContext).pop();
214 - await transactionDetailsViewModel.sendViewModel.commitTransaction(context);
215 - try {
192 + builder: (bottomSheetContext) => ConfirmSendingBottomSheet(
193 + key: const ValueKey("rbf_confirm_sending_bottom_sheet"),
194 + titleText: S.of(bottomSheetContext).confirm_transaction,
195 + isSlideActionEnabled: transactionDetailsViewModel.sendViewModel.isReadyForSend,
196 + walletType: transactionDetailsViewModel.sendViewModel.walletType,
197 + titleIconPath:
198 + transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
199 + currency: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency,
200 + amount: S.of(bottomSheetContext).send_amount,
201 + amountValue:
202 + transactionDetailsViewModel.sendViewModel.pendingTransaction!.amountFormatted,
203 + fiatAmountValue:
204 + transactionDetailsViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
205 + fee: S.of(bottomSheetContext).send_fee,
206 + feeValue:
207 + transactionDetailsViewModel.sendViewModel.pendingTransaction!.feeFormatted,
208 + feeFiatAmount: transactionDetailsViewModel
209 + .sendViewModel.pendingTransactionFeeFiatAmountFormatted,
210 + outputs: transactionDetailsViewModel.sendViewModel.outputs,
211 + footerType: FooterType.slideActionButton,
212 + accessibleNavigationModeSlideActionButtonText: S.of(context).send,
213 + onSlideActionComplete: () async {
214 + Navigator.of(bottomSheetContext).pop();
215 + await transactionDetailsViewModel.sendViewModel.commitTransaction(context);
216 + try {
217 + if (bottomSheetContext.mounted) {
218 Navigator.of(bottomSheetContext).pop();
217 - } catch (_) {}
218 - },
219 - change: transactionDetailsViewModel.sendViewModel.pendingTransaction!.change,
220 - amountParsingProxy: transactionDetailsViewModel.sendViewModel.amountParsingProxy,
221 - );
222 - },
219 + }
220 + } catch (_) {}
221 + },
222 + change: transactionDetailsViewModel.sendViewModel.pendingTransaction!.change,
223 + amountParsingProxy: transactionDetailsViewModel.sendViewModel.amountParsingProxy,
224 + ),
225 );
226 if (result == null) {
225 - transactionDetailsViewModel.sendViewModel.dismissTransaction();
227 + await transactionDetailsViewModel.sendViewModel.dismissTransaction();
228 }
229 }
230 });
@@ -232,14 +234,14 @@ class RBFDetailsPage extends BasePage {
234 WidgetsBinding.instance.addPostFrameCallback((_) {
235 if (context.mounted) {
236 showPopUp<void>(
235 - context: context,
236 - builder: (BuildContext popupContext) {
237 - return AlertWithOneAction(
238 - alertTitle: S.of(popupContext).sending,
239 - alertContent: S.of(popupContext).transaction_sent,
240 - buttonText: S.of(popupContext).ok,
241 - buttonAction: () => Navigator.of(popupContext).pop());
242 - });
237 + context: context,
238 + builder: (popupContext) => AlertWithOneAction(
239 + alertTitle: S.of(popupContext).sending,
240 + alertContent: S.of(popupContext).transaction_sent,
241 + buttonText: S.of(popupContext).ok,
242 + buttonAction: () => Navigator.of(popupContext).pop(),
243 + ),
244 + );
245 }
246 });
247 }
lib/view_model/transaction_details_view_model.dart
+275 -251
@@ -1,50 +1,50 @@
1 -import 'package:cake_wallet/reactions/wallet_connect.dart';
2 -import 'package:cake_wallet/solana/solana.dart';
3 -import 'package:cake_wallet/src/screens/transaction_details/address_list_item.dart';
4 -import 'package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart';
5 -import 'package:cake_wallet/store/app_store.dart';
6 -import 'package:cake_wallet/core/address_validator.dart';
7 -import 'package:cake_wallet/tron/tron.dart';
8 -import 'package:cake_wallet/zano/zano.dart';
9 -import 'package:cw_core/crypto_amount_format.dart';
10 -import 'package:cw_core/crypto_currency.dart';
11 -import 'package:cw_core/currency_for_wallet_type.dart';
12 -import 'package:cw_core/wallet_base.dart';
13 -import 'package:cw_core/transaction_info.dart';
14 -import 'package:cw_core/wallet_type.dart';
15 -import 'package:cake_wallet/evm/evm.dart';
16 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
17 -import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
18 -import 'package:cake_wallet/entities/transaction_description.dart';
19 -import 'package:cake_wallet/generated/i18n.dart';
20 -import 'package:cake_wallet/monero/monero.dart';
21 -import 'package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart';
22 -import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
23 -import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
24 -import 'package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart';
25 -import 'package:cake_wallet/utils/date_formatter.dart';
26 -import 'package:cake_wallet/view_model/send/send_view_model.dart';
27 -import 'package:collection/collection.dart';
28 -import 'package:cw_core/transaction_direction.dart';
29 -import 'package:cw_core/transaction_priority.dart';
30 -import 'package:flutter/foundation.dart';
31 -import 'package:hive/hive.dart';
32 -import 'package:intl/intl.dart';
33 -import 'package:mobx/mobx.dart';
34 -import 'package:url_launcher/url_launcher.dart';
35 -
36 -part 'transaction_details_view_model.g.dart';
1 +import "package:cake_wallet/bitcoin/bitcoin.dart";
2 +import "package:cake_wallet/core/address_validator.dart";
3 +import "package:cake_wallet/entities/priority_for_wallet_type.dart";
4 +import "package:cake_wallet/entities/transaction_description.dart";
5 +import "package:cake_wallet/evm/evm.dart";
6 +import "package:cake_wallet/generated/i18n.dart";
7 +import "package:cake_wallet/monero/monero.dart";
8 +import "package:cake_wallet/reactions/wallet_connect.dart";
9 +import "package:cake_wallet/solana/solana.dart";
10 +import "package:cake_wallet/src/screens/transaction_details/address_list_item.dart";
11 +import "package:cake_wallet/src/screens/transaction_details/confirmations_list_item.dart";
12 +import "package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart";
13 +import "package:cake_wallet/src/screens/transaction_details/standart_list_item.dart";
14 +import "package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart";
15 +import "package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart";
16 +import "package:cake_wallet/store/app_store.dart";
17 +import "package:cake_wallet/tron/tron.dart";
18 +import "package:cake_wallet/view_model/send/send_view_model.dart";
19 +import "package:cake_wallet/zano/zano.dart";
20 +import "package:collection/collection.dart";
21 +import "package:cw_core/crypto_currency.dart";
22 +import "package:cw_core/currency_for_wallet_type.dart";
23 +import "package:cw_core/transaction_direction.dart";
24 +import "package:cw_core/transaction_info.dart";
25 +import "package:cw_core/transaction_priority.dart";
26 +import "package:cw_core/wallet_base.dart";
27 +import "package:cw_core/wallet_type.dart";
28 +import "package:flutter/foundation.dart";
29 +import "package:hive/hive.dart";
30 +import "package:intl/intl.dart";
31 +import "package:mobx/mobx.dart";
32 +import "package:url_launcher/url_launcher.dart";
33 +
34 +part "transaction_details_view_model.g.dart";
35
36 bool _trueFunc(_) => true;
37
38 /// We're adding a regex here so we can remove any already saved address that has the account in it.
39 /// In the refactor, we will make another separate variable for accounts and the UI would handle it as needed.
40 String _moneroRecipientAddressForDisplay(String raw, WalletType walletType) {
43 - if (walletType != WalletType.monero || raw.isEmpty) return raw;
41 + if (walletType != WalletType.monero || raw.isEmpty) {
42 + return raw;
43 + }
44
45 - final compact = raw.replaceAll(RegExp(r'\s'), '');
45 + final compact = raw.replaceAll(RegExp(r"\s"), "");
46 final match = RegExp(
47 - r'4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}',
47 + r"4[0-9a-zA-Z]{94}|8[0-9a-zA-Z]{94}|[0-9a-zA-Z]{106}",
48 caseSensitive: false,
49 ).firstMatch(compact);
50 return match?.group(0) ?? raw.trim();
@@ -55,6 +55,14 @@ bool isLightning(TransactionInfo tx) => (tx.additionalInfo["isLightning"] as boo
55 bool hasLightningPreimage(TransactionInfo tx) => (tx.additionalInfo["preimage"] as String?) != null;
56
57 class TxDetailRowDefinition {
58 + TxDetailRowDefinition({
59 + required this.keyString,
60 + required this.title,
61 + required this.valueGetter,
62 + this.applicable = _trueFunc,
63 + this.listItemBuilder = StandartListItem.new,
64 + });
65 +
66 final String keyString;
67 final String title;
68 final String Function(TransactionDetailsViewModelBase) valueGetter;
@@ -65,20 +73,13 @@ class TxDetailRowDefinition {
73 required Key key,
74 }) listItemBuilder;
75
68 - TxDetailRowDefinition(
69 - {required this.keyString,
70 - required this.title,
71 - required this.valueGetter,
72 - this.applicable = _trueFunc,
73 - this.listItemBuilder = StandartListItem.new});
74 -
76 static final List<TxDetailRowDefinition> defs = [
77 TxDetailRowDefinition(
77 - keyString: "standard_list_item_transaction_details_date_key",
78 - title: S.current.transaction_details_date,
79 - valueGetter: (vm) =>
80 - DateFormat("d MMMM yyyy, HH:mm", vm._appStore.settingsStore.languageCode)
81 - .format(vm.transactionInfo.date)),
78 + keyString: "standard_list_item_transaction_details_date_key",
79 + title: S.current.transaction_details_date,
80 + valueGetter: (vm) => DateFormat("d MMMM yyyy, HH:mm", vm._appStore.settingsStore.languageCode)
81 + .format(vm.transactionInfo.date),
82 + ),
83 TxDetailRowDefinition(
84 keyString: "standard_list_item_transaction_details_height_key",
85 title: S.current.transaction_details_height,
@@ -88,125 +89,137 @@ class TxDetailRowDefinition {
89 !isLightning(vm.transactionInfo),
90 ),
91 TxDetailRowDefinition(
91 - keyString: "standard_list_item_transaction_details_fee_key",
92 - title: S.current.transaction_details_fee,
93 - valueGetter: (vm) => vm.transactionInfo.fee!.toStringWithSymbol(),
94 - applicable: (vm) =>
95 - vm.wallet.type != WalletType.nano &&
96 - (vm.transactionInfo.fee?.toStringWithSymbol() ?? "").isNotEmpty),
92 + keyString: "standard_list_item_transaction_details_fee_key",
93 + title: S.current.transaction_details_fee,
94 + valueGetter: (vm) => vm.feeAmount,
95 + applicable: (vm) =>
96 + vm.wallet.type != WalletType.nano &&
97 + (vm.transactionInfo.fee?.toStringWithSymbol() ?? "").isNotEmpty,
98 + ),
99 TxDetailRowDefinition(
98 - keyString: "standard_list_item_transaction_confirmations_key",
99 - title: S.current.confirmations,
100 - valueGetter: (vm) => "${vm.transactionInfo.confirmations}/${vm.neededConfirmations}",
101 - applicable: (vm) =>
102 - [...electrumWalletTypes, ...evmWalletTypes, WalletType.zcash, WalletType.monero]
103 - .contains(vm.wallet.type) &&
104 - !isLightning(vm.transactionInfo),
105 - listItemBuilder: ConfirmationsListItem.new),
100 + keyString: "standard_list_item_transaction_confirmations_key",
101 + title: S.current.confirmations,
102 + valueGetter: (vm) => "${vm.transactionInfo.confirmations}/${vm.neededConfirmations}",
103 + applicable: (vm) =>
104 + [...electrumWalletTypes, ...evmWalletTypes, WalletType.zcash, WalletType.monero]
105 + .contains(vm.wallet.type) &&
106 + !isLightning(vm.transactionInfo),
107 + listItemBuilder: ConfirmationsListItem.new,
108 + ),
109 TxDetailRowDefinition(
107 - keyString: "standard_list_item_transaction_details_recipient_address_key",
108 - title: S.current.transaction_details_recipient_address,
109 - valueGetter: (vm) {
110 - String? ret = null;
111 -
112 - switch (vm.wallet.type) {
113 - case WalletType.monero:
114 - if (vm.transactionInfo.direction == TransactionDirection.incoming) {
115 - ret = monero!.getTransactionAddress(
116 - vm.wallet,
117 - vm.transactionInfo.additionalInfo['accountIndex'] as int,
118 - vm.transactionInfo.additionalInfo['addressIndex'] as int);
119 - }
120 - case WalletType.bitcoin:
121 - ret = (bitcoin!.getTransactionAddresses(vm.wallet, vm.transactionInfo) ?? [])
122 - .firstOrNull ??
123 - "";
124 - case WalletType.tron:
125 - if (vm.transactionInfo.to != null)
126 - ret = tron!.getTronBase58Address(vm.transactionInfo.to!, vm.wallet);
127 - default:
128 - break;
129 - }
130 - if (ret == null) {
131 - ret = vm.transactionInfo.to ?? "";
132 - }
133 - final resolvedAddress = _moneroRecipientAddressForDisplay(ret, vm.wallet.type);
134 - vm.isRecipientAddressShown = resolvedAddress.isNotEmpty;
135 - return resolvedAddress;
136 - },
137 - applicable: (vm) =>
138 - vm.showRecipientAddress &&
139 - (vm.transactionInfo.to != null ||
140 - [WalletType.monero, WalletType.tron].contains(vm.wallet.type) ||
141 - vm.wallet.type == WalletType.bitcoin &&
142 - vm.transactionInfo.direction == TransactionDirection.incoming),
143 - listItemBuilder: AddressListItem.new),
110 + keyString: "standard_list_item_transaction_details_recipient_address_key",
111 + title: S.current.transaction_details_recipient_address,
112 + valueGetter: (vm) {
113 + String? ret;
114 +
115 + switch (vm.wallet.type) {
116 + case WalletType.monero:
117 + if (vm.transactionInfo.direction == TransactionDirection.incoming) {
118 + ret = monero!.getTransactionAddress(
119 + vm.wallet,
120 + vm.transactionInfo.additionalInfo["accountIndex"] as int,
121 + vm.transactionInfo.additionalInfo["addressIndex"] as int,
122 + );
123 + }
124 + case WalletType.bitcoin:
125 + ret = (bitcoin!.getTransactionAddresses(vm.wallet, vm.transactionInfo) ?? [])
126 + .firstOrNull ??
127 + "";
128 + case WalletType.tron:
129 + if (vm.transactionInfo.to != null) {
130 + ret = tron!.getTronBase58Address(vm.transactionInfo.to!, vm.wallet);
131 + }
132 + default:
133 + break;
134 + }
135 + ret ??= vm.transactionInfo.to ?? "";
136 +
137 + final resolvedAddress = _moneroRecipientAddressForDisplay(ret, vm.wallet.type);
138 + vm.isRecipientAddressShown = resolvedAddress.isNotEmpty;
139 + return resolvedAddress;
140 + },
141 + applicable: (vm) =>
142 + vm.showRecipientAddress &&
143 + (vm.transactionInfo.to != null ||
144 + [WalletType.monero, WalletType.tron].contains(vm.wallet.type) ||
145 + vm.wallet.type == WalletType.bitcoin &&
146 + vm.transactionInfo.direction == TransactionDirection.incoming),
147 + listItemBuilder: AddressListItem.new,
148 + ),
149 TxDetailRowDefinition(
145 - keyString: "standard_list_item_transaction_details_source_address_key",
146 - title: S.current.transaction_details_source_address,
147 - valueGetter: (vm) {
148 - switch (vm.wallet.type) {
149 - case WalletType.tron:
150 - return tron!.getTronBase58Address(vm.transactionInfo.from!, vm.wallet);
151 - default:
152 - return vm.transactionInfo.from!;
153 - }
154 - },
155 - applicable: (vm) => vm.transactionInfo.from != null,
156 - listItemBuilder: AddressListItem.new),
150 + keyString: "standard_list_item_transaction_details_source_address_key",
151 + title: S.current.transaction_details_source_address,
152 + valueGetter: (vm) {
153 + switch (vm.wallet.type) {
154 + case WalletType.tron:
155 + return tron!.getTronBase58Address(vm.transactionInfo.from!, vm.wallet);
156 + default:
157 + return vm.transactionInfo.from!;
158 + }
159 + },
160 + applicable: (vm) => vm.transactionInfo.from != null,
161 + listItemBuilder: AddressListItem.new,
162 + ),
163 TxDetailRowDefinition(
158 - keyString: "standard_list_item_address_label_key",
159 - title: S.current.address_label,
160 - valueGetter: (vm) => monero!.getSubaddressLabel(
161 - vm.wallet,
162 - vm.transactionInfo.additionalInfo['accountIndex'] as int,
163 - vm.transactionInfo.additionalInfo['addressIndex'] as int),
164 - applicable: (vm) => vm.wallet.type == WalletType.monero),
164 + keyString: "standard_list_item_address_label_key",
165 + title: S.current.address_label,
166 + valueGetter: (vm) => monero!.getSubaddressLabel(
167 + vm.wallet,
168 + vm.transactionInfo.additionalInfo["accountIndex"] as int,
169 + vm.transactionInfo.additionalInfo["addressIndex"] as int,
170 + ),
171 + applicable: (vm) => vm.wallet.type == WalletType.monero,
172 + ),
173 TxDetailRowDefinition(
166 - keyString: "standard_list_item_transaction_key",
167 - title: S.current.transaction_key,
168 - valueGetter: (vm) {
169 - final descriptionKey =
170 - '${vm.transactionInfo.txHash}_${vm.wallet.walletAddresses.primaryAddress}';
171 -
172 - final description = vm.transactionDescriptionBox.values.firstWhere(
173 - (val) => val.id == descriptionKey || val.id == vm.transactionInfo.txHash,
174 - orElse: () => TransactionDescription(id: descriptionKey));
175 - return vm.transactionInfo.additionalInfo['key'] as String? ??
176 - description.transactionKey ??
177 - "";
178 - },
179 - applicable: (vm) => vm.wallet.type == WalletType.monero),
174 + keyString: "standard_list_item_transaction_key",
175 + title: S.current.transaction_key,
176 + valueGetter: (vm) {
177 + final descriptionKey =
178 + "${vm.transactionInfo.txHash}_${vm.wallet.walletAddresses.primaryAddress}";
179 +
180 + final description = vm.transactionDescriptionBox.values.firstWhere(
181 + (val) => val.id == descriptionKey || val.id == vm.transactionInfo.txHash,
182 + orElse: () => TransactionDescription(id: descriptionKey),
183 + );
184 + return vm.transactionInfo.additionalInfo["key"] as String? ??
185 + description.transactionKey ??
186 + "";
187 + },
188 + applicable: (vm) => vm.wallet.type == WalletType.monero,
189 + ),
190 TxDetailRowDefinition(
191 keyString: "standard_list_item_lightning_preimage",
192 title: S.current.transaction_preimage,
183 - valueGetter: (vm) => vm.transactionInfo.additionalInfo['preimage'] as String? ?? "",
193 + valueGetter: (vm) => vm.transactionInfo.additionalInfo["preimage"] as String? ?? "",
194 applicable: (vm) =>
195 hasLightningPreimage(vm.transactionInfo) && isLightning(vm.transactionInfo),
196 ),
197 TxDetailRowDefinition(
188 - keyString: "standard_list_item_transaction_confirmed_key",
189 - title: S.current.confirmed_tx,
190 - valueGetter: (vm) => (vm.transactionInfo.confirmations > 0).toString(),
191 - applicable: (vm) => vm.wallet.type == WalletType.nano),
198 + keyString: "standard_list_item_transaction_confirmed_key",
199 + title: S.current.confirmed_tx,
200 + valueGetter: (vm) => (vm.transactionInfo.confirmations > 0).toString(),
201 + applicable: (vm) => vm.wallet.type == WalletType.nano,
202 + ),
203 TxDetailRowDefinition(
193 - keyString: "standard_list_item_transaction_details_memo_key",
194 - title: S.current.memo,
195 - valueGetter: (vm) => vm.transactionInfo.additionalInfo['memo'] as String,
196 - applicable: (vm) =>
197 - vm.wallet.type == WalletType.zcash &&
198 - vm.transactionInfo.additionalInfo["memo"] != null),
204 + keyString: "standard_list_item_transaction_details_memo_key",
205 + title: S.current.memo,
206 + valueGetter: (vm) => vm.transactionInfo.additionalInfo["memo"] as String,
207 + applicable: (vm) =>
208 + vm.wallet.type == WalletType.zcash && vm.transactionInfo.additionalInfo["memo"] != null,
209 + ),
210 TxDetailRowDefinition(
200 - keyString: "standard_list_item_transaction_details_asset_id_key",
201 - title: "Asset ID",
202 - valueGetter: (vm) =>
203 - vm.transactionInfo.additionalInfo["assetId"] as String? ?? "Unknown asset id",
204 - applicable: (vm) => vm.wallet.type == WalletType.zano),
211 + keyString: "standard_list_item_transaction_details_asset_id_key",
212 + title: "Asset ID",
213 + valueGetter: (vm) =>
214 + vm.transactionInfo.additionalInfo["assetId"] as String? ?? "Unknown asset id",
215 + applicable: (vm) => vm.wallet.type == WalletType.zano,
216 + ),
217 TxDetailRowDefinition(
206 - keyString: "standard_list_item_transaction_details_comment_key",
207 - title: S.current.transaction_details_title,
208 - valueGetter: (vm) => vm.transactionInfo.additionalInfo['comment'] as String? ?? "",
209 - applicable: (vm) => vm.wallet.type == WalletType.zano),
218 + keyString: "standard_list_item_transaction_details_comment_key",
219 + title: S.current.transaction_details_title,
220 + valueGetter: (vm) => vm.transactionInfo.additionalInfo["comment"] as String? ?? "",
221 + applicable: (vm) => vm.wallet.type == WalletType.zano,
222 + ),
223 TxDetailRowDefinition(
224 keyString: "standard_list_item_transaction_details_id_key",
225 title: S.current.transaction_details_transaction_id,
@@ -227,7 +240,7 @@ abstract class TransactionDetailsViewModelBase with Store {
240 required this.sendViewModel,
241 this.canReplaceByFee = false,
242 }) : items = [],
230 - RBFListItems = [],
243 + rbfListItems = [],
244 newFee = 0,
245 isRecipientAddressShown = false,
246 _appStore = appStore,
@@ -236,19 +249,23 @@ abstract class TransactionDetailsViewModelBase with Store {
249
250 for (final def in TxDetailRowDefinition.defs) {
251 if (def.applicable(this)) {
239 - items.add(def.listItemBuilder(
252 + items.add(
253 + def.listItemBuilder(
254 title: def.title,
255 value: def.valueGetter(this),
242 - key: ValueKey(def.keyString)) as TransactionDetailsListItem);
256 + key: ValueKey(def.keyString),
257 + ) as TransactionDetailsListItem,
258 + );
259 }
260 }
261
262 _checkForRBF(tx);
263
248 - final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
264 + final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
265 final description = transactionDescriptionBox.values.firstWhere(
250 - (val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
251 - orElse: () => TransactionDescription(id: descriptionKey));
266 + (val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
267 + orElse: () => TransactionDescription(id: descriptionKey),
268 + );
269
270 if (showRecipientAddress && !isRecipientAddressShown) {
271 final recipientAddress = description.recipientAddress;
@@ -260,7 +277,7 @@ abstract class TransactionDetailsViewModelBase with Store {
277 AddressListItem(
278 title: S.current.transaction_details_recipient_address,
279 value: recipientAddressForDisplay,
263 - key: ValueKey('standard_list_item_${recipientAddressForDisplay}_key'),
280 + key: ValueKey("standard_list_item_${recipientAddressForDisplay}_key"),
281 ),
282 );
283 }
@@ -268,10 +285,11 @@ abstract class TransactionDetailsViewModelBase with Store {
285 }
286
287 void updateNote(String note) {
271 - final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
288 + final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
289 final description = transactionDescriptionBox.values.firstWhere(
273 - (val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
274 - orElse: () => TransactionDescription(id: descriptionKey));
290 + (val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
291 + orElse: () => TransactionDescription(id: descriptionKey),
292 + );
293
294 description.transactionNote = note;
295
@@ -283,9 +301,10 @@ abstract class TransactionDetailsViewModelBase with Store {
301 }
302
303 String get note {
286 - final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
304 + final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
305 final description = transactionDescriptionBox.values
288 - .firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash);
306 + .firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash,
307 + );
308 return description?.transactionNote ?? "";
309 }
310
@@ -296,7 +315,7 @@ abstract class TransactionDetailsViewModelBase with Store {
315 final AppStore _appStore;
316
317 final List<TransactionDetailsListItem> items;
299 - final List<TransactionDetailsListItem> RBFListItems;
318 + final List<TransactionDetailsListItem> rbfListItems;
319 bool showRecipientAddress;
320 bool isRecipientAddressShown;
321 int newFee;
@@ -304,9 +323,13 @@ abstract class TransactionDetailsViewModelBase with Store {
323 TransactionPriority? transactionPriority;
324
325 CryptoCurrency get transactionAsset {
307 - if (isEVMCompatibleChain(wallet.type)) return evm!.assetOfTransaction(wallet, transactionInfo);
326 + if (isEVMCompatibleChain(wallet.type)) {
327 + return evm!.assetOfTransaction(wallet, transactionInfo);
328 + }
329
309 - if (isLightning(transactionInfo)) return CryptoCurrency.btcln;
330 + if (isLightning(transactionInfo)) {
331 + return CryptoCurrency.btcln;
332 + }
333
334 return switch (wallet.type) {
335 WalletType.solana => solana!.assetOfTransaction(wallet, transactionInfo),
@@ -316,26 +339,38 @@ abstract class TransactionDetailsViewModelBase with Store {
339 };
340 }
341
319 - // TODO integrate these getters with the TransactionInfo object
342 + @computed
343 + String get transactionAmount =>
344 + _appStore.amountParsingProxy.asDisplayStringWithSymbol(transactionInfo.amount);
345 +
346 + @computed
347 + String get feeAmount =>
348 + _appStore.amountParsingProxy.asDisplayStringWithSymbol(transactionInfo.fee!);
349 +
350 + @computed
351 + String get transactionCopyAmount =>
352 + _appStore.amountParsingProxy.asDisplayString(transactionInfo.amount);
353 +
354 + // TODO(malik1004x): integrate these getters with the TransactionInfo object
355 String get formattedPendingStatus {
356 switch (wallet.type) {
357 case WalletType.monero:
358 case WalletType.haven:
359 case WalletType.zano:
360 if (transactionInfo.confirmations >= 0 && transactionInfo.confirmations < 10) {
326 - return ' (${transactionInfo.confirmations}/10)';
361 + return " (${transactionInfo.confirmations}/10)";
362 }
363 break;
364 case WalletType.wownero:
365 if (transactionInfo.confirmations >= 0 && transactionInfo.confirmations < 3) {
331 - return ' (${transactionInfo.confirmations}/3)';
366 + return " (${transactionInfo.confirmations}/3)";
367 }
368 break;
369 case WalletType.litecoin:
335 - bool isPegIn = (transactionInfo.additionalInfo["isPegIn"] as bool?) ?? false;
336 - bool isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
337 - bool fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
338 - String str = '';
370 + final isPegIn = (transactionInfo.additionalInfo["isPegIn"] as bool?) ?? false;
371 + final isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
372 + final fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
373 + String str = "";
374 if (transactionInfo.confirmations <= 0) {
375 str = S.current.pending;
376 }
@@ -352,10 +387,10 @@ abstract class TransactionDetailsViewModelBase with Store {
387 }
388 return str;
389 default:
355 - return '';
390 + return "";
391 }
392
358 - return '';
393 + return "";
394 }
395
396 String get formattedStatus {
@@ -369,7 +404,7 @@ abstract class TransactionDetailsViewModelBase with Store {
404 return formattedPendingStatus;
405 }
406
372 - return transactionInfo.isPending ? S.current.pending : '';
407 + return transactionInfo.isPending ? S.current.pending : "";
408 }
409
410 int get neededConfirmations {
@@ -381,9 +416,11 @@ abstract class TransactionDetailsViewModelBase with Store {
416 case WalletType.wownero:
417 return 3;
418 case WalletType.litecoin:
384 - bool isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
385 - bool fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
386 - if (isPegOut || fromPegOut) return 6;
419 + final isPegOut = (transactionInfo.additionalInfo["isPegOut"] as bool?) ?? false;
420 + final fromPegOut = (transactionInfo.additionalInfo["fromPegOut"] as bool?) ?? false;
421 + if (isPegOut || fromPegOut) {
422 + return 6;
423 + }
424 default:
425 return 0;
426 }
@@ -408,52 +445,56 @@ abstract class TransactionDetailsViewModelBase with Store {
445 final txId = transactionInfo.txHash;
446 if (wallet.chainId != null) {
447 final explorerUrl = evm!.getExplorerUrlForChainId(wallet.chainId!);
411 - if (explorerUrl != null) return '$explorerUrl/tx/${txId}';
448 + if (explorerUrl != null) {
449 + return "$explorerUrl/tx/${txId}";
450 + }
451 }
452
453 switch (wallet.type) {
454 case WalletType.monero:
416 - return 'https://monero.com/tx/${txId}';
455 + return "https://monero.com/tx/${txId}";
456 case WalletType.bitcoin:
418 - return 'https://mempool.cakewallet.com/${wallet.isTestnet ? "testnet/" : ""}tx/${txId}';
457 + return isLightning(transactionInfo)
458 + ? "https://sparkscan.io/tx/${txId}"
459 + : 'https://mempool.cakewallet.com/${wallet.isTestnet ? "testnet/" : ""}tx/${txId}';
460 case WalletType.litecoin:
461 return bitcoin!.txIsMweb(transactionInfo)
462 ? "https://www.mwebexplorer.com/blocks/block/${transactionInfo.height}"
422 - : 'https://blockchair.com/litecoin/transaction/${txId}';
463 + : "https://blockchair.com/litecoin/transaction/${txId}";
464 case WalletType.bitcoinCash:
424 - return 'https://blockchair.com/bitcoin-cash/transaction/${txId}';
465 + return "https://blockchair.com/bitcoin-cash/transaction/${txId}";
466 case WalletType.haven:
426 - return 'https://explorer.havenprotocol.org/search?value=${txId}';
467 + return "https://explorer.havenprotocol.org/search?value=${txId}";
468 case WalletType.ethereum:
428 - return 'https://etherscan.io/tx/${txId}';
469 + return "https://etherscan.io/tx/${txId}";
470 case WalletType.base:
430 - return 'https://basescan.org/tx/${txId}';
471 + return "https://basescan.org/tx/${txId}";
472 case WalletType.arbitrum:
432 - return 'https://arbiscan.io/tx/${txId}';
473 + return "https://arbiscan.io/tx/${txId}";
474 case WalletType.bsc:
434 - return 'https://bscscan.com/tx/${txId}';
475 + return "https://bscscan.com/tx/${txId}";
476 case WalletType.polygon:
436 - return 'https://polygonscan.com/tx/${txId}';
477 + return "https://polygonscan.com/tx/${txId}";
478 case WalletType.nano:
438 - return 'https://nanexplorer.com/nano/block/${txId}';
479 + return "https://nanexplorer.com/nano/block/${txId}";
480 case WalletType.banano:
440 - return 'https://nanexplorer.com/banano/block/${txId}';
481 + return "https://nanexplorer.com/banano/block/${txId}";
482 case WalletType.solana:
442 - return 'https://solscan.io/tx/${txId}';
483 + return "https://solscan.io/tx/${txId}";
484 case WalletType.tron:
444 - return 'https://tronscan.org/#/transaction/${txId}';
485 + return "https://tronscan.org/#/transaction/${txId}";
486 case WalletType.wownero:
446 - return 'https://explore.wownero.com/tx/${txId}';
487 + return "https://explore.wownero.com/tx/${txId}";
488 case WalletType.zano:
448 - return 'https://explorer.zano.org/transaction/${txId}';
489 + return "https://explorer.zano.org/transaction/${txId}";
490 case WalletType.decred:
491 return 'https://${wallet.isTestnet ? "testnet" : "dcrdata"}.decred.org/tx/${txId.split(':')[0]}';
492 case WalletType.dogecoin:
452 - return 'https://blockchair.com/dogecoin/transaction/${txId}';
493 + return "https://blockchair.com/dogecoin/transaction/${txId}";
494 case WalletType.zcash:
454 - return 'https://blockchair.com/zcash/transaction/${txId}';
495 + return "https://blockchair.com/zcash/transaction/${txId}";
496 case WalletType.none:
456 - return '';
497 + return "";
498 }
499 }
500
@@ -473,13 +514,17 @@ abstract class TransactionDetailsViewModelBase with Store {
514 : transactionInfo.outputAddresses!.length;
515
516 newFee = bitcoin!.getFeeAmountForPriority(
476 - wallet, bitcoin!.getBitcoinTransactionPriorityMedium(), inputsCount, outputsCount);
517 + wallet,
518 + bitcoin!.getBitcoinTransactionPriorityMedium(),
519 + inputsCount,
520 + outputsCount,
521 + );
522
478 - RBFListItems.add(
523 + rbfListItems.add(
524 StandartListItem(
525 title: S.current.old_fee,
481 - value: tx.fee?.toStringWithSymbol() ?? '0.0',
482 - key: ValueKey('standard_list_item_rbf_old_fee_key'),
526 + value: tx.fee?.toStringWithSymbol() ?? "0.0",
527 + key: const ValueKey("standard_list_item_rbf_old_fee_key"),
528 ),
529 );
530
@@ -488,32 +533,33 @@ abstract class TransactionDetailsViewModelBase with Store {
533 final recommendedRate = (transactionInfo.fee! / BigInt.from(size)) +
534 transactionInfo.fee!.copyWith(amount: BigInt.one);
535
491 - RBFListItems.add(
492 - StandartListItem(title: 'New recommended fee rate', value: '$recommendedRate sat/byte'));
536 + rbfListItems.add(
537 + StandartListItem(title: "New recommended fee rate", value: "$recommendedRate sat/byte"),
538 + );
539 }
540
541 final priorities = priorityForWalletType(wallet.type);
542 final selectedItem = priorities.indexOf(sendViewModel.feesViewModel.transactionPriority);
543 final customItem = priorities.firstWhereOrNull(
498 - (element) => element == sendViewModel.feesViewModel.bitcoinTransactionPriorityCustom);
544 + (element) => element == sendViewModel.feesViewModel.bitcoinTransactionPriorityCustom,
545 + );
546 final customItemIndex = customItem != null ? priorities.indexOf(customItem) : null;
547 final maxCustomFeeRate = sendViewModel.feesViewModel.maxCustomFeeRate?.toDouble();
548
502 - RBFListItems.add(
549 + rbfListItems.add(
550 StandardPickerListItem(
504 - key: ValueKey('standard_picker_list_item_transaction_priorities_key'),
551 + key: const ValueKey("standard_picker_list_item_transaction_priorities_key"),
552 title: S.current.estimated_new_fee,
506 - value: bitcoin!.formatterBitcoinAmountToString(amount: newFee) + ' ${wallet.currency}',
553 + value: "${bitcoin!.formatterBitcoinAmountToString(amount: newFee)} ${wallet.currency}",
554 items: priorityForWalletType(wallet.type),
555 customValue: _appStore.settingsStore.customBitcoinFeeRate.toDouble(),
556 maxValue: maxCustomFeeRate,
557 selectedIdx: selectedItem,
558 customItemIndex: customItemIndex ?? 0,
512 - displayItem: (dynamic priority, double sliderValue) =>
559 + displayItem: (dynamic priority, sliderValue) =>
560 sendViewModel.feesViewModel.displayFeeRate(priority, sliderValue.round()),
514 - onSliderChanged: (double newValue) =>
515 - setNewFee(value: newValue, priority: transactionPriority!),
516 - onItemSelected: (dynamic item, double sliderValue) {
561 + onSliderChanged: (newValue) => setNewFee(value: newValue, priority: transactionPriority!),
562 + onItemSelected: (dynamic item, sliderValue) {
563 transactionPriority = item as TransactionPriority;
564 return setNewFee(value: sliderValue, priority: transactionPriority!);
565 },
@@ -521,9 +567,9 @@ abstract class TransactionDetailsViewModelBase with Store {
567 );
568
569 if (transactionInfo.inputAddresses != null && transactionInfo.inputAddresses!.isNotEmpty) {
524 - RBFListItems.add(
570 + rbfListItems.add(
571 StandardExpandableListItem(
526 - key: ValueKey('standard_expandable_list_item_transaction_input_addresses_key'),
572 + key: const ValueKey("standard_expandable_list_item_transaction_input_addresses_key"),
573 title: S.current.inputs,
574 expandableItems: transactionInfo.inputAddresses!,
575 ),
@@ -532,17 +578,17 @@ abstract class TransactionDetailsViewModelBase with Store {
578
579 if (transactionInfo.outputAddresses != null && transactionInfo.outputAddresses!.isNotEmpty) {
580 final outputAddresses = transactionInfo.outputAddresses!.map((element) {
535 - if (element.contains('OP_RETURN:') && element.length > 40) {
536 - return element.substring(0, 40) + '...';
581 + if (element.contains("OP_RETURN:") && element.length > 40) {
582 + return "${element.substring(0, 40)}...";
583 }
584 return element;
585 }).toList();
586
541 - RBFListItems.add(
587 + rbfListItems.add(
588 StandardExpandableListItem(
589 title: S.current.outputs,
590 expandableItems: outputAddresses,
545 - key: ValueKey('standard_expandable_list_item_transaction_output_addresses_key'),
591 + key: const ValueKey("standard_expandable_list_item_transaction_output_addresses_key"),
592 ),
593 );
594 }
@@ -552,7 +598,7 @@ abstract class TransactionDetailsViewModelBase with Store {
598 Future<void> _checkForRBF(TransactionInfo tx) async {
599 if (wallet.type == WalletType.bitcoin &&
600 transactionInfo.direction == TransactionDirection.outgoing) {
555 - final descriptionKey = '${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}';
601 + final descriptionKey = "${transactionInfo.txHash}_${wallet.walletAddresses.primaryAddress}";
602 final description = transactionDescriptionBox.values
603 .firstWhereOrNull((val) => val.id == descriptionKey || val.id == transactionInfo.txHash);
604
@@ -569,45 +615,23 @@ abstract class TransactionDetailsViewModelBase with Store {
615 }
616 }
617
572 - String setNewFee({double? value, required TransactionPriority priority}) {
618 + String setNewFee({required TransactionPriority priority, double? value}) {
619 newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null
620 ? bitcoin!.feeAmountWithFeeRate(
621 wallet,
622 value.round(),
623 transactionInfo.inputAddresses?.length ?? 1,
578 - transactionInfo.outputAddresses?.length ?? 1)
624 + transactionInfo.outputAddresses?.length ?? 1,
625 + )
626 : bitcoin!.getFeeAmountForPriority(
627 wallet,
628 priority,
629 transactionInfo.inputAddresses?.length ?? 1,
583 - transactionInfo.outputAddresses?.length ?? 1);
630 + transactionInfo.outputAddresses?.length ?? 1,
631 + );
632
633 return bitcoin!.formatterBitcoinAmountToString(amount: newFee);
634 }
635
588 - String get formattedCryptoAmount {
589 - if (wallet.type == WalletType.bitcoin) {
590 - final crypto = isLightning(transactionInfo) ? CryptoCurrency.btcln : CryptoCurrency.btc;
591 - final amount = _appStore.amountParsingProxy
592 - .asDisplayString(transactionInfo.amount)
593 - .withMaxDecimals(8)
594 - .withLocalSeperator(_appStore.settingsStore.languageCode);
595 -
596 - return '$amount ${_appStore.amountParsingProxy.getCryptoSymbol(crypto)}';
597 - }
598 -
599 - return transactionInfo.amount.toStringWithSymbol();
600 - }
601 -
636 void replaceByFee(String newFee) => sendViewModel.replaceByFee(transactionInfo, newFee);
603 -
604 - @computed
605 - String get pendingTransactionFiatAmountValueFormatted => sendViewModel.isFiatDisabled
606 - ? ''
607 - : sendViewModel.pendingTransactionFiatAmount + ' ' + sendViewModel.fiat.title;
608 -
609 - @computed
610 - String get pendingTransactionFeeFiatAmountFormatted => sendViewModel.isFiatDisabled
611 - ? ''
612 - : sendViewModel.pendingTransactionFeeFiatAmount + ' ' + sendViewModel.fiat.title;
637 }
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+194 -163
@@ -1,59 +1,58 @@
1 -import 'dart:core';
2 -import 'dart:developer' as dev;
3 -
4 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
5 -import 'package:cake_wallet/core/address_resolver/yat/yat_store.dart';
6 -import 'package:cake_wallet/core/amount_parsing_proxy.dart';
7 -import 'package:cake_wallet/core/fiat_conversion_service.dart';
8 -import 'package:cake_wallet/core/wallet_change_listener_view_model.dart';
9 -import 'package:cake_wallet/decred/decred.dart';
10 -import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
11 -import 'package:cake_wallet/entities/fiat_api_mode.dart';
12 -import 'package:cake_wallet/entities/fiat_currency.dart';
13 -import 'package:cake_wallet/evm/evm.dart';
14 -import 'package:cake_wallet/generated/i18n.dart';
15 -import 'package:cake_wallet/monero/monero.dart';
16 -import 'package:cake_wallet/reactions/wallet_connect.dart';
17 -import 'package:cake_wallet/reactions/wallet_utils.dart';
18 -import 'package:cake_wallet/solana/solana.dart';
19 -import 'package:cake_wallet/store/app_store.dart';
20 -import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
21 -import 'package:cake_wallet/tron/tron.dart';
22 -import 'package:cake_wallet/utils/list_item.dart';
23 -import 'package:cake_wallet/utils/qr_util.dart';
24 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
25 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_hidden_list_header.dart';
26 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
27 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
28 -import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_util.dart';
29 -import 'package:cake_wallet/wownero/wownero.dart';
30 -import 'package:cake_wallet/zcash/zcash.dart';
31 -import 'package:cake_wallet/zano/zano.dart';
32 -import 'package:cw_core/crypto_currency.dart';
33 -import 'package:cw_core/currency.dart';
34 -import 'package:cw_core/currency_for_wallet_type.dart';
35 -import 'package:cw_core/erc20_token.dart';
36 -import 'package:cw_core/payment_uris.dart';
37 -import 'package:cw_core/spl_token.dart';
38 -import 'package:cw_core/tron_token.dart';
39 -import 'package:cw_core/wallet_type.dart';
40 -import 'package:mobx/mobx.dart';
41 -
42 -part 'wallet_address_list_view_model.g.dart';
1 +import "dart:core";
2 +import "dart:developer" as dev;
3 +
4 +import "package:cake_wallet/bitcoin/bitcoin.dart";
5 +import "package:cake_wallet/core/address_resolver/yat/yat_store.dart";
6 +import "package:cake_wallet/core/amount_parsing_proxy.dart";
7 +import "package:cake_wallet/core/fiat_conversion_service.dart";
8 +import "package:cake_wallet/core/wallet_change_listener_view_model.dart";
9 +import "package:cake_wallet/decred/decred.dart";
10 +import "package:cake_wallet/entities/auto_generate_subaddress_status.dart";
11 +import "package:cake_wallet/entities/fiat_api_mode.dart";
12 +import "package:cake_wallet/entities/fiat_currency.dart";
13 +import "package:cake_wallet/evm/evm.dart";
14 +import "package:cake_wallet/generated/i18n.dart";
15 +import "package:cake_wallet/monero/monero.dart";
16 +import "package:cake_wallet/reactions/wallet_connect.dart";
17 +import "package:cake_wallet/reactions/wallet_utils.dart";
18 +import "package:cake_wallet/solana/solana.dart";
19 +import "package:cake_wallet/store/app_store.dart";
20 +import "package:cake_wallet/store/dashboard/fiat_conversion_store.dart";
21 +import "package:cake_wallet/tron/tron.dart";
22 +import "package:cake_wallet/utils/list_item.dart";
23 +import "package:cake_wallet/utils/qr_util.dart";
24 +import "package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart";
25 +import "package:cake_wallet/view_model/wallet_address_list/wallet_address_hidden_list_header.dart";
26 +import "package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart";
27 +import "package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart";
28 +import "package:cake_wallet/view_model/wallet_address_list/wallet_address_util.dart";
29 +import "package:cake_wallet/wownero/wownero.dart";
30 +import "package:cake_wallet/zano/zano.dart";
31 +import "package:cake_wallet/zcash/zcash.dart";
32 +import "package:cw_core/crypto_currency.dart";
33 +import "package:cw_core/currency.dart";
34 +import "package:cw_core/currency_for_wallet_type.dart";
35 +import "package:cw_core/erc20_token.dart";
36 +import "package:cw_core/payment_uris.dart";
37 +import "package:cw_core/spl_token.dart";
38 +import "package:cw_core/tron_token.dart";
39 +import "package:cw_core/wallet_type.dart";
40 +import "package:mobx/mobx.dart";
41 +
42 +part "wallet_address_list_view_model.g.dart";
43
44 class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
45
46 abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
47 WalletAddressListViewModelBase({
48 - required AppStore appStore,
48 + required super.appStore,
49 required this.yatStore,
50 required this.fiatConversionStore,
51 }) : _baseItems = <ListItem>[],
52 selectedCurrency = appStore.wallet!.currency,
53 hasAccounts = [WalletType.monero, WalletType.wownero].contains(appStore.wallet!.type),
54 _appStore = appStore,
55 - receivePageOption = appStore.wallet!.walletAddresses.walletInfo.addressPageType ?? '',
56 - super(appStore: appStore) {
55 + receivePageOption = appStore.wallet!.walletAddresses.walletInfo.addressPageType ?? "" {
56 _init();
57 }
58
@@ -81,8 +80,12 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
80 CryptoCurrency? tokenCurrency;
81
82 @computed
84 - String get cryptoCurrencySymbol =>
85 - _appStore.amountParsingProxy.getCryptoSymbol(tokenCurrency ?? wallet.currency);
83 + String get cryptoCurrencySymbol => _appStore.amountParsingProxy.getCryptoSymbol(
84 + tokenCurrency ??
85 + (selectedCurrency is CryptoCurrency
86 + ? selectedCurrency as CryptoCurrency
87 + : wallet.currency),
88 + );
89
90 void setTokenCurrency(Currency curr) {
91 if (curr == wallet.currency || curr == CryptoCurrency.btcln) {
@@ -122,7 +125,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
125 _appStore.amountParsingProxy.useSatoshi(selectedCurrency as CryptoCurrency);
126
127 @observable
125 - String searchText = '';
128 + String searchText = "";
129
130 @computed
131 int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
@@ -131,30 +134,45 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
134 int get tokenCurrencyIndex => tokenCurrency == null ? 0 : tokenCurrencies.indexOf(tokenCurrency!);
135
136 @observable
134 - String _amount = '';
137 + String _amount = "";
138
139 @computed
137 - String get displayAmount => _appStore.amountParsingProxy
138 - .getDisplayCryptoAmount(_amount, tokenCurrency ?? wallet.currency);
140 + String get displayAmount => _appStore.amountParsingProxy.getDisplayCryptoAmount(
141 + _amount,
142 + tokenCurrency ??
143 + (selectedCurrency is CryptoCurrency
144 + ? selectedCurrency as CryptoCurrency
145 + : wallet.currency),
146 + );
147
148 // NOT PRECISE! just for display purposes.
149 @computed
150 String get fiatAmount {
143 - if (_amount.isEmpty) return "";
151 + if (_amount.isEmpty) {
152 + return "";
153 + }
154 +
155 var cryptoCurrency = tokenCurrency ?? wallet.currency;
145 - if (cryptoCurrency == CryptoCurrency.btcln) cryptoCurrency = CryptoCurrency.btc;
156 + if (cryptoCurrency == CryptoCurrency.btcln) {
157 + cryptoCurrency = CryptoCurrency.btc;
158 + }
159 +
160 if (selectedCurrency is FiatCurrency && _fiatRate != null) {
161 return selectedCurrencyFiatAmount;
162 }
163
150 - if (!fiatConversionStore.prices.containsKey(cryptoCurrency)) return "";
164 + if (!fiatConversionStore.prices.containsKey(cryptoCurrency)) {
165 + return "";
166 + }
167 final amount = double.tryParse(_amount) ?? 0;
168 return (amount * fiatConversionStore.prices[cryptoCurrency]!).toStringAsFixed(2);
169 }
170
171 @computed
172 String get selectedCurrencyFiatAmount {
157 - if (_fiatRate == null) return "";
173 + if (_fiatRate == null) {
174 + return "";
175 + }
176 final amount = double.tryParse(_amount) ?? 0;
177 return (amount * _fiatRate!).toStringAsFixed(2);
178 }
@@ -201,26 +219,29 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
219 PaymentURI get uri {
220 if (tokenCurrency != null && isEVMCompatibleChain(wallet.type)) {
221 return ERC681URI(
204 - chainId: wallet.chainId ?? 1,
205 - address: wallet.walletAddresses.address,
206 - amount: _amount,
207 - contractAddress: (tokenCurrency as Erc20Token).contractAddress);
222 + chainId: wallet.chainId ?? 1,
223 + address: wallet.walletAddresses.address,
224 + amount: _amount,
225 + contractAddress: (tokenCurrency! as Erc20Token).contractAddress,
226 + );
227 }
228 if (tokenCurrency is TronToken && wallet.type == WalletType.tron) {
229 return TronURI(
230 amount: _amount,
231 address: wallet.walletAddresses.address,
213 - contractAddress: (tokenCurrency as TronToken).contractAddress,
232 + contractAddress: (tokenCurrency! as TronToken).contractAddress,
233 );
234 }
235 if (tokenCurrency is SPLToken && wallet.type == WalletType.solana) {
236 return SolanaURI(
237 amount: _amount,
238 address: wallet.walletAddresses.address,
220 - contractAddress: (tokenCurrency as SPLToken).mintAddress,
239 + contractAddress: (tokenCurrency! as SPLToken).mintAddress,
240 );
241 }
223 - if (isLightning && _lnPaymentRequest != null) return _lnPaymentRequest!;
242 + if (isLightning && _lnPaymentRequest != null) {
243 + return _lnPaymentRequest!;
244 + }
245 return wallet.walletAddresses.getPaymentUri(_amount);
246 }
247
@@ -257,10 +278,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
278 final isPrimary = subaddress == primaryAddress;
279
280 return WalletAddressListItem(
260 - id: subaddress.id,
261 - isPrimary: isPrimary,
262 - name: subaddress.label,
263 - address: subaddress.address);
281 + id: subaddress.id,
282 + isPrimary: isPrimary,
283 + name: subaddress.label,
284 + address: subaddress.address,
285 + );
286 });
287 addressList.addAll(addressItems);
288 }
@@ -285,37 +307,37 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
307 addressList.addAll(addressItems);
308 addressList.add(WalletAddressListHeader(title: S.current.received));
309
288 - final receivedAddressItems =
289 - bitcoin!.getSilentPaymentReceivedAddresses(wallet).map((address) {
290 - return WalletAddressListItem(
291 - id: address.id,
292 - isPrimary: false,
293 - name: address.name,
294 - address: address.address,
295 - txCount: address.txCount,
296 - balance: _appStore.amountParsingProxy
297 - .getDisplayCryptoString(address.balance, walletTypeToCryptoCurrency(type)),
298 - isChange: address.isChange,
299 - isOneTimeReceiveAddress: true,
300 - derivationPath: address.derivationPath,
301 - );
302 - });
310 + final receivedAddressItems = bitcoin!.getSilentPaymentReceivedAddresses(wallet).map(
311 + (address) => WalletAddressListItem(
312 + id: address.id,
313 + isPrimary: false,
314 + name: address.name,
315 + address: address.address,
316 + txCount: address.txCount,
317 + balance: _appStore.amountParsingProxy
318 + .getDisplayCryptoString(address.balance, walletTypeToCryptoCurrency(type)),
319 + isChange: address.isChange,
320 + isOneTimeReceiveAddress: true,
321 + derivationPath: address.derivationPath,
322 + ),
323 + );
324 addressList.addAll(receivedAddressItems);
325 } else {
326 var addressItems = bitcoin!.getSubAddresses(wallet).map((subaddress) {
327 final isPrimary = subaddress.id == 0;
328
329 return WalletAddressListItem(
309 - id: subaddress.id,
310 - isPrimary: isPrimary,
311 - name: subaddress.name,
312 - address: subaddress.address,
313 - txCount: subaddress.txCount,
314 - balance: _appStore.amountParsingProxy
315 - .getDisplayCryptoString(subaddress.balance, walletTypeToCryptoCurrency(type)),
316 - isChange: subaddress.isChange,
317 - isLegacyDerivation: subaddress.isLegacyDerivation,
318 - derivationPath: subaddress.derivationPath);
330 + id: subaddress.id,
331 + isPrimary: isPrimary,
332 + name: subaddress.name,
333 + address: subaddress.address,
334 + txCount: subaddress.txCount,
335 + balance: _appStore.amountParsingProxy
336 + .getDisplayCryptoString(subaddress.balance, walletTypeToCryptoCurrency(type)),
337 + isChange: subaddress.isChange,
338 + isLegacyDerivation: subaddress.isLegacyDerivation,
339 + derivationPath: subaddress.derivationPath,
340 + );
341 });
342
343 // don't show all 1000+ mweb addresses:
@@ -346,11 +368,13 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
368 }
369
370 if (wallet.type == WalletType.nano) {
349 - addressList.add(WalletAddressListItem(
350 - isPrimary: true,
351 - name: null,
352 - address: wallet.walletAddresses.address,
353 - ));
371 + addressList.add(
372 + WalletAddressListItem(
373 + isPrimary: true,
374 + name: null,
375 + address: wallet.walletAddresses.address,
376 + ),
377 + );
378 }
379
380 if (wallet.type == WalletType.tron) {
@@ -361,29 +385,35 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
385
386 if (wallet.type == WalletType.decred) {
387 final addrInfos = decred!.getAddressInfos(wallet);
364 - addrInfos.forEach((info) {
388 + for (final info in addrInfos) {
389 addressList.add(
366 - new WalletAddressListItem(isPrimary: false, address: info.address, name: info.label));
367 - });
390 + WalletAddressListItem(isPrimary: false, address: info.address, name: info.label),
391 + );
392 + }
393 }
394
395 if (wallet.type == WalletType.zcash) {
396 final addrInfos = zcash!.getAddressInfos(wallet);
372 - addrInfos.forEach((info) {
397 + for (final info in addrInfos) {
398 addressList.add(
374 - new WalletAddressListItem(isPrimary: false, address: info.address, name: info.label));
375 - });
399 + WalletAddressListItem(isPrimary: false, address: info.address, name: info.label),
400 + );
401 + }
402 }
403
404 for (var i = 0; i < addressList.length; i++) {
379 - if (!(addressList[i] is WalletAddressListItem)) continue;
405 + if (addressList[i] is! WalletAddressListItem) {
406 + continue;
407 + }
408 final item = addressList[i] as WalletAddressListItem;
409 item.isHidden = wallet.walletAddresses.hiddenAddresses.contains(item.address) ||
410 (isElectrumWallet && item.isLegacyDerivation);
411 }
412
413 for (var i = 0; i < addressList.length; i++) {
386 - if (!(addressList[i] is WalletAddressListItem)) continue;
414 + if (addressList[i] is! WalletAddressListItem) {
415 + continue;
416 + }
417 (addressList[i] as WalletAddressListItem).isManual = wallet.walletAddresses.manualAddresses
418 .contains((addressList[i] as WalletAddressListItem).address);
419 }
@@ -395,25 +425,25 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
425 }
426
427 if (searchText.isNotEmpty) {
398 - return ObservableList.of(addressList.where((item) {
399 - if (item is WalletAddressListItem) {
400 - return item.address.toLowerCase().contains(searchText.toLowerCase());
401 - }
402 - return false;
403 - }));
428 + return ObservableList.of(
429 + addressList.where((item) {
430 + if (item is WalletAddressListItem) {
431 + return item.address.toLowerCase().contains(searchText.toLowerCase());
432 + }
433 + return false;
434 + }),
435 + );
436 }
437
438 return addressList;
439 }
440
441 @computed
410 - ObservableList<ListItem> get addressList {
411 - return _computeAddressList();
412 - }
442 + ObservableList<ListItem> get addressList => _computeAddressList();
443
444 List<ListItem> get forceRecomputeItems {
445 // necessary because the addressList contains non-observable items
416 - List<ListItem> recomputed = [];
446 + final recomputed = <ListItem>[];
447 recomputed.addAll(_baseItems);
448 recomputed.addAll(_computeAddressList());
449 return recomputed;
@@ -451,9 +481,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
481 case WalletType.wownero:
482 wownero!.getCurrentAccount(wallet).label;
483 default:
454 - return '';
484 + return "";
485 }
456 - return '';
486 + return "";
487 }
488
489 @computed
@@ -473,7 +503,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
503 WalletType.litecoin,
504 WalletType.decred,
505 WalletType.dogecoin,
476 - WalletType.zcash
506 + WalletType.zcash,
507 ].contains(wallet.type) &&
508 !isLightning &&
509 isZCashTransparent;
@@ -486,7 +516,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
516 WalletType.bitcoin,
517 WalletType.litecoin,
518 WalletType.bitcoinCash,
489 - WalletType.dogecoin
519 + WalletType.dogecoin,
520 ].contains(wallet.type);
521
522 List<String> getWalletImages(int? chainId) {
@@ -494,44 +524,44 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
524 switch (chainId) {
525 case 1:
526 return [
497 - 'assets/new-ui/crypto_full_icons/ethereum.svg',
498 - 'assets/images/usdc_icon.svg',
499 - 'assets/images/usdt_wallet_icon.svg',
500 - 'assets/images/deuro_icon.svg',
501 - 'assets/images/more_tokens.svg',
527 + "assets/new-ui/crypto_full_icons/ethereum.svg",
528 + "assets/images/usdc_icon.svg",
529 + "assets/images/usdt_wallet_icon.svg",
530 + "assets/images/deuro_icon.svg",
531 + "assets/images/more_tokens.svg",
532 ];
533 case 137:
534 return [
505 - 'assets/new-ui/crypto_full_icons/polygon.svg',
506 - 'assets/images/eth_pol_icon.svg',
507 - 'assets/images/usdc_icon.svg',
508 - 'assets/images/usdt_wallet_icon.svg',
509 - 'assets/images/more_tokens.svg',
535 + "assets/new-ui/crypto_full_icons/polygon.svg",
536 + "assets/images/eth_pol_icon.svg",
537 + "assets/images/usdc_icon.svg",
538 + "assets/images/usdt_wallet_icon.svg",
539 + "assets/images/more_tokens.svg",
540 ];
541 case 8453:
542 return [
513 - 'assets/new-ui/crypto_full_icons/ethereum.svg',
514 - 'assets/images/usdc_icon.svg',
515 - 'assets/images/more_tokens.svg',
543 + "assets/new-ui/crypto_full_icons/ethereum.svg",
544 + "assets/images/usdc_icon.svg",
545 + "assets/images/more_tokens.svg",
546 ];
547 case 42161:
548 return [
519 - 'assets/new-ui/crypto_full_icons/arbitrum.svg',
520 - 'assets/images/usdc_icon.svg',
521 - 'assets/images/more_tokens.svg',
549 + "assets/new-ui/crypto_full_icons/arbitrum.svg",
550 + "assets/images/usdc_icon.svg",
551 + "assets/images/more_tokens.svg",
552 ];
553 case 56:
554 return [
525 - 'assets/new-ui/crypto_full_icons/bnb.svg',
526 - 'assets/images/usdc_icon.svg',
527 - 'assets/images/usdt_wallet_icon.svg',
528 - 'assets/images/more_tokens.svg',
555 + "assets/new-ui/crypto_full_icons/bnb.svg",
556 + "assets/images/usdc_icon.svg",
557 + "assets/images/usdt_wallet_icon.svg",
558 + "assets/images/more_tokens.svg",
559 ];
560 default:
561 return [
532 - 'assets/new-ui/crypto_full_icons/ethereum.svg',
533 - 'assets/images/usdc_icon.svg',
534 - 'assets/images/usdt_wallet_icon.svg',
562 + "assets/new-ui/crypto_full_icons/ethereum.svg",
563 + "assets/images/usdc_icon.svg",
564 + "assets/images/usdt_wallet_icon.svg",
565 ];
566 }
567 }
@@ -539,22 +569,22 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
569 switch (wallet.type) {
570 case WalletType.solana:
571 return [
542 - 'assets/images/sol_icon.svg',
543 - 'assets/images/usdc_icon.svg',
544 - 'assets/images/usdt_wallet_icon.svg',
545 - 'assets/images/more_tokens.svg',
572 + "assets/images/sol_icon.svg",
573 + "assets/images/usdc_icon.svg",
574 + "assets/images/usdt_wallet_icon.svg",
575 + "assets/images/more_tokens.svg",
576 ];
577 case WalletType.tron:
578 return [
549 - 'assets/images/trx_icon.svg',
550 - 'assets/images/usdc_icon.svg',
551 - 'assets/images/usdt_wallet_icon.svg',
552 - 'assets/images/more_tokens.svg',
579 + "assets/images/trx_icon.svg",
580 + "assets/images/usdc_icon.svg",
581 + "assets/images/usdt_wallet_icon.svg",
582 + "assets/images/more_tokens.svg",
583 ];
584 case WalletType.zano:
585 return [
556 - 'assets/images/zano_icon.svg',
557 - 'assets/images/more_tokens.svg',
586 + "assets/images/zano_icon.svg",
587 + "assets/images/more_tokens.svg",
588 ];
589 default:
590 return [];
@@ -563,7 +593,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
593
594 @computed
595 String get qrImage {
566 - if (isLightning) return 'assets/images/btc_chain_qr_lightning.svg';
596 + if (isLightning) {
597 + return "assets/images/btc_chain_qr_lightning.svg";
598 + }
599 return getQrImage(type);
600 }
601
@@ -582,8 +614,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
614
615 @computed
616 bool get isLightning =>
585 - wallet.type == WalletType.bitcoin &&
586 - (wallet.walletAddresses.getPaymentUri(_amount) is LightningPaymentRequest);
617 + wallet.type == WalletType.bitcoin && selectedCurrency == CryptoCurrency.btcln;
618
619 @computed
620 bool get isZCashTransparent {
@@ -698,16 +729,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
729 @action
730 void changeAmount(String amount) {
731 if (selectedCurrency is FiatCurrency) {
701 - this._amount = amount;
732 + _amount = amount;
733 _convertAmountToCrypto();
734 } else if (selectedCurrency is CryptoCurrency) {
704 - this._amount = _appStore.amountParsingProxy
735 + _amount = _appStore.amountParsingProxy
736 .getCanonicalCryptoAmount(amount, selectedCurrency as CryptoCurrency);
737 }
738 if (isLightning) {
708 - wallet.walletAddresses
709 - .getPaymentRequestUri(this._amount)
710 - .then((uri) => _lnPaymentRequest = uri);
739 + wallet.walletAddresses.getPaymentRequestUri(_amount).then((uri) => _lnPaymentRequest = uri);
740 }
741 }
742
@@ -719,22 +748,24 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
748 @action
749 void _convertAmountToCrypto() {
750 var cryptoCurrency = tokenCurrency ?? wallet.currency;
722 - if (cryptoCurrency == CryptoCurrency.btcln) cryptoCurrency = CryptoCurrency.btc;
751 + if (cryptoCurrency == CryptoCurrency.btcln) {
752 + cryptoCurrency = CryptoCurrency.btc;
753 + }
754 final fiatRate = _fiatRate ?? (fiatConversionStore.prices[cryptoCurrency] ?? 0.0);
755
756 if (fiatRate <= 0.0) {
757 dev.log("Invalid Fiat Rate $fiatRate");
727 - _amount = '';
758 + _amount = "";
759 return;
760 }
761
762 try {
732 - final crypto = (double.parse(_amount.replaceAll(',', '.')) / fiatRate).toStringAsFixed(8);
763 + final crypto = (double.parse(_amount.replaceAll(",", ".")) / fiatRate).toStringAsFixed(8);
764 if (_amount != crypto) {
765 _amount = crypto;
766 }
767 } catch (e) {
737 - _amount = '';
768 + _amount = "";
769 }
770 }
771