rework trade details screen (#449)

* create standart list card item * create standart list status item * update localization * fix date format * fix theme gradient * PR comments * fix issues from code review

Serhii committed Sep 2, 2022 at 16:10 UTC 0aee6e1b8dcf9926a1b455bf5eebb6256900e277
15 files changed +296 -31
lib/di.dart
+2 -1
@@ -574,7 +574,8 @@ Future setup(
574 (WalletType type, _) => PreSeedPage(type));
575
576 getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
577 - TradeDetailsViewModel(tradeForDetails: trade, trades: _tradesSource));
577 + TradeDetailsViewModel(tradeForDetails: trade, trades: _tradesSource,
578 + settingsStore: getIt.get<SettingsStore>()));
579
580 getIt.registerFactory(() => BackupService(
581 getIt.get<FlutterSecureStorage>(),
lib/src/screens/dashboard/widgets/sync_indicator_icon.dart
+45 -9
@@ -2,20 +2,56 @@ import 'package:flutter/material.dart';
2 import 'package:cake_wallet/palette.dart';
3
4 class SyncIndicatorIcon extends StatelessWidget {
5 - SyncIndicatorIcon({this.isSynced});
5 + SyncIndicatorIcon(
6 + {this.boolMode = true,
7 + this.isSynced = false,
8 + this.value = waiting,
9 + this.size = 4.0});
10
11 + final bool boolMode;
12 final bool isSynced;
13 + final String value;
14 + final double size;
15 +
16 + static const String waiting = 'waiting';
17 + static const String actionRequired = 'action required';
18 + static const String created = 'created';
19 + static const String fetching = 'fetching';
20 + static const String finished = 'finished';
21
22 @override
23 Widget build(BuildContext context) {
24 + Color indicatorColor;
25 +
26 + if (boolMode) {
27 + indicatorColor = isSynced
28 + ? PaletteDark.brightGreen
29 + : Theme.of(context).textTheme.caption.color;
30 + } else {
31 + switch (value.toLowerCase()) {
32 + case waiting:
33 + indicatorColor = Colors.red;
34 + break;
35 + case actionRequired:
36 + indicatorColor = Theme.of(context).textTheme.display3.decorationColor;
37 + break;
38 + case created:
39 + indicatorColor = PaletteDark.brightGreen;
40 + break;
41 + case fetching:
42 + indicatorColor = Colors.red;
43 + break;
44 + case finished:
45 + indicatorColor = PaletteDark.brightGreen;
46 + break;
47 + default:
48 + indicatorColor = Colors.red;
49 + }
50 + }
51 return Container(
12 - height: 4,
13 - width: 4,
14 - decoration: BoxDecoration(
15 - shape: BoxShape.circle,
16 - color: isSynced
17 - ? PaletteDark.brightGreen
18 - : Theme.of(context).textTheme.caption.color),
19 - );
52 + height: size,
53 + width: size,
54 + decoration:
55 + BoxDecoration(shape: BoxShape.circle, color: indicatorColor));
56 }
57 }
lib/src/screens/trade_details/trade_details_list_card.dart new
+35
@@ -0,0 +1,35 @@
1 +import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
2 +import 'package:cw_core/crypto_currency.dart';
3 +import 'package:flutter/cupertino.dart';
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +
6 +class TradeDetailsListCardItem extends StandartListItem {
7 + TradeDetailsListCardItem(
8 + {String title,
9 + String value,
10 + this.id,
11 + this.createdAt,
12 + this.pair,
13 + this.onTap})
14 + : super(title: title, value: value);
15 +
16 + factory TradeDetailsListCardItem.tradeDetails(
17 + {@required String id,
18 + @required String createdAt,
19 + @required CryptoCurrency from,
20 + @required CryptoCurrency to,
21 + @required Function onTap}) {
22 + return TradeDetailsListCardItem(
23 + id: '${S.current.trade_details_id} ${formatAsText(id)}',
24 + createdAt: formatAsText(createdAt),
25 + pair: '${formatAsText(from)} → ${formatAsText(to)}',
26 + onTap: onTap);
27 + }
28 +
29 + final String id;
30 + final String createdAt;
31 + final String pair;
32 + final Function onTap;
33 +
34 + static String formatAsText<T>(T value) => value?.toString() ?? '';
35 +}
lib/src/screens/trade_details/trade_details_page.dart
+21 -2
@@ -1,4 +1,6 @@
1 import 'package:cake_wallet/src/widgets/standard_list.dart';
2 +import 'package:cake_wallet/src/widgets/standart_list_card.dart';
3 +import 'package:cake_wallet/src/widgets/standart_list_status_row.dart';
4 import 'package:cake_wallet/utils/show_bar.dart';
5 import 'package:cake_wallet/view_model/trade_details_view_model.dart';
6 import 'package:flutter/material.dart';
@@ -9,6 +11,8 @@ import 'package:cake_wallet/generated/i18n.dart';
11 import 'package:cake_wallet/src/screens/base_page.dart';
12 import 'package:cake_wallet/src/widgets/standart_list_row.dart';
13 import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
14 +import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
15 +import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
16
17 class TradeDetailsPage extends BasePage {
18 TradeDetailsPage(this.tradeDetailsViewModel);
@@ -58,7 +62,23 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
62 onTap: item.onTap,
63 child: StandartListRow(
64 title: '${item.title}', value: '${item.value}'));
61 - } else {
65 + }
66 +
67 + if (item is DetailsListStatusItem) {
68 + return StandartListStatusRow(
69 + title: item.title,
70 + value: item.value);
71 + }
72 +
73 + if (item is TradeDetailsListCardItem) {
74 + return TradeDatailsStandartListCard(
75 + id: item.id,
76 + create: item.createdAt,
77 + pair: item.pair,
78 + currentTheme: tradeDetailsViewModel.settingsStore.currentTheme.type,
79 + onTap: item.onTap,);
80 + }
81 +
82 return GestureDetector(
83 onTap: () {
84 Clipboard.setData(ClipboardData(text: '${item.value}'));
@@ -66,7 +86,6 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
86 },
87 child: StandartListRow(
88 title: '${item.title}', value: '${item.value}'));
69 - }
89 });
90 });
91 }
lib/src/screens/trade_details/trade_details_status_item.dart new
+7
@@ -0,0 +1,7 @@
1 +import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
2 +
3 +class DetailsListStatusItem extends StandartListItem {
4 + DetailsListStatusItem(
5 + {String title, String value})
6 + : super(title: title, value: value);
7 +}
lib/src/widgets/standard_list.dart
+6
@@ -1,4 +1,6 @@
1 import 'package:cake_wallet/palette.dart';
2 +import 'package:cake_wallet/src/widgets/standart_list_card.dart';
3 +import 'package:cake_wallet/src/widgets/standart_list_status_row.dart';
4 import 'package:flutter/cupertino.dart';
5 import 'package:flutter/material.dart';
6
@@ -212,6 +214,10 @@ class SectionStandardList extends StatelessWidget {
214 return Container();
215 }
216
217 + if (row is StandartListStatusRow || row is TradeDatailsStandartListCard) {
218 + return Container();
219 + }
220 +
221 final nextRow = totalRows[index + 1];
222
223 // If current row is pre last and last row is separator.
lib/src/widgets/standart_list_card.dart new
+77
@@ -0,0 +1,77 @@
1 +import 'package:cake_wallet/palette.dart';
2 +import 'package:flutter/cupertino.dart';
3 +import 'package:flutter/material.dart';
4 +import 'package:cake_wallet/themes/theme_base.dart';
5 +
6 +class TradeDatailsStandartListCard extends StatelessWidget {
7 + TradeDatailsStandartListCard(
8 + {this.id, this.create, this.pair, this.onTap, this.currentTheme});
9 +
10 + final String id;
11 + final String create;
12 + final String pair;
13 + final ThemeType currentTheme;
14 + final Function onTap;
15 +
16 + @override
17 + Widget build(BuildContext context) {
18 + final darkTheme = currentTheme == ThemeType.dark;
19 +
20 + final baseGradient = LinearGradient(colors: [
21 + Theme.of(context).primaryTextTheme.subtitle.color,
22 + Theme.of(context).primaryTextTheme.subtitle.decorationColor,
23 + ], begin: Alignment.centerLeft, end: Alignment.centerRight);
24 +
25 + final gradient = LinearGradient(colors: [
26 + PaletteDark.wildNightBlue,
27 + PaletteDark.oceanBlue,
28 + ], begin: Alignment.bottomCenter, end: Alignment.topCenter);
29 +
30 + final textColor = Colors.white;
31 +
32 + return Padding(
33 + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
34 + child: GestureDetector(
35 + onTap: () => onTap(context),
36 + child: Container(
37 + decoration: BoxDecoration(
38 + borderRadius: BorderRadius.circular(15.0),
39 + gradient: darkTheme ? gradient : baseGradient),
40 + child: Padding(
41 + padding:
42 + const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
43 + child: Column(
44 + mainAxisSize: MainAxisSize.min,
45 + crossAxisAlignment: CrossAxisAlignment.start,
46 + children: [
47 + Text(id,
48 + style: TextStyle(
49 + fontSize: 16,
50 + fontFamily: 'Lato',
51 + fontWeight: FontWeight.w400,
52 + color: textColor)),
53 + SizedBox(
54 + height: 8,
55 + ),
56 + Text(create,
57 + style: TextStyle(
58 + fontSize: 12,
59 + fontFamily: 'Lato',
60 + fontWeight: FontWeight.w400,
61 + color: textColor)),
62 + SizedBox(
63 + height: 35,
64 + ),
65 + Text(pair,
66 + style: TextStyle(
67 + fontSize: 24,
68 + fontFamily: 'Lato',
69 + fontWeight: FontWeight.bold,
70 + color: textColor)),
71 + ]),
72 + ),
73 + ),
74 + ),
75 + );
76 + }
77 +}
lib/src/widgets/standart_list_status_row.dart new
+66
@@ -0,0 +1,66 @@
1 +import 'package:cake_wallet/palette.dart';
2 +import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
3 +import 'package:flutter/cupertino.dart';
4 +import 'package:flutter/material.dart';
5 +
6 +class StandartListStatusRow extends StatelessWidget {
7 + StandartListStatusRow({this.title, this.value});
8 +
9 + final String title;
10 + final String value;
11 +
12 + @override
13 + Widget build(BuildContext context) {
14 + return Container(
15 + width: double.infinity,
16 + color: Theme.of(context).backgroundColor,
17 + child: Padding(
18 + padding:
19 + const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
20 + child: Column(
21 + crossAxisAlignment: CrossAxisAlignment.start,
22 + children: <Widget>[
23 + Text(title,
24 + style: TextStyle(
25 + fontSize: 14,
26 + fontWeight: FontWeight.w500,
27 + color: Theme.of(context).primaryTextTheme.overline.color),
28 + textAlign: TextAlign.left),
29 + Padding(
30 + padding: const EdgeInsets.only(top: 12),
31 + child: Container(
32 + decoration: BoxDecoration(
33 + color: Theme.of(context).accentTextTheme.display2.color,
34 + borderRadius: BorderRadius.circular(30.0),
35 + ),
36 + child: Padding(
37 + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
38 + child: Row(
39 + mainAxisSize: MainAxisSize.min,
40 + children: <Widget>[
41 + SyncIndicatorIcon(
42 + boolMode: false,
43 + value: value,
44 + size: 6,
45 + ),
46 + SizedBox(
47 + width: 5,
48 + ),
49 + Text(value,
50 + style: TextStyle(
51 + fontSize: 16,
52 + fontWeight: FontWeight.w500,
53 + color: Theme.of(context)
54 + .primaryTextTheme
55 + .title
56 + .color))
57 + ],
58 + ),
59 + ),
60 + ),
61 + )
62 + ]),
63 + ),
64 + );
65 + }
66 +}
lib/utils/date_formatter.dart
+8 -8
@@ -3,21 +3,21 @@ import 'package:cake_wallet/di.dart';
3 import 'package:cake_wallet/store/settings_store.dart';
4
5 class DateFormatter {
6 - static String currentLocalFormat({bool hasTime = true}) {
6 + static String currentLocalFormat({bool hasTime = true, bool reverse = false}) {
7 final isUSA = getIt.get<SettingsStore>().languageCode.toLowerCase() == 'en';
8 final format =
9 - isUSA ? usaStyleFormat(hasTime) : regularStyleFormat(hasTime);
9 + isUSA ? usaStyleFormat(hasTime, reverse) : regularStyleFormat(hasTime, reverse);
10
11 return format;
12 }
13
14 - static DateFormat withCurrentLocal({bool hasTime = true}) => DateFormat(
15 - currentLocalFormat(hasTime: hasTime),
14 + static DateFormat withCurrentLocal({bool hasTime = true, bool reverse = false}) => DateFormat(
15 + currentLocalFormat(hasTime: hasTime, reverse: reverse),
16 getIt.get<SettingsStore>().languageCode);
17
18 - static String usaStyleFormat(bool hasTime) =>
19 - hasTime ? 'yyyy.MM.dd, HH:mm' : 'yyyy.MM.dd';
18 + static String usaStyleFormat(bool hasTime, bool reverse) =>
19 + hasTime ? (reverse ? 'HH:mm yyyy.MM.dd' : 'yyyy.MM.dd, HH:mm') : 'yyyy.MM.dd';
20
21 - static String regularStyleFormat(bool hasTime) =>
22 - hasTime ? 'dd.MM.yyyy, HH:mm' : 'dd.MM.yyyy';
21 + static String regularStyleFormat(bool hasTime, bool reverse) =>
22 + hasTime ? (reverse ? 'HH:mm dd.MM.yyyy' : 'dd.MM.yyyy, HH:mm') : 'dd.MM.yyyy';
23 }
lib/view_model/trade_details_view_model.dart
+24 -6
@@ -7,12 +7,18 @@ import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'
7 import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
8 import 'package:cake_wallet/exchange/trade.dart';
9 import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
10 +import 'package:cake_wallet/store/settings_store.dart';
11 import 'package:cake_wallet/utils/date_formatter.dart';
12 +import 'package:cake_wallet/utils/show_bar.dart';
13 +import 'package:flutter/cupertino.dart';
14 +import 'package:flutter/services.dart';
15 import 'package:hive/hive.dart';
16 import 'package:mobx/mobx.dart';
17 import 'package:cake_wallet/generated/i18n.dart';
18 import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
19 import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
20 +import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
21 +import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
22 import 'package:url_launcher/url_launcher.dart';
23 part 'trade_details_view_model.g.dart';
24
@@ -20,7 +26,7 @@ class TradeDetailsViewModel = TradeDetailsViewModelBase
26 with _$TradeDetailsViewModel;
27
28 abstract class TradeDetailsViewModelBase with Store {
23 - TradeDetailsViewModelBase({Trade tradeForDetails, this.trades}) {
29 + TradeDetailsViewModelBase({Trade tradeForDetails, this.trades, this.settingsStore}) {
30 trade = tradeForDetails;
31
32 switch (trade.provider) {
@@ -62,6 +68,8 @@ abstract class TradeDetailsViewModelBase with Store {
68
69 Timer timer;
70
71 + final SettingsStore settingsStore;
72 +
73 @action
74 Future<void> _updateTrade() async {
75 try {
@@ -80,18 +88,28 @@ abstract class TradeDetailsViewModelBase with Store {
88 }
89
90 void _updateItems() {
83 - final dateFormat = DateFormatter.withCurrentLocal();
91 + final dateFormat = DateFormatter.withCurrentLocal(reverse: true);
92
93 items?.clear();
94
87 - items.addAll([
88 - StandartListItem(title: S.current.trade_details_id, value: trade.id),
89 - StandartListItem(
95 + items.add(
96 + DetailsListStatusItem(
97 title: S.current.trade_details_state,
98 value: trade.state != null
99 ? trade.state.toString()
100 : S.current.trade_details_fetching)
94 - ]);
101 + );
102 +
103 + items.add(TradeDetailsListCardItem.tradeDetails(
104 + id: trade.id,
105 + createdAt: dateFormat.format(trade.createdAt),
106 + from: trade.from,
107 + to: trade.to,
108 + onTap: (BuildContext context) {
109 + Clipboard.setData(ClipboardData(text: '${trade.id}'));
110 + showBar<void>(context, S.of(context).copied_to_clipboard);
111 + },
112 + ));
113
114 if (trade.provider != null) {
115 items.add(StandartListItem(
res/values/strings_en.arb
+1 -1
@@ -276,7 +276,7 @@
276
277 "trade_details_title" : "Trade Details",
278 "trade_details_id" : "ID",
279 - "trade_details_state" : "State",
279 + "trade_details_state" : "Status",
280 "trade_details_fetching" : "Fetching",
281 "trade_details_provider" : "Provider",
282 "trade_details_created_at" : "Created at",
res/values/strings_fr.arb
+1 -1
@@ -274,7 +274,7 @@
274
275 "trade_details_title" : "Détails de l'échange",
276 "trade_details_id" : "ID",
277 - "trade_details_state" : "État",
277 + "trade_details_state" : "Statut",
278 "trade_details_fetching" : "Récupération",
279 "trade_details_provider" : "Fournisseur",
280 "trade_details_created_at" : "Créé le",
res/values/strings_hi.arb
+1 -1
@@ -276,7 +276,7 @@
276
277 "trade_details_title" : "व्यापार विवरण",
278 "trade_details_id" : "आईडी",
279 - "trade_details_state" : "राज्य",
279 + "trade_details_state" : "दर्जा",
280 "trade_details_fetching" : "ला रहा है",
281 "trade_details_provider" : "प्रदाता",
282 "trade_details_created_at" : "पर बनाया गया",
res/values/strings_hr.arb
+1 -1
@@ -276,7 +276,7 @@
276
277 "trade_details_title" : "Detalji razmjene",
278 "trade_details_id" : "ID",
279 - "trade_details_state" : "Stanje",
279 + "trade_details_state" : "Status",
280 "trade_details_fetching" : "Dohvaćanje",
281 "trade_details_provider" : "Pružatelj",
282 "trade_details_created_at" : "Stvoreno u",
res/values/strings_nl.arb
+1 -1
@@ -276,7 +276,7 @@
276
277 "trade_details_title" : "Handelsgegevens",
278 "trade_details_id" : "ID",
279 - "trade_details_state" : "Staat",
279 + "trade_details_state" : "Toestand",
280 "trade_details_fetching" : "Ophalen",
281 "trade_details_provider" : "Leverancier",
282 "trade_details_created_at" : "Gemaakt bij",