dev
dart 248 lines 13.1 KB
Raw
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/core/csv_export_service.dart';
3 import 'package:cake_wallet/entities/balance_display_mode.dart';
4 import 'package:cake_wallet/order/order_source_description.dart';
5 import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart';
6 import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
7 import 'package:cake_wallet/src/screens/dashboard/widgets/payjoin_transaction_row.dart';
8 import 'package:cake_wallet/src/screens/dashboard/widgets/trade_row.dart';
9 import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
10 import 'package:cake_wallet/utils/responsive_layout_util.dart';
11 import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
12 import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
13 import 'package:cake_wallet/view_model/dashboard/payjoin_transaction_list_item.dart';
14 import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
15 import 'package:cw_core/crypto_currency.dart';
16 import 'package:cw_core/sync_status.dart';
17 import 'package:cw_core/wallet_type.dart';
18 import 'package:flutter/material.dart';
19 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
20 import 'package:flutter_mobx/flutter_mobx.dart';
21 import 'package:cake_wallet/src/screens/dashboard/widgets/header_row.dart';
22 import 'package:cake_wallet/src/screens/dashboard/widgets/date_section_raw.dart';
23 import 'package:cake_wallet/src/screens/dashboard/widgets/transaction_raw.dart';
24 import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
25 import 'package:cake_wallet/view_model/dashboard/date_section_item.dart';
26 import 'package:intl/intl.dart';
27 import 'package:cake_wallet/routes.dart';
28 import 'package:cake_wallet/generated/i18n.dart';
29 import 'package:url_launcher/url_launcher.dart';
30
31 class TransactionsPage extends StatelessWidget {
32 TransactionsPage({required this.dashboardViewModel});
33
34 final DashboardViewModel dashboardViewModel;
35
36 @override
37 Widget build(BuildContext context) {
38 return GestureDetector(
39 onLongPress: () => dashboardViewModel.balanceViewModel.isReversing =
40 !dashboardViewModel.balanceViewModel.isReversing,
41 onLongPressUp: () => dashboardViewModel.balanceViewModel.isReversing =
42 !dashboardViewModel.balanceViewModel.isReversing,
43 child: Container(
44 color: responsiveLayoutUtil.shouldRenderMobileUI
45 ? null
46 : Theme.of(context).colorScheme.surface,
47 child: Column(
48 children: <Widget>[
49 Observer(builder: (_) {
50 final status = dashboardViewModel.status;
51 if (status is SyncingSyncStatus) {
52 return DashBoardRoundedCardWidget(
53 key: ValueKey('transactions_page_syncing_alert_card_key'),
54 onTap: () {
55 try {
56 final uri = Uri.parse("https://docs.cakewallet.com/faq/funds-not-appearing");
57 launchUrl(uri, mode: LaunchMode.externalApplication);
58 } catch (_) {}
59 },
60 title: S.of(context).syncing_wallet_alert_title,
61 subTitle: S.of(context).syncing_wallet_alert_content,
62 );
63 } else {
64 return Container();
65 }
66 }),
67 HeaderRow(
68 dashboardViewModel: dashboardViewModel,
69 onExportCsv: () => CsvExportService().exportToCsv(dashboardViewModel.items, context),
70 key: ValueKey('transactions_page_header_row_key'),
71 ),
72 Expanded(
73 child: Observer(
74 builder: (_) {
75 final items = dashboardViewModel.items;
76 return items.isNotEmpty
77 ? ListView.builder(
78 key: ValueKey('transactions_page_list_view_builder_key'),
79 itemCount: items.length + 1,
80 itemBuilder: (context, index) {
81 if (index == items.length) return SizedBox(height: 150);
82
83 final item = items[index];
84
85 if (item is DateSectionItem) {
86 return DateSectionRaw(date: item.date, key: item.key);
87 }
88
89 if (item is TransactionListItem) {
90 if (item.hasTokens && item.assetOfTransaction == null) {
91 return Container();
92 }
93
94 final transaction = item.transaction;
95 final transactionType =
96 dashboardViewModel.getTransactionType(transaction);
97
98 List<String> tags = [];
99 if (dashboardViewModel.type == WalletType.bitcoin) {
100 if (bitcoin!.txIsReceivedSilentPayment(transaction)) {
101 tags.add(S.of(context).silent_payment);
102 }
103 }
104 if (dashboardViewModel.type == WalletType.litecoin) {
105 if (bitcoin!.txIsMweb(transaction)) {
106 tags.add("MWEB");
107 }
108 }
109 return Observer(
110 builder: (_) => TransactionRow(
111 key: item.key,
112 onTap: () => Navigator.of(context)
113 .pushNamed(Routes.transactionDetails, arguments: transaction),
114 isShield: transaction.additionalInfo['isAutoShield'] == true,
115 direction: transaction.direction,
116 formattedDate: DateFormat('HH:mm').format(transaction.date),
117 formattedAmount: item.formattedCryptoAmount,
118 formattedFiatAmount:
119 dashboardViewModel.balanceViewModel.isFiatDisabled
120 ? ''
121 : item.formattedFiatAmount,
122 title:
123 item.formattedTitle + item.formattedStatus + transactionType,
124 tags: tags,
125 ),
126 );
127 }
128
129 if (item is AnonpayTransactionListItem) {
130 final transactionInfo = item.transaction;
131
132 return AnonpayTransactionRow(
133 key: item.key,
134 onTap: () => Navigator.of(context).pushNamed(
135 Routes.anonPayDetailsPage,
136 arguments: transactionInfo),
137 currency: transactionInfo.fiatAmount != null
138 ? transactionInfo.fiatEquiv ?? ''
139 : CryptoCurrency.fromFullName(transactionInfo.coinTo)
140 .name
141 .toUpperCase(),
142 provider: transactionInfo.provider,
143 amount: transactionInfo.fiatAmount?.toString() ??
144 (transactionInfo.amountTo?.toString() ?? ''),
145 createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt),
146 );
147 }
148
149 if (item is PayjoinTransactionListItem) {
150 final session = item.session;
151
152 return PayjoinTransactionRow(
153 key: item.key,
154 onTap: () => Navigator.of(context).pushNamed(
155 Routes.payjoinDetails,
156 arguments: [item.sessionId, item.transaction],
157 ),
158 currency: "BTC",
159 state: item.status,
160 amount: dashboardViewModel.appStore.amountParsingProxy
161 .getDisplayCryptoString(
162 session.amount.toInt(), CryptoCurrency.btc),
163 createdAt: DateFormat('HH:mm').format(session.inProgressSince!),
164 isSending: session.isSenderSession,
165 );
166 }
167
168 if (item is TradeListItem) {
169 final trade = item.trade;
170
171 final tradeFrom = trade.from;
172
173 final tradeTo = trade.to;
174
175 return tradeFrom != null && tradeTo != null
176 ? Observer(
177 builder: (_) => TradeRow(
178 key: item.key,
179 onTap: () => Navigator.of(context)
180 .pushNamed(Routes.tradeDetails, arguments: trade),
181 swapState: trade.state,
182 provider: trade.provider,
183 title: "$tradeFrom$tradeTo",
184 fromSymbol: dashboardViewModel.appStore.amountParsingProxy
185 .getCryptoSymbol(tradeFrom),
186 toSymbol: dashboardViewModel.appStore.amountParsingProxy
187 .getCryptoSymbol(tradeTo),
188 createdAtFormattedDate: trade.createdAt != null
189 ? DateFormat("HH:mm").format(trade.createdAt!)
190 : null,
191 formattedAmount: item.tradeFormattedAmount,
192 formattedReceiveAmount: item.tradeFormattedReceiveAmount,
193 ),
194 )
195 : Container();
196 }
197 if (item is OrderListItem) {
198 final order = item.order;
199 return Observer(
200 builder: (_) {
201 // TODO: Refactor Amount Hiding Logic it is not working properly for Orders and Trades
202 final hide = dashboardViewModel.balanceViewModel.displayMode ==
203 BalanceDisplayMode.hiddenBalance;
204
205 final formattedAmount = hide ? '---' : order.amountFormatted();
206 final formattedReceiveAmount = hide ? '---' : order.receiveAmount;
207
208 return OrderRow(
209 key: item.key,
210 onTap: () => Navigator.of(context)
211 .pushNamed(Routes.orderDetails, arguments: order),
212 providerTitle: order.providerTitle,
213 providerIconPath: order.providerIcon,
214 from: order.from ?? '',
215 to: order.to ?? '',
216 createdAtFormattedDate:
217 DateFormat('HH:mm').format(order.createdAt),
218 formattedAmount: formattedAmount,
219 formattedReceiveAmount:
220 dashboardViewModel.balanceViewModel.isFiatDisabled &&
221 order.source == OrderSourceDescription.order
222 ? ''
223 : formattedReceiveAmount,
224 );
225 },
226 );
227 }
228 return Container(color: Colors.transparent, height: 1);
229 },
230 )
231 : Center(
232 child: Text(
233 key: ValueKey('transactions_page_placeholder_transactions_text_key'),
234 S.of(context).placeholder_transactions,
235 style: Theme.of(context).textTheme.bodyMedium?.copyWith(
236 color: Theme.of(context).colorScheme.onSurfaceVariant,
237 ),
238 ),
239 );
240 },
241 ),
242 ),
243 ],
244 ),
245 ),
246 );
247 }
248 }