desktop all trades view

This commit is contained in:
julian 2022-11-29 13:29:03 -06:00
parent 37bacf8c7f
commit 4c45487e6e
5 changed files with 479 additions and 9 deletions

View file

@ -16,7 +16,6 @@ import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';

View file

@ -0,0 +1,454 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:tuple/tuple.dart';
class DesktopAllTradesView extends ConsumerStatefulWidget {
const DesktopAllTradesView({Key? key}) : super(key: key);
static const String routeName = "/desktopAllTrades";
@override
ConsumerState<DesktopAllTradesView> createState() =>
_DesktopAllTradesViewState();
}
class _DesktopAllTradesViewState extends ConsumerState<DesktopAllTradesView> {
late final TextEditingController _searchController;
late final FocusNode searchFieldFocusNode;
String _searchString = "";
List<Tuple2<String, List<Trade>>> groupTransactionsByMonth(
List<Trade> trades) {
Map<String, List<Trade>> map = {};
for (var trade in trades) {
final date = trade.timestamp;
final monthYear = "${Constants.monthMap[date.month]} ${date.year}";
if (map[monthYear] == null) {
map[monthYear] = [];
}
map[monthYear]!.add(trade);
}
List<Tuple2<String, List<Trade>>> result = [];
map.forEach((key, value) {
result.add(Tuple2(key, value));
});
return result;
}
@override
void initState() {
_searchController = TextEditingController();
searchFieldFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
_searchController.dispose();
searchFieldFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DesktopScaffold(
appBar: DesktopAppBar(
isCompactHeight: true,
background: Theme.of(context).extension<StackColors>()!.popupBG,
leading: Row(
children: [
const SizedBox(
width: 32,
),
AppBarIconButton(
size: 32,
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
shadows: const [],
icon: SvgPicture.asset(
Assets.svg.arrowLeft,
width: 18,
height: 18,
color: Theme.of(context)
.extension<StackColors>()!
.topNavIconPrimary,
),
onPressed: Navigator.of(context).pop,
),
const SizedBox(
width: 12,
),
Text(
"Trades",
style: STextStyles.desktopH3(context),
),
],
),
),
body: Padding(
padding: const EdgeInsets.only(
left: 20,
top: 20,
right: 20,
),
child: Column(
children: [
Row(
children: [
SizedBox(
width: 570,
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: false,
enableSuggestions: false,
controller: _searchController,
focusNode: searchFieldFocusNode,
onChanged: (value) {
setState(() {
_searchString = value;
});
},
style:
STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveText,
height: 1.8,
),
decoration: standardInputDecoration(
"Search...",
searchFieldFocusNode,
context,
desktopMed: true,
).copyWith(
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 18,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 20,
height: 20,
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
_searchString = "";
});
},
),
],
),
),
)
: null,
),
),
),
),
],
),
const SizedBox(
height: 8,
),
Expanded(
child: Consumer(
builder: (_, ref, __) {
List<Trade> trades = ref.watch(
tradesServiceProvider.select((value) => value.trades));
if (_searchString.isNotEmpty) {
final term = _searchString.toLowerCase();
trades = trades
.where((e) => e.toString().toLowerCase().contains(term))
.toList(growable: false);
}
final monthlyList = groupTransactionsByMonth(trades);
return ListView.builder(
primary: false,
itemCount: monthlyList.length,
itemBuilder: (_, index) {
final month = monthlyList[index];
return Padding(
padding: const EdgeInsets.all(4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (index != 0)
const SizedBox(
height: 12,
),
Text(
month.item1,
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: ListView.separated(
shrinkWrap: true,
primary: false,
separatorBuilder: (context, _) => Container(
height: 1,
color: Theme.of(context)
.extension<StackColors>()!
.background,
),
itemCount: month.item2.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.all(4),
child: DesktopTradeRowCard(
key: Key(
"transactionCard_key_${month.item2[index].tradeId}"),
tradeId: month.item2[index].tradeId,
),
),
),
),
],
),
);
},
);
},
),
),
],
),
),
);
}
}
class DesktopTradeRowCard extends ConsumerStatefulWidget {
const DesktopTradeRowCard({
Key? key,
required this.tradeId,
}) : super(key: key);
final String tradeId;
@override
ConsumerState<DesktopTradeRowCard> createState() =>
_DesktopTradeRowCardState();
}
class _DesktopTradeRowCardState extends ConsumerState<DesktopTradeRowCard> {
late final String tradeId;
String _fetchIconAssetForStatus(String statusString, BuildContext context) {
ChangeNowTransactionStatus? status;
try {
if (statusString.toLowerCase().startsWith("waiting")) {
statusString = "waiting";
}
status = changeNowTransactionStatusFromStringIgnoreCase(statusString);
} on ArgumentError catch (_) {
status = ChangeNowTransactionStatus.Failed;
}
switch (status) {
case ChangeNowTransactionStatus.New:
case ChangeNowTransactionStatus.Waiting:
case ChangeNowTransactionStatus.Confirming:
case ChangeNowTransactionStatus.Exchanging:
case ChangeNowTransactionStatus.Sending:
case ChangeNowTransactionStatus.Refunded:
case ChangeNowTransactionStatus.Verifying:
return Assets.svg.txExchangePending(context);
case ChangeNowTransactionStatus.Finished:
return Assets.svg.txExchange(context);
case ChangeNowTransactionStatus.Failed:
return Assets.svg.txExchangeFailed(context);
}
}
@override
void initState() {
tradeId = widget.tradeId;
super.initState();
}
@override
Widget build(BuildContext context) {
final String? txid =
ref.read(tradeSentFromStackLookupProvider).getTxidForTradeId(tradeId);
final List<String>? walletIds = ref
.read(tradeSentFromStackLookupProvider)
.getWalletIdsForTradeId(tradeId);
final trade =
ref.watch(tradesServiceProvider.select((value) => value.get(tradeId)))!;
return Material(
color: Theme.of(context).extension<StackColors>()!.popupBG,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(Constants.size.circularBorderRadius),
),
child: RawMaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () async {
if (txid != null && walletIds != null && walletIds.isNotEmpty) {
final manager = ref
.read(walletsChangeNotifierProvider)
.getManager(walletIds.first);
debugPrint("name: ${manager.walletName}");
// TODO store tx data completely locally in isar so we don't lock up ui here when querying txData
final txData = await manager.transactionData;
final tx = txData.getAllTransactions()[txid];
if (mounted) {
unawaited(
Navigator.of(context).pushNamed(
TradeDetailsView.routeName,
arguments: Tuple4(
tradeId,
tx,
walletIds.first,
manager.walletName,
),
),
);
}
} else {
unawaited(
Navigator.of(context).pushNamed(
TradeDetailsView.routeName,
arguments: Tuple4(
tradeId,
null,
walletIds?.first,
null,
),
),
);
}
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 16,
),
child: Row(
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
),
child: Center(
child: SvgPicture.asset(
_fetchIconAssetForStatus(
trade.status,
context,
),
width: 32,
height: 32,
),
),
),
const SizedBox(
width: 12,
),
Expanded(
flex: 3,
child: Text(
"${trade.payInCurrency.toUpperCase()}${trade.payOutCurrency.toUpperCase()}",
style:
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark,
),
),
),
Expanded(
flex: 4,
child: Text(
Format.extractDateFrom(
trade.timestamp.millisecondsSinceEpoch ~/ 1000),
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
Expanded(
flex: 6,
child: Text(
"-${Decimal.tryParse(trade.payInAmount)?.toStringAsFixed(8) ?? "..."} ${trade.payInCurrency.toUpperCase()}",
style:
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark,
),
),
),
Expanded(
flex: 4,
child: Text(
trade.exchangeName,
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
SvgPicture.asset(
Assets.svg.circleInfo,
width: 20,
height: 20,
color:
Theme.of(context).extension<StackColors>()!.textSubtitle2,
),
],
),
),
),
);
}
}

View file

@ -3,20 +3,20 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart';
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import '../../../route_generator.dart';
import '../../../widgets/desktop/desktop_dialog.dart';
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
class DesktopTradeHistory extends ConsumerStatefulWidget {
const DesktopTradeHistory({Key? key}) : super(key: key);
@ -71,7 +71,8 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
BlueTextButton(
text: "See all",
onTap: () {
// todo display all trades
Navigator.of(context)
.pushNamed(DesktopAllTradesView.routeName);
},
),
],

View file

@ -85,6 +85,7 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_sear
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
import 'package:stackwallet/pages_desktop_specific/forgot_password_desktop_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/desktop_address_book.dart';
@ -96,12 +97,14 @@ import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_vie
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/nodes_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/security_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/settings_menu.dart';
@ -115,9 +118,6 @@ import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:tuple/tuple.dart';
import 'pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
import 'pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart';
class RouteGenerator {
static const bool useMaterialPageRoute = true;
@ -1029,6 +1029,12 @@ class RouteGenerator {
builder: (_) => const DesktopExchangeView(),
settings: RouteSettings(name: settings.name));
case DesktopAllTradesView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const DesktopAllTradesView(),
settings: RouteSettings(name: settings.name));
case DesktopSettingsView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,

View file

@ -11,6 +11,16 @@ class TradesService extends ChangeNotifier {
return list;
}
Trade? get(String tradeId) {
try {
return DB.instance
.values<Trade>(boxName: DB.boxNameTradesV2)
.firstWhere((e) => e.tradeId == tradeId);
} catch (_) {
return null;
}
}
Future<void> add({
required Trade trade,
required bool shouldNotifyListeners,