dev
dart 270 lines 12.1 KB
Raw
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/new-ui/widgets/coin_control_page/coin_control_list_item.dart';
3 import 'package:cake_wallet/new-ui/widgets/modal_header.dart';
4 import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
5 import 'package:cake_wallet/routes.dart';
6 import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
7 import 'package:cw_core/unspent_coin_type.dart';
8 import 'package:flutter/cupertino.dart';
9 import 'package:flutter/material.dart';
10 import 'package:flutter_mobx/flutter_mobx.dart';
11 import "package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart";
12
13 class CoinControlPageArgs {
14 final bool canEdit;
15 final UnspentCoinType? coinTypeToSpendFrom;
16
17 const CoinControlPageArgs({required this.canEdit, this.coinTypeToSpendFrom});
18 }
19
20 class NewCoinControlPage extends StatefulWidget {
21 const NewCoinControlPage(
22 {super.key, required this.unspentCoinsListViewModel, required this.canEdit});
23
24 final UnspentCoinsListViewModel unspentCoinsListViewModel;
25 final bool canEdit;
26
27 @override
28 State<NewCoinControlPage> createState() => _NewCoinControlPageState();
29 }
30
31 class _NewCoinControlPageState extends State<NewCoinControlPage> {
32 late Future<void> _initialization;
33
34 @override
35 void initState() {
36 super.initState();
37 _initialization = widget.unspentCoinsListViewModel.initialSetup();
38 }
39
40 @override
41 Widget build(BuildContext context) {
42 return PopScope(
43 canPop: !widget.unspentCoinsListViewModel.isSavingItems,
44 onPopInvokedWithResult: (didPop, result) async {
45 await widget.unspentCoinsListViewModel.dispose();
46 if (!didPop && mounted) Navigator.of(context).pop();
47 },
48 child: Material(
49 borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
50 color: Colors.transparent,
51 child: Container(
52 decoration: BoxDecoration(
53 color: Theme.of(context).colorScheme.surface,
54 borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
55 child: Column(
56 children: [
57 ModalTopBar(
58 title: "",
59 trailingWidget: GestureDetector(
60 onTap: Navigator.of(context).pop,
61 child: Container(
62 decoration: BoxDecoration(
63 color: Theme.of(context).colorScheme.surfaceContainer,
64 borderRadius: BorderRadius.circular(99999)),
65 child: Padding(
66 padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
67 child: Text(
68 S.of(context).done,
69 style: TextStyle(
70 color: Theme.of(context).colorScheme.primary,
71 fontWeight: FontWeight.w600),
72 ),
73 ),
74 ),
75 ),
76 ),
77 FutureBuilder(
78 future: _initialization,
79 builder: (context, asyncSnapshot) {
80 if (asyncSnapshot.connectionState == ConnectionState.waiting) {
81 return Expanded(
82 child: Center(
83 child: Row(
84 mainAxisAlignment: MainAxisAlignment.center,
85 spacing: 12,
86 children: [
87 CupertinoActivityIndicator(),
88 Text("${S.of(context).loading}...")
89 ],
90 )),
91 );
92 }
93
94 if (asyncSnapshot.hasError)
95 return Center(child: Text(S.of(context).coin_control_load_failed));
96
97 return Expanded(
98 child: SingleChildScrollView(
99 child: SafeArea(
100 child: Column(
101 children: [
102 Padding(
103 padding: const EdgeInsets.symmetric(horizontal: 18.0),
104 child: ModalHeader(
105 iconPath: "assets/new-ui/settings_row_icons/coin-control.svg",
106 title: "Coin Control",
107 message: widget.canEdit
108 ? S.of(context).coin_control_desc
109 : S.of(context).coin_control_desc_no_edit),
110 ),
111 if (widget.unspentCoinsListViewModel.items.isNotEmpty &&
112 widget.canEdit)
113 Padding(
114 padding: const EdgeInsets.all(12.0),
115 child: Row(
116 spacing: 20,
117 children: [
118 GestureDetector(
119 onTap: () {
120 widget.unspentCoinsListViewModel.toggleSelectAll(true);
121 },
122 child: Text(S.of(context).select_all,
123 style: TextStyle(
124 color: Theme.of(context).colorScheme.primary,
125 fontSize: 14,
126 fontWeight: FontWeight.w400)),
127 ),
128 GestureDetector(
129 onTap: () {
130 widget.unspentCoinsListViewModel.toggleSelectAll(false);
131 },
132 child: Text(S.of(context).unselect_all,
133 style: TextStyle(
134 color: Theme.of(context).colorScheme.primary,
135 fontSize: 14,
136 fontWeight: FontWeight.w400)),
137 )
138 ],
139 ),
140 )
141 else
142 SizedBox(
143 height: 24,
144 ),
145 if (widget.unspentCoinsListViewModel.nonFrozenItems.isEmpty &&
146 widget.unspentCoinsListViewModel.frozenItems.isEmpty) ...[
147 SizedBox(height: 12),
148 Center(
149 child: Text(
150 S.of(context).no_unspent_coins,
151 textAlign: TextAlign.center,
152 style: Theme.of(context).textTheme.bodyMedium!.copyWith(
153 color: Theme.of(context).colorScheme.onSurfaceVariant,
154 ),
155 )),
156 ],
157 Observer(
158 builder: (_) => widget
159 .unspentCoinsListViewModel.nonFrozenItems.isEmpty
160 ? SizedBox.shrink()
161 : Padding(
162 padding: const EdgeInsets.symmetric(horizontal: 18.0),
163 child: CoinControlListSection(
164 canEdit: widget.canEdit,
165 items: widget.unspentCoinsListViewModel.nonFrozenItems,
166 unspentCoinsListViewModel:
167 widget.unspentCoinsListViewModel),
168 ),
169 ),
170 Observer(
171 builder: (context) =>
172 widget.unspentCoinsListViewModel.frozenItems.isEmpty
173 ? SizedBox.shrink()
174 : Padding(
175 padding: const EdgeInsets.symmetric(horizontal: 18.0),
176 child: Column(
177 spacing: 10,
178 crossAxisAlignment: CrossAxisAlignment.start,
179 children: [
180 SizedBox(height: 12),
181 Text(
182 S.of(context).frozen,
183 style: TextStyle(
184 fontSize: 14,
185 fontWeight: FontWeight.w400,
186 color: Theme.of(context)
187 .colorScheme
188 .onSurfaceVariant),
189 ),
190 CoinControlListSection(
191 canEdit: widget.canEdit,
192 items: widget
193 .unspentCoinsListViewModel.frozenItems,
194 unspentCoinsListViewModel:
195 widget.unspentCoinsListViewModel),
196 ]),
197 ),
198 ),
199 SizedBox(height: 12)
200 ],
201 ),
202 ),
203 ),
204 );
205 })
206 ],
207 ),
208 ),
209 ),
210 );
211 }
212 }
213
214 class CoinControlListSection extends StatelessWidget {
215 const CoinControlListSection(
216 {super.key,
217 required this.items,
218 required this.unspentCoinsListViewModel,
219 required this.canEdit});
220
221 final List<UnspentCoinsItem> items;
222 final bool canEdit;
223 final UnspentCoinsListViewModel unspentCoinsListViewModel;
224
225 @override
226 Widget build(BuildContext context) {
227 return ListView.separated(
228 shrinkWrap: true,
229 padding: EdgeInsets.zero,
230 physics: NeverScrollableScrollPhysics(),
231 itemCount: items.length,
232 separatorBuilder: (_, __) => Container(
233 height: 1,
234 color: Theme.of(context).colorScheme.surfaceContainerHigh,
235 ),
236 itemBuilder: (_, int index) {
237 return Observer(builder: (_) {
238 final item = items[index];
239 final fiatAmount = unspentCoinsListViewModel.fiatAmounts[item.amount] ?? '';
240 return GestureDetector(
241 onTap: () => Navigator.of(context).pushNamed(
242 Routes.unspentCoinsDetails,
243 arguments: [item, unspentCoinsListViewModel],
244 ),
245 child: CoinControlListItem(
246 note: item.note,
247 amount: item.amount,
248 fiatAmount: fiatAmount,
249 address: item.address,
250 isSending: item.isSending,
251 isFrozen: item.isFrozen,
252 isChange: item.isChange,
253 isSilentPayment: item.isSilentPayment,
254 isLoading: item.isBeingSaved,
255 isFirst: index == 0,
256 isLast: index == items.length - 1,
257 hasCheckbox: canEdit,
258 onCheckBoxTap: item.isFrozen
259 ? null
260 : () async {
261 item.isSending = !item.isSending;
262 await unspentCoinsListViewModel.saveUnspentCoinInfo(item);
263 },
264 ),
265 );
266 });
267 },
268 );
269 }
270 }