dev
dart 217 lines 7.89 KB
Raw
1 import 'dart:math';
2
3 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
4 import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart';
6 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
7 import 'package:cw_core/crypto_currency.dart';
8 import 'package:cw_core/wallet_type.dart';
9 import 'package:flutter/material.dart';
10
11 class ReceiveInfoBox extends StatelessWidget {
12 ReceiveInfoBox(
13 {super.key,
14 required this.iconPath,
15 required this.message,
16 required this.onDismissed,
17 this.bottomWidget});
18
19 /// [addressRotates] tells whether the address type currently selected on the
20 /// receive page actually rotates. Most wallet types only ever expose rotating
21 /// address types, so it defaults to true; Zcash also offers static types, for
22 /// which the rotation notice would be wrong.
23 static ReceiveInfoBox? forWalletType(WalletType type,
24 {required VoidCallback onDismissed,
25 required AutoGenerateSubaddressStatus autoGenerateSubaddressStatus,
26 List<CryptoCurrency>? supportedCurrencies,
27 bool addressRotates = true}) {
28 switch (type) {
29 case WalletType.nano:
30 return null;
31 case WalletType.ethereum:
32 case WalletType.base:
33 case WalletType.solana:
34 case WalletType.arbitrum:
35 case WalletType.tron:
36 case WalletType.polygon:
37 case WalletType.zano:
38 case WalletType.bsc:
39 if (autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.disabled) return null;
40 return ReceiveInfoBox(
41 iconPath: "",
42 message: "${S.current.infobox_multichain} ${walletTypeToString(type)}",
43 onDismissed: onDismissed,
44 bottomWidget: InfoboxCurrencyRow(
45 currencies: supportedCurrencies ?? [],
46 chainIconPath:
47 "assets/new-ui/chain_badges/${walletTypeToString(type).toLowerCase()}.svg",
48 ));
49 default:
50 if (autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.disabled) return null;
51 if (!addressRotates) return null;
52 return ReceiveInfoBox(
53 iconPath: "assets/new-ui/info.svg",
54 message: S.current.infobox_auto_address,
55 onDismissed: onDismissed,
56 );
57 }
58 }
59
60 late final String iconPath;
61 late final String message;
62 final Widget? bottomWidget;
63 final VoidCallback onDismissed;
64
65 @override
66 Widget build(BuildContext context) {
67 return Padding(
68 padding: const EdgeInsets.symmetric(horizontal: 25),
69 child: Container(
70 decoration: BoxDecoration(
71 color: Theme.of(context).colorScheme.surfaceContainer,
72 borderRadius: BorderRadius.circular(16)),
73 child: Padding(
74 padding: const EdgeInsets.all(16.0),
75 child: Row(
76 crossAxisAlignment: CrossAxisAlignment.start,
77 spacing: 10,
78 children: [
79 if (iconPath.isNotEmpty)
80 ExcludeSemantics(
81 child: CakeImageWidget(
82 imageUrl: iconPath,
83 width: 16,
84 height: 16,
85 colorFilter: ColorFilter.mode(
86 Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn),
87 ),
88 ),
89 Flexible(
90 child: Column(
91 spacing: 10,
92 mainAxisSize: MainAxisSize.max,
93 mainAxisAlignment: MainAxisAlignment.start,
94 crossAxisAlignment: CrossAxisAlignment.start,
95 children: [
96 Text(
97 message,
98 style: TextStyle(
99 color: Theme.of(context).colorScheme.onSurfaceVariant,
100 fontSize: 12,
101 fontWeight: FontWeight.w300),
102 ),
103 Row(
104 mainAxisAlignment: MainAxisAlignment.spaceBetween,
105 children: [
106 if (bottomWidget != null) bottomWidget!,
107 MergeSemantics(
108 child: Semantics(
109 button: true,
110 child: GestureDetector(
111 onTap: onDismissed,
112 child: Text(
113 S.of(context).dismiss,
114 style: TextStyle(
115 color: Theme.of(context).colorScheme.primary,
116 fontSize: 12,
117 fontWeight: FontWeight.w300),
118 )),
119 ),
120 ),
121 ],
122 )
123 ]),
124 )
125 ],
126 ),
127 ),
128 ),
129 );
130 }
131 }
132
133 class InfoboxCurrencyRow extends StatelessWidget {
134 const InfoboxCurrencyRow({super.key, required this.currencies, required this.chainIconPath});
135
136 final String chainIconPath;
137 final List<CryptoCurrency> currencies;
138
139 static const overlap = 16.0;
140 static const iconSize = 24.0;
141 static const maxCurrencies = 4;
142 static const iconBorder = 1.5;
143
144 @override
145 Widget build(BuildContext context) {
146 final currenciesWithImage = currencies.where((item) => item.iconPath != null).toList();
147 final currenciesLimited =
148 currenciesWithImage.sublist(0, min(currenciesWithImage.length, maxCurrencies));
149
150 final double stackWidth = iconSize + (overlap * (currenciesLimited.length));
151
152 // Purely illustrative: the meaning ("receive any token on <chain>") is
153 // carried by the infobox message text next to it.
154 return ExcludeSemantics(
155 child: Row(
156 spacing: 8,
157 children: [
158 CakeImageWidget(
159 imageUrl: chainIconPath,
160 width: 20,
161 height: 20,
162 colorFilter:
163 ColorFilter.mode(Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn),
164 ),
165 Container(
166 height: 28,
167 width: 1,
168 color: Theme.of(context).colorScheme.surfaceContainerHigh,
169 ),
170 SizedBox(
171 height: iconSize + iconBorder * 2,
172 width: stackWidth,
173 child: Stack(
174 children: [
175 Positioned(
176 top: iconBorder,
177 left: overlap * currenciesLimited.length,
178 child: Container(
179 width: 24,
180 height: 24,
181 decoration: BoxDecoration(
182 color: Colors.white.withAlpha(25),
183 borderRadius: BorderRadius.circular(9999999)),
184 child: Icon(
185 Icons.add,
186 size: 16,
187 color: Colors.white.withAlpha(128),
188 ),
189 ),
190 ),
191 ...currenciesLimited
192 .asMap()
193 .entries
194 .map((entry) => Positioned(
195 left: 16.0 * entry.key,
196 child: Container(
197 decoration: BoxDecoration(
198 border: Border.all(
199 color: Theme.of(context).colorScheme.surfaceContainer,
200 width: iconBorder),
201 borderRadius: BorderRadius.circular(9999999)),
202 child: TokenImageWidget(
203 imageUrl: entry.value.iconPath ?? '',
204 size: 24,
205 ),
206 ),
207 ))
208 .toList()
209 .reversed,
210 ],
211 ),
212 )
213 ],
214 ),
215 );
216 }
217 }