Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion mobile-app/lib/features/components/notification_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/features/components/transaction_list_item.dart';
import 'package:resonance_network_wallet/models/notification_models.dart';
import 'package:resonance_network_wallet/providers/account_providers.dart';
import 'package:resonance_network_wallet/services/transaction_service.dart';

class NotificationCard extends ConsumerStatefulWidget {
Expand Down Expand Up @@ -141,7 +142,18 @@ class _NotificationCardState extends ConsumerState<NotificationCard> with Ticker
final transaction = txService.deserializeTxEventFromJsonIfPossible(widget.notification.metadata);

if (transaction != null) {
showTransactionActionSheet(context, transaction: transaction, role: txService.getTransactionRole(transaction));
final role = txService.getTransactionRole(transaction);
final activeAccount = ref.read(activeAccountProvider).value;
showTransactionActionSheet(
context,
transaction: transaction,
role: role,
config: TransactionService.getTransactionDetailViewConfig(
transaction: transaction,
role: role,
activeAccount: activeAccount,
),
);
}
}

Expand Down
58 changes: 33 additions & 25 deletions mobile-app/lib/features/components/transaction_list_item.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/features/components/skeleton.dart';
Expand All @@ -11,16 +10,23 @@ import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
import 'package:resonance_network_wallet/features/styles/app_size_theme.dart';
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
import 'package:resonance_network_wallet/models/transaction_role.dart';
import 'package:resonance_network_wallet/providers/account_providers.dart';
import 'package:resonance_network_wallet/services/transaction_service.dart';
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
import 'package:resonance_network_wallet/shared/extensions/transaction_event_extension.dart';

class TransactionListItem extends StatefulWidget {
final TransactionEvent transaction;
final TransactionRole role;
final bool showFromAndTo;
final TransactionDetailViewConfig actionSheetConfig;

const TransactionListItem({super.key, required this.transaction, required this.role, this.showFromAndTo = true});
const TransactionListItem({
super.key,
required this.transaction,
required this.role,
this.showFromAndTo = true,
this.actionSheetConfig = TransactionDetailViewConfig.normal,
});

@override
TransactionListItemState createState() => TransactionListItemState();
Expand Down Expand Up @@ -144,7 +150,12 @@ class TransactionListItemState extends State<TransactionListItem> {

return InkWell(
onTap: () {
showTransactionActionSheet(context, transaction: widget.transaction, role: widget.role);
showTransactionActionSheet(
context,
transaction: widget.transaction,
role: widget.role,
config: widget.actionSheetConfig,
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
Expand Down Expand Up @@ -234,31 +245,28 @@ class TransactionListItemState extends State<TransactionListItem> {
}
}

void showTransactionActionSheet(BuildContext context, {required TransactionEvent transaction, required role}) {
final container = ProviderScope.containerOf(context, listen: false);
final activeDisplayAccount = container.read(activeAccountProvider).value;
EntrustedAccount? entrustedAccount;
if (activeDisplayAccount is EntrustedDisplayAccount) {
entrustedAccount = activeDisplayAccount.account;
}
final isEntrustedAccount = entrustedAccount != null;

void showTransactionActionSheet(
BuildContext context, {
required TransactionEvent transaction,
required TransactionRole role,
required TransactionDetailViewConfig config,
}) {
Widget sheet;

if (transaction is ReversibleTransferEvent) {
final reversibleTx = transaction;
if ((reversibleTx.isReversibleScheduled || reversibleTx.isReversibleCancelled) &&
(role == TransactionRole.sender || role == TransactionRole.both)) {
switch (config.type) {
case TransactionViewType.normal:
sheet = TransactionDetailsActionSheet(transaction: transaction, role: role);
case TransactionViewType.reversible:
sheet = ReversibleTransactionActionSheet(
transaction: reversibleTx,
mode: isEntrustedAccount ? ReversibleTransactionMode.guardianIntercept : ReversibleTransactionMode.reversible,
entrustedAccount: entrustedAccount,
transaction: transaction as ReversibleTransferEvent,
mode: ReversibleTransactionMode.reversible,
);
case TransactionViewType.guardianIntercept:
sheet = ReversibleTransactionActionSheet(
transaction: transaction as ReversibleTransferEvent,
mode: ReversibleTransactionMode.guardianIntercept,
entrustedAccount: config.entrustedAccount,
);
} else {
sheet = TransactionDetailsActionSheet(transaction: transaction, role: role);
}
} else {
sheet = TransactionDetailsActionSheet(transaction: transaction, role: role);
}

showModalBottomSheet(
Expand Down
35 changes: 20 additions & 15 deletions mobile-app/lib/features/components/transactions_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/features/components/transaction_list_item.dart';
import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
import 'package:resonance_network_wallet/providers/account_providers.dart';
import 'package:resonance_network_wallet/services/transaction_service.dart';

class RecentTransactionsList extends ConsumerWidget {
final List<TransactionEvent> transactions;
final List<String> accountIds; // List of account IDs we're showing transactions for
final List<String> accountIds;
final bool Function(TransactionEvent)? filter;
final Color backgroundColor;

Expand All @@ -23,6 +24,7 @@ class RecentTransactionsList extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final txService = ref.read(transactionServiceProvider);
final activeAccount = ref.watch(activeAccountProvider).value;
final transactionsToShow = filter == null ? transactions : transactions.where(filter!).toList();

final scheduled = transactionsToShow
Expand All @@ -37,6 +39,21 @@ class RecentTransactionsList extends ConsumerWidget {
return true;
}).toList();

Widget buildItem(TransactionEvent transaction) {
final role = txService.getTransactionRole(transaction, accountIds: accountIds);
return TransactionListItem(
key: ValueKey(transaction.id),
transaction: transaction,
role: role,
showFromAndTo: accountIds.length > 1,
actionSheetConfig: TransactionService.getTransactionDetailViewConfig(
transaction: transaction,
role: role,
activeAccount: activeAccount,
),
);
}

return Container(
padding: const EdgeInsets.all(10),
decoration: ShapeDecoration(
Expand All @@ -62,15 +79,9 @@ class RecentTransactionsList extends ConsumerWidget {
padding: EdgeInsets.zero,
itemCount: scheduled.length,
itemBuilder: (context, index) {
final transaction = scheduled[index];
return Padding(
padding: EdgeInsets.only(top: index == 0 ? 7.0 : 0),
child: TransactionListItem(
key: ValueKey(transaction.id),
transaction: transaction,
role: txService.getTransactionRole(transaction, accountIds: accountIds),
showFromAndTo: accountIds.length > 1,
),
child: buildItem(scheduled[index]),
);
},
separatorBuilder: (context, index) => const _Divider(),
Expand All @@ -87,15 +98,9 @@ class RecentTransactionsList extends ConsumerWidget {
padding: EdgeInsets.zero,
itemCount: others.length,
itemBuilder: (context, index) {
final transaction = others[index];
return Padding(
padding: EdgeInsets.only(top: index == 0 ? 7.0 : 0),
child: TransactionListItem(
key: ValueKey(transaction.id),
transaction: transaction,
role: txService.getTransactionRole(transaction, accountIds: accountIds),
showFromAndTo: accountIds.length > 1,
),
child: buildItem(others[index]),
);
},
separatorBuilder: (context, index) => const _Divider(),
Expand Down
14 changes: 12 additions & 2 deletions mobile-app/lib/features/main/screens/transactions_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,19 @@ class _TransactionsScreenState extends ConsumerState<TransactionsScreen> {

final txIntent = ref.read(transactionIntentProvider);
if (txIntent != null) {
// After we consume the intent, we clean it up
ref.read(transactionIntentProvider.notifier).state = null;
showTransactionActionSheet(context, transaction: txIntent, role: txService.getTransactionRole(txIntent));
final role = txService.getTransactionRole(txIntent);
final activeAccount = ref.read(activeAccountProvider).value;
showTransactionActionSheet(
context,
transaction: txIntent,
role: role,
config: TransactionService.getTransactionDetailViewConfig(
transaction: txIntent,
role: role,
activeAccount: activeAccount,
),
);
}
});
}
Expand Down
43 changes: 43 additions & 0 deletions mobile-app/lib/services/transaction_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ final transactionServiceProvider = Provider<TransactionService>((ref) {
return TransactionService(ref);
});

enum TransactionViewType { normal, reversible, guardianIntercept }

class TransactionDetailViewConfig {
final TransactionViewType type;
final EntrustedAccount? entrustedAccount;

const TransactionDetailViewConfig({required this.type, this.entrustedAccount});

static const normal = TransactionDetailViewConfig(type: TransactionViewType.normal);
}

class TransactionService {
final Ref _ref;

Expand Down Expand Up @@ -94,4 +105,36 @@ class TransactionService {

return event;
}

/// Basically deciding whether or not to show a reversible or intercept user interface,
/// or whether to just show a normal transaction detail view.
static TransactionDetailViewConfig getTransactionDetailViewConfig({
required TransactionEvent transaction,
required TransactionRole role,
required DisplayAccount? activeAccount,
}) {
if (transaction is! ReversibleTransferEvent) {
return TransactionDetailViewConfig.normal;
}

final isScheduled = transaction.status == ReversibleTransferStatus.SCHEDULED;
final isCancelled = transaction.status == ReversibleTransferStatus.CANCELLED;
// Reversible transaction UX is shown for scheduled transactions, so user can intercept / revert them.
// It is also shown for canceled transactions, showing a "this transaction was canceled/intercepted/reverted" message.
final showReversibleTransactionUX = isScheduled || isCancelled;
final isActorRole = role == TransactionRole.sender || role == TransactionRole.both;

if (!showReversibleTransactionUX || !isActorRole) {
return TransactionDetailViewConfig.normal;
}

if (activeAccount is EntrustedDisplayAccount) {
return TransactionDetailViewConfig(
type: TransactionViewType.guardianIntercept,
entrustedAccount: activeAccount.account,
);
}

return const TransactionDetailViewConfig(type: TransactionViewType.reversible);
}
}