From e7877358daf9160bf90efed73ee09e6df90f6509 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 10 Jan 2023 17:53:09 -0600 Subject: [PATCH] temp. branching of transaction model type for dogecoin wallets --- .../sub_widgets/transactions_list.dart | 416 ++++++++++++++---- .../wallet_view/sub_widgets/tx_icon.dart | 79 +++- lib/widgets/transaction_card.dart | 280 +++++++++++- 3 files changed, 678 insertions(+), 97 deletions(-) diff --git a/lib/pages/wallet_view/sub_widgets/transactions_list.dart b/lib/pages/wallet_view/sub_widgets/transactions_list.dart index 667d46aae..ea78984b9 100644 --- a/lib/pages/wallet_view/sub_widgets/transactions_list.dart +++ b/lib/pages/wallet_view/sub_widgets/transactions_list.dart @@ -2,14 +2,19 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/models/paymint/transactions_model.dart'; +import 'package:isar/isar.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/pages/exchange_view/trade_details_view.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart'; +import 'package:stackwallet/providers/blockchain/dogecoin/current_height_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/services/coins/dogecoin/dogecoin_wallet.dart'; import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/utilities/util.dart'; @@ -37,11 +42,12 @@ class TransactionsList extends ConsumerStatefulWidget { class _TransactionsListState extends ConsumerState { // bool _hasLoaded = false; - Map _transactions = {}; + Map _transactions = {}; + List _transactions2 = []; late final ChangeNotifierProvider managerProvider; - void updateTransactions(TransactionData newData) { + void updateTransactions(old.TransactionData newData) { _transactions = {}; final newTransactions = newData.txChunks.expand((element) => element.transactions); @@ -73,7 +79,10 @@ class _TransactionsListState extends ConsumerState { } Widget itemBuilder( - BuildContext context, Transaction tx, BorderRadius? radius) { + BuildContext context, + old.Transaction tx, + BorderRadius? radius, + ) { final matchingTrades = ref .read(tradesServiceProvider) .trades @@ -190,6 +199,134 @@ class _TransactionsListState extends ConsumerState { } } + Widget itemBuilder2( + BuildContext context, + Transaction tx, + BorderRadius? radius, + ) { + 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()!.popupBG, + borderRadius: radius, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TransactionCard2( + // this may mess with combined firo transactions + key: Key(tx.toString()), // + transaction: tx, + walletId: widget.walletId, + ), + TradeCard( + // this may mess with combined firo transactions + key: Key(tx.toString() + trade.uuid), // + trade: trade, + onTap: () async { + if (Util.isDesktop) { + // await showDialog( + // 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, + // transactionIfSentFromStack: tx, + // walletName: + // ref.read(managerProvider).walletName, + // walletId: widget.walletId, + // ), + // ), + // ], + // ), + // ), + // const RouteSettings( + // name: TradeDetailsView.routeName, + // ), + // ), + // ]; + // }, + // ), + // ); + } else { + unawaited( + Navigator.of(context).pushNamed( + TradeDetailsView.routeName, + arguments: Tuple4( + trade.tradeId, + tx, + widget.walletId, + ref.read(managerProvider).walletName, + ), + ), + ); + } + }, + ) + ], + ), + ); + } else { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).extension()!.popupBG, + borderRadius: radius, + ), + child: TransactionCard2( + // this may mess with combined firo transactions + key: Key(tx.toString()), // + transaction: tx, + walletId: widget.walletId, + ), + ); + } + } + + void updateHeightProvider(Manager manager) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + ref.read(currentHeightProvider(manager.coin).state).state = + manager.currentHeight; + }); + } + @override void initState() { managerProvider = widget.managerProvider; @@ -202,94 +339,189 @@ class _TransactionsListState extends ConsumerState { // .watch(walletsChangeNotifierProvider) // .getManagerProvider(widget.walletId); - return FutureBuilder( - future: - ref.watch(managerProvider.select((value) => value.transactionData)), - builder: (fbContext, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done && - snapshot.hasData) { - updateTransactions(snapshot.data!); - _hasLoaded = true; - } - if (!_hasLoaded) { - return Column( - children: const [ - Spacer(), - Center( - child: LoadingIndicator( - height: 50, - width: 50, - ), - ), - Spacer( - flex: 4, - ), - ], - ); - } - if (_transactions.isEmpty) { - return const NoTransActionsFound(); - } else { - final list = _transactions.values.toList(growable: false); - list.sort((a, b) => b.timestamp - a.timestamp); - return RefreshIndicator( - onRefresh: () async { - //todo: check if print needed - // debugPrint("pulled down to refresh on transaction list"); - final managerProvider = ref - .read(walletsChangeNotifierProvider) - .getManagerProvider(widget.walletId); - if (!ref.read(managerProvider).isRefreshing) { - unawaited(ref.read(managerProvider).refresh()); - } - }, - child: Util.isDesktop - ? ListView.separated( - itemBuilder: (context, index) { - BorderRadius? radius; - if (list.length == 1) { - radius = BorderRadius.circular( - Constants.size.circularBorderRadius, - ); - } else if (index == list.length - 1) { - radius = _borderRadiusLast; - } else if (index == 0) { - radius = _borderRadiusFirst; - } - final tx = list[index]; - return itemBuilder(context, tx, radius); - }, - separatorBuilder: (context, index) { - return Container( - width: double.infinity, - height: 2, - color: Theme.of(context) - .extension()! - .background, - ); - }, - itemCount: list.length, - ) - : ListView.builder( - itemCount: list.length, - itemBuilder: (context, index) { - BorderRadius? radius; - if (list.length == 1) { - radius = BorderRadius.circular( - Constants.size.circularBorderRadius, - ); - } else if (index == list.length - 1) { - radius = _borderRadiusLast; - } else if (index == 0) { - radius = _borderRadiusFirst; - } - final tx = list[index]; - return itemBuilder(context, tx, radius); - }, + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId))); + + if (manager.coin == Coin.dogecoinTestNet) { + updateHeightProvider(manager); + final wallet = manager.wallet as DogecoinWallet; + return FutureBuilder( + future: wallet.isar.transactions.where().findAll(), + builder: (fbContext, AsyncSnapshot> snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + _transactions2 = snapshot.data!; + _hasLoaded = true; + } + if (!_hasLoaded) { + return Column( + children: const [ + Spacer(), + Center( + child: LoadingIndicator( + height: 50, + width: 50, ), - ); - } - }, - ); + ), + Spacer( + flex: 4, + ), + ], + ); + } + if (_transactions2.isEmpty) { + return const NoTransActionsFound(); + } else { + _transactions2.sort((a, b) => b.timestamp - a.timestamp); + return RefreshIndicator( + onRefresh: () async { + //todo: check if print needed + // debugPrint("pulled down to refresh on transaction list"); + final managerProvider = ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(widget.walletId); + if (!ref.read(managerProvider).isRefreshing) { + unawaited(ref.read(managerProvider).refresh()); + } + }, + child: Util.isDesktop + ? ListView.separated( + itemBuilder: (context, index) { + BorderRadius? radius; + if (_transactions2.length == 1) { + radius = BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == _transactions2.length - 1) { + radius = _borderRadiusLast; + } else if (index == 0) { + radius = _borderRadiusFirst; + } + final tx = _transactions2[index]; + return itemBuilder2(context, tx, radius); + }, + separatorBuilder: (context, index) { + return Container( + width: double.infinity, + height: 2, + color: Theme.of(context) + .extension()! + .background, + ); + }, + itemCount: _transactions2.length, + ) + : ListView.builder( + itemCount: _transactions2.length, + itemBuilder: (context, index) { + BorderRadius? radius; + if (_transactions2.length == 1) { + radius = BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == _transactions2.length - 1) { + radius = _borderRadiusLast; + } else if (index == 0) { + radius = _borderRadiusFirst; + } + final tx = _transactions2[index]; + return itemBuilder2(context, tx, radius); + }, + ), + ); + } + }, + ); + } else { + return FutureBuilder( + future: + ref.watch(managerProvider.select((value) => value.transactionData)), + builder: (fbContext, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + updateTransactions(snapshot.data!); + _hasLoaded = true; + } + if (!_hasLoaded) { + return Column( + children: const [ + Spacer(), + Center( + child: LoadingIndicator( + height: 50, + width: 50, + ), + ), + Spacer( + flex: 4, + ), + ], + ); + } + if (_transactions.isEmpty) { + return const NoTransActionsFound(); + } else { + final list = _transactions.values.toList(growable: false); + list.sort((a, b) => b.timestamp - a.timestamp); + return RefreshIndicator( + onRefresh: () async { + //todo: check if print needed + // debugPrint("pulled down to refresh on transaction list"); + final managerProvider = ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(widget.walletId); + if (!ref.read(managerProvider).isRefreshing) { + unawaited(ref.read(managerProvider).refresh()); + } + }, + child: Util.isDesktop + ? ListView.separated( + itemBuilder: (context, index) { + BorderRadius? radius; + if (list.length == 1) { + radius = BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == list.length - 1) { + radius = _borderRadiusLast; + } else if (index == 0) { + radius = _borderRadiusFirst; + } + final tx = list[index]; + return itemBuilder(context, tx, radius); + }, + separatorBuilder: (context, index) { + return Container( + width: double.infinity, + height: 2, + color: Theme.of(context) + .extension()! + .background, + ); + }, + itemCount: list.length, + ) + : ListView.builder( + itemCount: list.length, + itemBuilder: (context, index) { + BorderRadius? radius; + if (list.length == 1) { + radius = BorderRadius.circular( + Constants.size.circularBorderRadius, + ); + } else if (index == list.length - 1) { + radius = _borderRadiusLast; + } else if (index == 0) { + radius = _borderRadiusFirst; + } + final tx = list[index]; + return itemBuilder(context, tx, radius); + }, + ), + ); + } + }, + ); + } } } diff --git a/lib/pages/wallet_view/sub_widgets/tx_icon.dart b/lib/pages/wallet_view/sub_widgets/tx_icon.dart index 6222301a6..6d96cb279 100644 --- a/lib/pages/wallet_view/sub_widgets/tx_icon.dart +++ b/lib/pages/wallet_view/sub_widgets/tx_icon.dart @@ -1,11 +1,13 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/models/paymint/transactions_model.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; +import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; class TxIcon extends StatelessWidget { const TxIcon({Key? key, required this.transaction}) : super(key: key); - final Transaction transaction; + final old.Transaction transaction; static const Size size = Size(32, 32); @@ -62,3 +64,76 @@ class TxIcon extends StatelessWidget { ); } } + +class TxIcon2 extends StatelessWidget { + const TxIcon2({ + Key? key, + required this.transaction, + required this.currentHeight, + required this.coin, + }) : super(key: key); + + final isar_models.Transaction transaction; + final int currentHeight; + final Coin coin; + + static const Size size = Size(32, 32); + + String _getAssetName( + bool isCancelled, bool isReceived, bool isPending, BuildContext context) { + if (!isReceived && + transaction.subType == isar_models.TransactionSubType.mint) { + if (isCancelled) { + return Assets.svg.anonymizeFailed; + } + if (isPending) { + return Assets.svg.anonymizePending; + } + return Assets.svg.anonymize; + } + + if (isReceived) { + if (isCancelled) { + return Assets.svg.receiveCancelled(context); + } + if (isPending) { + return Assets.svg.receivePending(context); + } + return Assets.svg.receive(context); + } else { + if (isCancelled) { + return Assets.svg.sendCancelled(context); + } + if (isPending) { + return Assets.svg.sendPending(context); + } + return Assets.svg.send(context); + } + } + + @override + Widget build(BuildContext context) { + final txIsReceived = + transaction.type == isar_models.TransactionType.incoming; + + return SizedBox( + width: size.width, + height: size.height, + child: Center( + child: SvgPicture.asset( + _getAssetName( + transaction.cancelled, + txIsReceived, + !transaction.isConfirmed( + currentHeight, + coin.requiredConfirmations, + ), + context, + ), + width: size.width, + height: size.height, + ), + ), + ); + } +} diff --git a/lib/widgets/transaction_card.dart b/lib/widgets/transaction_card.dart index 4389573c3..29139a5a7 100644 --- a/lib/widgets/transaction_card.dart +++ b/lib/widgets/transaction_card.dart @@ -2,7 +2,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/models/paymint/transactions_model.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; +import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/tx_icon.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart'; @@ -16,6 +17,8 @@ import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; import 'package:tuple/tuple.dart'; +import '../providers/blockchain/dogecoin/current_height_provider.dart'; + class TransactionCard extends ConsumerStatefulWidget { const TransactionCard({ Key? key, @@ -23,7 +26,7 @@ class TransactionCard extends ConsumerStatefulWidget { required this.walletId, }) : super(key: key); - final Transaction transaction; + final old.Transaction transaction; final String walletId; @override @@ -31,7 +34,7 @@ class TransactionCard extends ConsumerStatefulWidget { } class _TransactionCardState extends ConsumerState { - late final Transaction _transaction; + late final old.Transaction _transaction; late final String walletId; String whatIsIt(String type, Coin coin) { @@ -266,3 +269,274 @@ class _TransactionCardState extends ConsumerState { ); } } + +class TransactionCard2 extends ConsumerStatefulWidget { + const TransactionCard2({ + Key? key, + required this.transaction, + required this.walletId, + }) : super(key: key); + + final isar_models.Transaction transaction; + final String walletId; + + @override + ConsumerState createState() => _TransactionCardState2(); +} + +class _TransactionCardState2 extends ConsumerState { + late final isar_models.Transaction _transaction; + late final String walletId; + + String whatIsIt( + isar_models.TransactionType type, + Coin coin, + int currentHeight, + ) { + if (coin == Coin.epicCash && _transaction.slateId == null) { + return "Restored Funds"; + } + + final confirmedStatus = _transaction.isConfirmed( + currentHeight, + coin.requiredConfirmations, + ); + + if (_transaction.subType == isar_models.TransactionSubType.mint) { + // if (type == "Received") { + if (confirmedStatus) { + return "Anonymized"; + } else { + return "Anonymizing"; + } + // } else if (type == "Sent") { + // if (_transaction.confirmedStatus) { + // return "Sent MINT"; + // } else { + // return "Sending MINT"; + // } + // } else { + // return type; + // } + } + + if (type == isar_models.TransactionType.incoming) { + // if (_transaction.isMinting) { + // return "Minting"; + // } else + if (confirmedStatus) { + return "Received"; + } else { + return "Receiving"; + } + } else if (type == isar_models.TransactionType.outgoing) { + if (confirmedStatus) { + return "Sent"; + } else { + return "Sending"; + } + } else { + return type.name; + } + } + + @override + void initState() { + walletId = widget.walletId; + _transaction = widget.transaction; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final locale = ref.watch( + localeServiceChangeNotifierProvider.select((value) => value.locale)); + final manager = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(walletId))); + + final baseCurrency = ref + .watch(prefsChangeNotifierProvider.select((value) => value.currency)); + + final coin = manager.coin; + + final price = ref + .watch(priceAnd24hChangeNotifierProvider + .select((value) => value.getPrice(coin))) + .item1; + + String prefix = ""; + if (Util.isDesktop) { + if (_transaction.type == isar_models.TransactionType.outgoing) { + prefix = "-"; + } else if (_transaction.type == isar_models.TransactionType.incoming) { + prefix = "+"; + } + } + + final currentHeight = ref.watch(currentHeightProvider(coin).state).state; + + return Material( + color: Theme.of(context).extension()!.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 (coin == Coin.epicCash && _transaction.slateId == null) { + unawaited(showFloatingFlushBar( + context: context, + message: + "Restored Epic funds from your Seed have no Data.\nUse Stack Backup to keep your transaction history.", + type: FlushBarType.warning, + duration: const Duration(seconds: 5), + )); + return; + } + if (Util.isDesktop) { + // await showDialog( + // context: context, + // builder: (context) => DesktopDialog( + // maxHeight: MediaQuery.of(context).size.height - 64, + // maxWidth: 580, + // child: TransactionDetailsView( + // transaction: _transaction, + // coin: coin, + // walletId: walletId, + // ), + // ), + // ); + } else { + unawaited( + Navigator.of(context).pushNamed( + TransactionDetailsView.routeName, + arguments: Tuple3( + _transaction, + coin, + walletId, + ), + ), + ); + } + }, + child: Padding( + padding: const EdgeInsets.all(8), + child: Row( + children: [ + TxIcon2( + transaction: _transaction, + coin: ref.watch(walletsChangeNotifierProvider.select( + (value) => value.getManager(widget.walletId).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( + _transaction.cancelled + ? "Cancelled" + : whatIsIt( + _transaction.type, + coin, + currentHeight, + ), + style: STextStyles.itemSubtitle12(context), + ), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Builder( + builder: (_) { + final amount = _transaction.amount; + return Text( + "$prefix${Format.satoshiAmountToPrettyString(amount, locale, coin)} ${coin.ticker}", + style: + STextStyles.itemSubtitle12_600(context), + ); + }, + ), + ), + ), + ], + ), + const SizedBox( + height: 4, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + // crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + Format.extractDateFrom(_transaction.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: (_) { + int value = _transaction.amount; + + return Text( + "$prefix${Format.localizedStringAsFixed( + value: Format.satoshisToAmount(value, + coin: coin) * + price, + locale: locale, + decimalPlaces: 2, + )} $baseCurrency", + style: STextStyles.label(context), + ); + }, + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } +}