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