dev
dart 231 lines 9.02 KB
Raw
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/new-ui/widgets/bridge/confirm_details_card.dart';
3 import 'package:cake_wallet/new-ui/widgets/bridge/network_path_pill.dart';
4 import 'package:cake_wallet/new-ui/widgets/confirm_swiper.dart';
5 import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
6 import 'package:cake_wallet/new-ui/widgets/send_page/send_confirm_bottom_widget.dart';
7 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
8 import 'package:cake_wallet/view_model/bridge/bridge_view_model.dart';
9 import 'package:flutter/cupertino.dart';
10 import 'package:flutter/material.dart';
11 import 'package:flutter_mobx/flutter_mobx.dart';
12 import 'package:mobx/mobx.dart';
13
14 class BridgeConfirmSheet extends StatefulWidget {
15 const BridgeConfirmSheet(this.bridgeViewModel);
16
17 final BridgeViewModel bridgeViewModel;
18
19 @override
20 State<BridgeConfirmSheet> createState() => _BridgeConfirmSheetState();
21 }
22
23 class _BridgeConfirmSheetState extends State<BridgeConfirmSheet> {
24 late final ReactionDisposer _successDisposer;
25
26 BridgeViewModel get bridgeViewModel => widget.bridgeViewModel;
27
28 @override
29 void initState() {
30 super.initState();
31 _successDisposer = reaction(
32 (_) => bridgeViewModel.bridgeSuccess,
33 (bool success) {
34 if (success && mounted) {
35 Navigator.of(context).maybePop();
36 }
37 },
38 );
39
40 WidgetsBinding.instance.addPostFrameCallback((_) {
41 bridgeViewModel.loadQuote();
42 bridgeViewModel.ensureFiatPriceForSelectedToken();
43 bridgeViewModel.ensureFiatPriceForNativeCurrency();
44 });
45 }
46
47 @override
48 void dispose() {
49 _successDisposer();
50 super.dispose();
51 }
52
53 @override
54 Widget build(BuildContext context) {
55 final scheme = Theme.of(context).colorScheme;
56
57 return Container(
58 decoration: BoxDecoration(
59 color: scheme.surface,
60 borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
61 ),
62 child: SafeArea(
63 child: Column(
64 mainAxisSize: MainAxisSize.min,
65 children: [
66 Observer(
67 builder: (_) {
68 return ModalTopBar(
69 title: '',
70 leadingWidget: Row(
71 spacing: 8,
72 children: [
73 CakeImageWidget(
74 imageUrl: bridgeViewModel.selectedToken?.iconPath ?? '',
75 width: 36,
76 height: 36,
77 ),
78 Text(
79 'Bridge',
80 style: Theme.of(context).textTheme.titleLarge?.copyWith(
81 fontWeight: FontWeight.w500,
82 fontSize: 20,
83 letterSpacing: -0.1,
84 color: scheme.onSurface,
85 ),
86 ),
87 ],
88 ),
89 trailingIcon: const Icon(Icons.close),
90 trailingSemanticLabel: S.of(context).close,
91 onTrailingPressed: () => Navigator.of(context).maybePop(),
92 );
93 },
94 ),
95 SingleChildScrollView(
96 padding: const EdgeInsets.symmetric(horizontal: 24),
97 child: Observer(
98 builder: (_) {
99 if (bridgeViewModel.isQuoteLoading && bridgeViewModel.quote == null) {
100 return const Padding(
101 padding: EdgeInsets.symmetric(vertical: 48),
102 child: Center(
103 child: CupertinoActivityIndicator(),
104 ),
105 );
106 }
107
108 if (bridgeViewModel.quoteError != null) {
109 return Padding(
110 padding: const EdgeInsets.only(bottom: 24),
111 child: Column(
112 children: [
113 Text(
114 bridgeViewModel.quoteError!,
115 textAlign: TextAlign.center,
116 style: Theme.of(context).textTheme.bodyMedium?.copyWith(
117 fontWeight: FontWeight.w400,
118 fontSize: 14,
119 letterSpacing: -0.07,
120 color: scheme.error,
121 ),
122 ),
123 const SizedBox(height: 16),
124 FilledButton(
125 onPressed: () => bridgeViewModel.loadQuote(),
126 child: Text(
127 S.of(context).try_again,
128 style: Theme.of(context).textTheme.bodyMedium?.copyWith(
129 fontWeight: FontWeight.w500,
130 fontSize: 16,
131 letterSpacing: -0.08,
132 color: scheme.onPrimary,
133 ),
134 ),
135 ),
136 ],
137 ),
138 );
139 }
140
141 return Column(
142 crossAxisAlignment: CrossAxisAlignment.stretch,
143 spacing: 24,
144 children: [
145 Column(
146 children: [
147 Row(
148 mainAxisAlignment: MainAxisAlignment.center,
149 spacing: 4,
150 children: [
151 Text(
152 bridgeViewModel.amountDisplayFormatted,
153 style: Theme.of(context).textTheme.displaySmall?.copyWith(
154 fontSize: 36,
155 fontWeight: FontWeight.w400,
156 color: scheme.onSurface,
157 ),
158 ),
159 Text(
160 bridgeViewModel.selectedToken?.title ?? '',
161 style: Theme.of(context).textTheme.displaySmall?.copyWith(
162 fontSize: 36,
163 fontWeight: FontWeight.w400,
164 color: scheme.onSurfaceVariant,
165 ),
166 ),
167 ],
168 ),
169 const SizedBox(height: 4),
170 Text(
171 '${bridgeViewModel.fiatCurrencyTitle} ${bridgeViewModel.fiatAmountFormatted}',
172 style: Theme.of(context).textTheme.titleLarge?.copyWith(
173 fontSize: 20,
174 fontWeight: FontWeight.w500,
175 color: scheme.onSurfaceVariant,
176 letterSpacing: -0.1,
177 ),
178 ),
179 ],
180 ),
181 Row(
182 mainAxisAlignment: MainAxisAlignment.center,
183 children: [
184 NetworkPathPill(
185 sourceChainName: bridgeViewModel.wallet.type.name,
186 destChainName: bridgeViewModel.destinationChainInfo?.name ?? '',
187 ),
188 ],
189 ),
190 ConfirmDetailsCard(bridgeViewModel: bridgeViewModel),
191 if (bridgeViewModel.executeError != null) ...[
192 Text(
193 bridgeViewModel.executeError!,
194 textAlign: TextAlign.center,
195 style: TextStyle(color: scheme.error),
196 ),
197 ],
198 const SizedBox(height: 8),
199 ],
200 );
201 },
202 ),
203 ),
204 Padding(
205 padding: const EdgeInsets.fromLTRB(24, 0, 24, 16),
206 child: Observer(
207 builder: (_) {
208 if (bridgeViewModel.isExecuting) {
209 return LoadingBottomWidget(
210 text: "${S.of(context).bridging}...",
211 );
212 }
213 if (bridgeViewModel.isQuoteLoading || bridgeViewModel.quote == null) {
214 return LoadingBottomWidget(text: "${S.of(context).loading}...");
215 }
216 return ConfirmSwiper(
217 onConfirmed: () {
218 bridgeViewModel.executeBridge();
219 },
220 swiperText: S.of(context).swipe_to_bridge,
221 accessibleNavigationModeButtonText: S.of(context).confirm,
222 );
223 },
224 ),
225 ),
226 ],
227 ),
228 ),
229 );
230 }
231 }