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
This commit is contained in:
Serhii 2022-09-02 16:10:54 +03:00 committed by GitHub
parent 04be884357
commit 0aee6e1b8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 296 additions and 31 deletions

View file

@ -574,7 +574,8 @@ Future setup(
(WalletType type, _) => PreSeedPage(type)); (WalletType type, _) => PreSeedPage(type));
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) => getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
TradeDetailsViewModel(tradeForDetails: trade, trades: _tradesSource)); TradeDetailsViewModel(tradeForDetails: trade, trades: _tradesSource,
settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactory(() => BackupService( getIt.registerFactory(() => BackupService(
getIt.get<FlutterSecureStorage>(), getIt.get<FlutterSecureStorage>(),

View file

@ -2,20 +2,56 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class SyncIndicatorIcon extends StatelessWidget { class SyncIndicatorIcon extends StatelessWidget {
SyncIndicatorIcon({this.isSynced}); SyncIndicatorIcon(
{this.boolMode = true,
this.isSynced = false,
this.value = waiting,
this.size = 4.0});
final bool boolMode;
final bool isSynced; final bool isSynced;
final String value;
final double size;
static const String waiting = 'waiting';
static const String actionRequired = 'action required';
static const String created = 'created';
static const String fetching = 'fetching';
static const String finished = 'finished';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( Color indicatorColor;
height: 4,
width: 4, if (boolMode) {
decoration: BoxDecoration( indicatorColor = isSynced
shape: BoxShape.circle,
color: isSynced
? PaletteDark.brightGreen ? PaletteDark.brightGreen
: Theme.of(context).textTheme.caption.color), : Theme.of(context).textTheme.caption.color;
); } else {
switch (value.toLowerCase()) {
case waiting:
indicatorColor = Colors.red;
break;
case actionRequired:
indicatorColor = Theme.of(context).textTheme.display3.decorationColor;
break;
case created:
indicatorColor = PaletteDark.brightGreen;
break;
case fetching:
indicatorColor = Colors.red;
break;
case finished:
indicatorColor = PaletteDark.brightGreen;
break;
default:
indicatorColor = Colors.red;
}
}
return Container(
height: size,
width: size,
decoration:
BoxDecoration(shape: BoxShape.circle, color: indicatorColor));
} }
} }

View file

@ -0,0 +1,35 @@
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
class TradeDetailsListCardItem extends StandartListItem {
TradeDetailsListCardItem(
{String title,
String value,
this.id,
this.createdAt,
this.pair,
this.onTap})
: super(title: title, value: value);
factory TradeDetailsListCardItem.tradeDetails(
{@required String id,
@required String createdAt,
@required CryptoCurrency from,
@required CryptoCurrency to,
@required Function onTap}) {
return TradeDetailsListCardItem(
id: '${S.current.trade_details_id} ${formatAsText(id)}',
createdAt: formatAsText(createdAt),
pair: '${formatAsText(from)}${formatAsText(to)}',
onTap: onTap);
}
final String id;
final String createdAt;
final String pair;
final Function onTap;
static String formatAsText<T>(T value) => value?.toString() ?? '';
}

View file

@ -1,4 +1,6 @@
import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/widgets/standart_list_card.dart';
import 'package:cake_wallet/src/widgets/standart_list_status_row.dart';
import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart'; import 'package:cake_wallet/view_model/trade_details_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -9,6 +11,8 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart'; import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart'; import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
class TradeDetailsPage extends BasePage { class TradeDetailsPage extends BasePage {
TradeDetailsPage(this.tradeDetailsViewModel); TradeDetailsPage(this.tradeDetailsViewModel);
@ -58,7 +62,23 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
onTap: item.onTap, onTap: item.onTap,
child: StandartListRow( child: StandartListRow(
title: '${item.title}', value: '${item.value}')); title: '${item.title}', value: '${item.value}'));
} else { }
if (item is DetailsListStatusItem) {
return StandartListStatusRow(
title: item.title,
value: item.value);
}
if (item is TradeDetailsListCardItem) {
return TradeDatailsStandartListCard(
id: item.id,
create: item.createdAt,
pair: item.pair,
currentTheme: tradeDetailsViewModel.settingsStore.currentTheme.type,
onTap: item.onTap,);
}
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Clipboard.setData(ClipboardData(text: '${item.value}')); Clipboard.setData(ClipboardData(text: '${item.value}'));
@ -66,7 +86,6 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
}, },
child: StandartListRow( child: StandartListRow(
title: '${item.title}', value: '${item.value}')); title: '${item.title}', value: '${item.value}'));
}
}); });
}); });
} }

View file

@ -0,0 +1,7 @@
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
class DetailsListStatusItem extends StandartListItem {
DetailsListStatusItem(
{String title, String value})
: super(title: title, value: value);
}

View file

@ -1,4 +1,6 @@
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/standart_list_card.dart';
import 'package:cake_wallet/src/widgets/standart_list_status_row.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -212,6 +214,10 @@ class SectionStandardList extends StatelessWidget {
return Container(); return Container();
} }
if (row is StandartListStatusRow || row is TradeDatailsStandartListCard) {
return Container();
}
final nextRow = totalRows[index + 1]; final nextRow = totalRows[index + 1];
// If current row is pre last and last row is separator. // If current row is pre last and last row is separator.

View file

@ -0,0 +1,77 @@
import 'package:cake_wallet/palette.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/themes/theme_base.dart';
class TradeDatailsStandartListCard extends StatelessWidget {
TradeDatailsStandartListCard(
{this.id, this.create, this.pair, this.onTap, this.currentTheme});
final String id;
final String create;
final String pair;
final ThemeType currentTheme;
final Function onTap;
@override
Widget build(BuildContext context) {
final darkTheme = currentTheme == ThemeType.dark;
final baseGradient = LinearGradient(colors: [
Theme.of(context).primaryTextTheme.subtitle.color,
Theme.of(context).primaryTextTheme.subtitle.decorationColor,
], begin: Alignment.centerLeft, end: Alignment.centerRight);
final gradient = LinearGradient(colors: [
PaletteDark.wildNightBlue,
PaletteDark.oceanBlue,
], begin: Alignment.bottomCenter, end: Alignment.topCenter);
final textColor = Colors.white;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
child: GestureDetector(
onTap: () => onTap(context),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
gradient: darkTheme ? gradient : baseGradient),
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(id,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: textColor)),
SizedBox(
height: 8,
),
Text(create,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: textColor)),
SizedBox(
height: 35,
),
Text(pair,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
color: textColor)),
]),
),
),
),
);
}
}

View file

@ -0,0 +1,66 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandartListStatusRow extends StatelessWidget {
StandartListStatusRow({this.title, this.value});
final String title;
final String value;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Theme.of(context).backgroundColor,
child: Padding(
padding:
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.overline.color),
textAlign: TextAlign.left),
Padding(
padding: const EdgeInsets.only(top: 12),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.display2.color,
borderRadius: BorderRadius.circular(30.0),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SyncIndicatorIcon(
boolMode: false,
value: value,
size: 6,
),
SizedBox(
width: 5,
),
Text(value,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.title
.color))
],
),
),
),
)
]),
),
);
}
}

View file

@ -3,21 +3,21 @@ import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
class DateFormatter { class DateFormatter {
static String currentLocalFormat({bool hasTime = true}) { static String currentLocalFormat({bool hasTime = true, bool reverse = false}) {
final isUSA = getIt.get<SettingsStore>().languageCode.toLowerCase() == 'en'; final isUSA = getIt.get<SettingsStore>().languageCode.toLowerCase() == 'en';
final format = final format =
isUSA ? usaStyleFormat(hasTime) : regularStyleFormat(hasTime); isUSA ? usaStyleFormat(hasTime, reverse) : regularStyleFormat(hasTime, reverse);
return format; return format;
} }
static DateFormat withCurrentLocal({bool hasTime = true}) => DateFormat( static DateFormat withCurrentLocal({bool hasTime = true, bool reverse = false}) => DateFormat(
currentLocalFormat(hasTime: hasTime), currentLocalFormat(hasTime: hasTime, reverse: reverse),
getIt.get<SettingsStore>().languageCode); getIt.get<SettingsStore>().languageCode);
static String usaStyleFormat(bool hasTime) => static String usaStyleFormat(bool hasTime, bool reverse) =>
hasTime ? 'yyyy.MM.dd, HH:mm' : 'yyyy.MM.dd'; hasTime ? (reverse ? 'HH:mm yyyy.MM.dd' : 'yyyy.MM.dd, HH:mm') : 'yyyy.MM.dd';
static String regularStyleFormat(bool hasTime) => static String regularStyleFormat(bool hasTime, bool reverse) =>
hasTime ? 'dd.MM.yyyy, HH:mm' : 'dd.MM.yyyy'; hasTime ? (reverse ? 'HH:mm dd.MM.yyyy' : 'dd.MM.yyyy, HH:mm') : 'dd.MM.yyyy';
} }

View file

@ -7,12 +7,18 @@ import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart'; import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/date_formatter.dart'; import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart'; import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
part 'trade_details_view_model.g.dart'; part 'trade_details_view_model.g.dart';
@ -20,7 +26,7 @@ class TradeDetailsViewModel = TradeDetailsViewModelBase
with _$TradeDetailsViewModel; with _$TradeDetailsViewModel;
abstract class TradeDetailsViewModelBase with Store { abstract class TradeDetailsViewModelBase with Store {
TradeDetailsViewModelBase({Trade tradeForDetails, this.trades}) { TradeDetailsViewModelBase({Trade tradeForDetails, this.trades, this.settingsStore}) {
trade = tradeForDetails; trade = tradeForDetails;
switch (trade.provider) { switch (trade.provider) {
@ -62,6 +68,8 @@ abstract class TradeDetailsViewModelBase with Store {
Timer timer; Timer timer;
final SettingsStore settingsStore;
@action @action
Future<void> _updateTrade() async { Future<void> _updateTrade() async {
try { try {
@ -80,18 +88,28 @@ abstract class TradeDetailsViewModelBase with Store {
} }
void _updateItems() { void _updateItems() {
final dateFormat = DateFormatter.withCurrentLocal(); final dateFormat = DateFormatter.withCurrentLocal(reverse: true);
items?.clear(); items?.clear();
items.addAll([ items.add(
StandartListItem(title: S.current.trade_details_id, value: trade.id), DetailsListStatusItem(
StandartListItem(
title: S.current.trade_details_state, title: S.current.trade_details_state,
value: trade.state != null value: trade.state != null
? trade.state.toString() ? trade.state.toString()
: S.current.trade_details_fetching) : S.current.trade_details_fetching)
]); );
items.add(TradeDetailsListCardItem.tradeDetails(
id: trade.id,
createdAt: dateFormat.format(trade.createdAt),
from: trade.from,
to: trade.to,
onTap: (BuildContext context) {
Clipboard.setData(ClipboardData(text: '${trade.id}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
));
if (trade.provider != null) { if (trade.provider != null) {
items.add(StandartListItem( items.add(StandartListItem(

View file

@ -276,7 +276,7 @@
"trade_details_title" : "Trade Details", "trade_details_title" : "Trade Details",
"trade_details_id" : "ID", "trade_details_id" : "ID",
"trade_details_state" : "State", "trade_details_state" : "Status",
"trade_details_fetching" : "Fetching", "trade_details_fetching" : "Fetching",
"trade_details_provider" : "Provider", "trade_details_provider" : "Provider",
"trade_details_created_at" : "Created at", "trade_details_created_at" : "Created at",

View file

@ -274,7 +274,7 @@
"trade_details_title" : "Détails de l'échange", "trade_details_title" : "Détails de l'échange",
"trade_details_id" : "ID", "trade_details_id" : "ID",
"trade_details_state" : "État", "trade_details_state" : "Statut",
"trade_details_fetching" : "Récupération", "trade_details_fetching" : "Récupération",
"trade_details_provider" : "Fournisseur", "trade_details_provider" : "Fournisseur",
"trade_details_created_at" : "Créé le", "trade_details_created_at" : "Créé le",

View file

@ -276,7 +276,7 @@
"trade_details_title" : "व्यापार विवरण", "trade_details_title" : "व्यापार विवरण",
"trade_details_id" : "आईडी", "trade_details_id" : "आईडी",
"trade_details_state" : "राज्य", "trade_details_state" : "दर्जा",
"trade_details_fetching" : "ला रहा है", "trade_details_fetching" : "ला रहा है",
"trade_details_provider" : "प्रदाता", "trade_details_provider" : "प्रदाता",
"trade_details_created_at" : "पर बनाया गया", "trade_details_created_at" : "पर बनाया गया",

View file

@ -276,7 +276,7 @@
"trade_details_title" : "Detalji razmjene", "trade_details_title" : "Detalji razmjene",
"trade_details_id" : "ID", "trade_details_id" : "ID",
"trade_details_state" : "Stanje", "trade_details_state" : "Status",
"trade_details_fetching" : "Dohvaćanje", "trade_details_fetching" : "Dohvaćanje",
"trade_details_provider" : "Pružatelj", "trade_details_provider" : "Pružatelj",
"trade_details_created_at" : "Stvoreno u", "trade_details_created_at" : "Stvoreno u",

View file

@ -276,7 +276,7 @@
"trade_details_title" : "Handelsgegevens", "trade_details_title" : "Handelsgegevens",
"trade_details_id" : "ID", "trade_details_id" : "ID",
"trade_details_state" : "Staat", "trade_details_state" : "Toestand",
"trade_details_fetching" : "Ophalen", "trade_details_fetching" : "Ophalen",
"trade_details_provider" : "Leverancier", "trade_details_provider" : "Leverancier",
"trade_details_created_at" : "Gemaakt bij", "trade_details_created_at" : "Gemaakt bij",