mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-10 20:54:33 +00:00
WIP group fusions on wallet home screen activity
This commit is contained in:
parent
79d117d7f1
commit
c6a370e4f5
5 changed files with 392 additions and 130 deletions
|
@ -0,0 +1,176 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class FusionTxGroup {
|
||||
final List<TransactionV2> transactions;
|
||||
FusionTxGroup(this.transactions);
|
||||
}
|
||||
|
||||
class FusionTxGroupCard extends ConsumerWidget {
|
||||
const FusionTxGroupCard({super.key, required this.group});
|
||||
|
||||
final FusionTxGroup group;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final walletId = group.transactions.first.walletId;
|
||||
|
||||
final coin = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(walletId).coin));
|
||||
|
||||
final currentHeight = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(walletId).currentHeight));
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(Constants.size.circularBorderRadius),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (Util.isDesktop) {
|
||||
// await showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (context) => DesktopDialog(
|
||||
// maxHeight: MediaQuery.of(context).size.height - 64,
|
||||
// maxWidth: 580,
|
||||
// child: TransactionV2DetailsView(
|
||||
// transaction: _transaction,
|
||||
// coin: coin,
|
||||
// walletId: walletId,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
} else {
|
||||
// unawaited(
|
||||
// Navigator.of(context).pushNamed(
|
||||
// TransactionV2DetailsView.routeName,
|
||||
// arguments: (
|
||||
// tx: _transaction,
|
||||
// coin: coin,
|
||||
// walletId: walletId,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
children: [
|
||||
TxIcon(
|
||||
transaction: group.transactions.first,
|
||||
coin: coin,
|
||||
currentHeight: currentHeight,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
"Fusions",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Builder(
|
||||
builder: (_) {
|
||||
return Text(
|
||||
"${group.transactions.length} fusion transactions",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
Format.extractDateFrom(
|
||||
group.transactions.last.timestamp,
|
||||
),
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
// if (ref.watch(prefsChangeNotifierProvider
|
||||
// .select((value) => value.externalCalls)))
|
||||
// const SizedBox(
|
||||
// width: 10,
|
||||
// ),
|
||||
// if (ref.watch(prefsChangeNotifierProvider
|
||||
// .select((value) => value.externalCalls)))
|
||||
// Flexible(
|
||||
// child: FittedBox(
|
||||
// fit: BoxFit.scaleDown,
|
||||
// child: Builder(
|
||||
// builder: (_) {
|
||||
// return Text(
|
||||
// "$prefix${Amount.fromDecimal(
|
||||
// amount.decimal * price,
|
||||
// fractionDigits: 2,
|
||||
// ).fiatString(
|
||||
// locale: locale,
|
||||
// )} $baseCurrency",
|
||||
// style: STextStyles.label(context),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,8 +13,10 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/fusion_tx_group_card.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
import 'package:stackwallet/providers/db/main_db_provider.dart';
|
||||
|
@ -102,6 +104,59 @@ class _TransactionsV2ListState extends ConsumerState<TransactionsV2List> {
|
|||
return const NoTransActionsFound();
|
||||
} else {
|
||||
_transactions.sort((a, b) => b.timestamp - a.timestamp);
|
||||
|
||||
final List<Object> _txns = [];
|
||||
|
||||
List<TransactionV2> fusions = [];
|
||||
|
||||
for (int i = 0; i < _transactions.length; i++) {
|
||||
final tx = _transactions[i];
|
||||
|
||||
if (tx.subType == TransactionSubType.cashFusion) {
|
||||
if (fusions.isNotEmpty) {
|
||||
final prevTime = DateTime.fromMillisecondsSinceEpoch(
|
||||
fusions.last.timestamp * 1000);
|
||||
final thisTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000);
|
||||
|
||||
print(
|
||||
"DIFFERERNCE: ${prevTime.difference(thisTime).inMinutes}");
|
||||
|
||||
if (prevTime.difference(thisTime).inMinutes > 30) {
|
||||
_txns.add(FusionTxGroup(fusions));
|
||||
fusions = [tx];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fusions.add(tx);
|
||||
}
|
||||
|
||||
if (i + 1 < _transactions.length) {
|
||||
final nextTx = _transactions[i + 1];
|
||||
if (nextTx.subType != TransactionSubType.cashFusion &&
|
||||
fusions.isNotEmpty) {
|
||||
_txns.add(FusionTxGroup(fusions));
|
||||
fusions = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.subType != TransactionSubType.cashFusion) {
|
||||
_txns.add(tx);
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
int count = 0;
|
||||
for (final e in _txns) {
|
||||
if (e is TransactionV2) {
|
||||
count++;
|
||||
} else if (e is FusionTxGroup) {
|
||||
count += e.transactions.length;
|
||||
}
|
||||
}
|
||||
assert(count == _transactions.length);
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
final managerProvider = ref
|
||||
|
@ -116,16 +171,16 @@ class _TransactionsV2ListState extends ConsumerState<TransactionsV2List> {
|
|||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
BorderRadius? radius;
|
||||
if (_transactions.length == 1) {
|
||||
if (_txns.length == 1) {
|
||||
radius = BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
);
|
||||
} else if (index == _transactions.length - 1) {
|
||||
} else if (index == _txns.length - 1) {
|
||||
radius = _borderRadiusLast;
|
||||
} else if (index == 0) {
|
||||
radius = _borderRadiusFirst;
|
||||
}
|
||||
final tx = _transactions[index];
|
||||
final tx = _txns[index];
|
||||
return TxListItem(
|
||||
tx: tx,
|
||||
coin: manager.coin,
|
||||
|
@ -141,24 +196,24 @@ class _TransactionsV2ListState extends ConsumerState<TransactionsV2List> {
|
|||
.background,
|
||||
);
|
||||
},
|
||||
itemCount: _transactions.length,
|
||||
itemCount: _txns.length,
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: _transactions.length,
|
||||
itemCount: _txns.length,
|
||||
itemBuilder: (context, index) {
|
||||
BorderRadius? radius;
|
||||
bool shouldWrap = false;
|
||||
if (_transactions.length == 1) {
|
||||
if (_txns.length == 1) {
|
||||
radius = BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
);
|
||||
} else if (index == _transactions.length - 1) {
|
||||
} else if (index == _txns.length - 1) {
|
||||
radius = _borderRadiusLast;
|
||||
shouldWrap = true;
|
||||
} else if (index == 0) {
|
||||
radius = _borderRadiusFirst;
|
||||
}
|
||||
final tx = _transactions[index];
|
||||
final tx = _txns[index];
|
||||
if (shouldWrap) {
|
||||
return Column(
|
||||
children: [
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/fusion_tx_group_card.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart';
|
||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
|
@ -24,137 +25,156 @@ class TxListItem extends ConsumerWidget {
|
|||
required this.tx,
|
||||
this.radius,
|
||||
required this.coin,
|
||||
});
|
||||
}) : assert(tx is TransactionV2 || tx is FusionTxGroup);
|
||||
|
||||
final TransactionV2 tx;
|
||||
final Object tx;
|
||||
final BorderRadius? radius;
|
||||
final Coin coin;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final matchingTrades = ref
|
||||
.read(tradesServiceProvider)
|
||||
.trades
|
||||
.where((e) => e.payInTxid == tx.txid || e.payOutTxid == tx.txid);
|
||||
if (tx is TransactionV2) {
|
||||
final _tx = tx as TransactionV2;
|
||||
|
||||
if (tx.type == TransactionType.outgoing && matchingTrades.isNotEmpty) {
|
||||
final trade = matchingTrades.first;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TransactionCardV2(
|
||||
key: UniqueKey(),
|
||||
transaction: tx,
|
||||
),
|
||||
TradeCard(
|
||||
key: Key(tx.txid +
|
||||
tx.type.name +
|
||||
tx.hashCode.toString() +
|
||||
trade.uuid), //
|
||||
trade: trade,
|
||||
onTap: () async {
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: TradeDetailsView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Trade details",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: TradeDetailsView(
|
||||
tradeId: trade.tradeId,
|
||||
// TODO
|
||||
// transactionIfSentFromStack: tx,
|
||||
transactionIfSentFromStack: null,
|
||||
walletName: ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value
|
||||
.getManager(tx.walletId)
|
||||
.walletName,
|
||||
),
|
||||
final matchingTrades = ref
|
||||
.read(tradesServiceProvider)
|
||||
.trades
|
||||
.where((e) => e.payInTxid == _tx.txid || e.payOutTxid == _tx.txid);
|
||||
|
||||
if (_tx.type == TransactionType.outgoing && matchingTrades.isNotEmpty) {
|
||||
final trade = matchingTrades.first;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TransactionCardV2(
|
||||
key: UniqueKey(),
|
||||
transaction: _tx,
|
||||
),
|
||||
TradeCard(
|
||||
key: Key(_tx.txid +
|
||||
_tx.type.name +
|
||||
_tx.hashCode.toString() +
|
||||
trade.uuid), //
|
||||
trade: trade,
|
||||
onTap: () async {
|
||||
if (Util.isDesktop) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: TradeDetailsView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Trade details",
|
||||
style:
|
||||
STextStyles.desktopH3(context),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
walletId: tx.walletId,
|
||||
),
|
||||
),
|
||||
],
|
||||
Flexible(
|
||||
child: TradeDetailsView(
|
||||
tradeId: trade.tradeId,
|
||||
// TODO
|
||||
// transactionIfSentFromStack: tx,
|
||||
transactionIfSentFromStack: null,
|
||||
walletName: ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value
|
||||
.getManager(_tx.walletId)
|
||||
.walletName,
|
||||
),
|
||||
),
|
||||
walletId: _tx.walletId,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TradeDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TradeDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TradeDetailsView.routeName,
|
||||
arguments: Tuple4(
|
||||
trade.tradeId,
|
||||
tx,
|
||||
tx.walletId,
|
||||
ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(tx.walletId)
|
||||
.walletName,
|
||||
];
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: TransactionCardV2(
|
||||
// this may mess with combined firo transactions
|
||||
key: UniqueKey(),
|
||||
transaction: tx,
|
||||
),
|
||||
);
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TradeDetailsView.routeName,
|
||||
arguments: Tuple4(
|
||||
trade.tradeId,
|
||||
_tx,
|
||||
_tx.walletId,
|
||||
ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(_tx.walletId)
|
||||
.walletName,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: TransactionCardV2(
|
||||
// this may mess with combined firo transactions
|
||||
key: UniqueKey(),
|
||||
transaction: _tx,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final group = tx as FusionTxGroup;
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: FusionTxGroupCard(
|
||||
// this may mess with combined firo transactions
|
||||
key: UniqueKey(),
|
||||
group: group,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -446,7 +446,7 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
|
|||
.getManager(widget.walletId)
|
||||
.hasTokenSupport))
|
||||
? "Tokens"
|
||||
: "Recent transactions",
|
||||
: "Recent activity",
|
||||
style: STextStyles.desktopTextExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
|
|
|
@ -50,16 +50,27 @@ abstract class Format {
|
|||
// }
|
||||
|
||||
// format date string from unix timestamp
|
||||
static String extractDateFrom(int timestamp, {bool localized = true}) {
|
||||
static String extractDateFrom(
|
||||
int timestamp, {
|
||||
bool localized = true,
|
||||
bool noTime = false,
|
||||
}) {
|
||||
var date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||
|
||||
if (!localized) {
|
||||
date = date.toUtc();
|
||||
}
|
||||
|
||||
final dayAndYear =
|
||||
"${date.day} ${Constants.monthMapShort[date.month]} ${date.year}";
|
||||
|
||||
if (noTime) {
|
||||
return dayAndYear;
|
||||
}
|
||||
|
||||
final minutes =
|
||||
date.minute < 10 ? "0${date.minute}" : date.minute.toString();
|
||||
return "${date.day} ${Constants.monthMapShort[date.month]} ${date.year}, ${date.hour}:$minutes";
|
||||
return "$dayAndYear, ${date.hour}:$minutes";
|
||||
}
|
||||
|
||||
// static String localizedStringAsFixed({
|
||||
|
|
Loading…
Reference in a new issue