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",