Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:resonance_network_wallet/shared/extensions/current_route_extensi
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
import 'package:resonance_network_wallet/v2/components/quantus_button.dart';
import 'package:resonance_network_wallet/v2/screens/send/input_amount_screen.dart';
import 'package:resonance_network_wallet/v2/screens/send/regular_send_strategy.dart';

class SharedAddressActionSheet extends StatefulWidget {
final String address;
Expand Down Expand Up @@ -58,7 +59,12 @@ class _SharedAddressActionSheetState extends State<SharedAddressActionSheet> {

void _sendToAddress() {
Navigator.of(context).pop();
Navigator.push(context, MaterialPageRoute(builder: (_) => InputAmountScreen(recipientAddress: widget.address)));
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => InputAmountScreen(strategy: const RegularSendStrategy(), recipientAddress: widget.address),
),
);
}

void _closeSheet() {
Expand Down
2 changes: 1 addition & 1 deletion mobile-app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2461,7 +2461,7 @@
"description": "Empty state on refund address picker"
},

"componentQrScannerTitle": "Scan QR Code",
"componentQrScannerTitle": "X QR Code",
"@componentQrScannerTitle": {
"description": "Text for app bar or button label on QR scanner component"
},
Expand Down
2 changes: 1 addition & 1 deletion mobile-app/lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3179,7 +3179,7 @@ abstract class AppLocalizations {
/// Text for app bar or button label on QR scanner component
///
/// In en, this message translates to:
/// **'Scan QR Code'**
/// **'X QR Code'**
String get componentQrScannerTitle;

/// Snackbar when gallery image has no QR code
Expand Down
2 changes: 1 addition & 1 deletion mobile-app/lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1689,7 +1689,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get swapRefundPickerEmpty => 'No recent refund addresses';

@override
String get componentQrScannerTitle => 'Scan QR Code';
String get componentQrScannerTitle => 'X QR Code';

@override
String get componentQrScannerNoCode => 'No QR code found in image';
Expand Down
17 changes: 12 additions & 5 deletions mobile-app/lib/services/transaction_submission_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class TransactionSubmissionService {

TransactionSubmissionService(this._ref) : _poller = PendingTransactionPollingService(_ref);

Future<void> balanceTransfer(
Future<String> balanceTransfer(
Account account,
String targetAddress,
BigInt amount,
Expand All @@ -52,13 +52,16 @@ class TransactionSubmissionService {
TelemetryService().sendEvent('send_transfer');

// C. Submit and track the transaction
await submitAndTrackTransaction(() => BalancesService().balanceTransfer(account, targetAddress, amount), pendingTx);
return submitAndTrackTransaction(
() => BalancesService().balanceTransfer(account, targetAddress, amount),
pendingTx,
);
}

/// Broadcasts a transfer whose signature was produced off-device (e.g. by a
/// Keystone hardware wallet). The [unsignedData] is rebuilt into an extrinsic
/// using the externally provided [signature] and [publicKey].
Future<void> submitExternallySignedTransfer({
Future<String> submitExternallySignedTransfer({
required Account account,
required String targetAddress,
required BigInt amount,
Expand All @@ -80,7 +83,7 @@ class TransactionSubmissionService {

TelemetryService().sendEvent('send_transfer_hardware');

await submitAndTrackTransaction(
return submitAndTrackTransaction(
() => SubstrateService().submitExtrinsicWithExternalSignature(unsignedData, signature, publicKey),
pendingTx,
);
Expand Down Expand Up @@ -400,7 +403,10 @@ class TransactionSubmissionService {
/// Retries live in SubstrateService.submitExtrinsic, which resubmits the
/// same signed bytes. Outer retries would re-sign with a fresh nonce and can
/// double spend if a prior submit already reached the network.
Future<void> submitAndTrackTransaction(Future<Uint8List> Function() submit, PendingTransactionEvent pendingTx) async {
Future<String> submitAndTrackTransaction(
Future<Uint8List> Function() submit,
PendingTransactionEvent pendingTx,
) async {
try {
quantusDebugPrint('Submitting transaction: ${pendingTx.id}');

Expand All @@ -413,6 +419,7 @@ class TransactionSubmissionService {
.updateState(pendingTx.id, TransactionState.pending, error: pendingTx.error, extrinsicHash: extrinsicHash);

_startPollingForTransaction(pendingTx.copyWith(extrinsicHash: extrinsicHash));
return extrinsicHash;
} catch (e, stackTrace) {
quantusDebugPrint('Failed to submit transaction ${pendingTx.id}: $e');
quantusDebugPrint('Stack trace: $stackTrace');
Expand Down
5 changes: 5 additions & 0 deletions mobile-app/lib/shared/utils/url_utils.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:url_launcher/url_launcher.dart';

/// Block-explorer URL for an immediate (single-signer) transfer extrinsic.
String explorerImmediateTransactionUrl(String extrinsicHash) =>
'${AppConstants.explorerEndpoint}/immediate-transactions/$extrinsicHash';

Future<void> launchXPost(String xUrl) async {
final match = RegExp(r'/status/(\d+)').firstMatch(xUrl);
if (match != null) {
Expand Down
39 changes: 39 additions & 0 deletions mobile-app/lib/v2/components/explorer_link.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:resonance_network_wallet/providers/l10n_provider.dart';
import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';

/// Underlined "View in Explorer ↗" link that opens [url] in an external
/// browser. Shared across the send terminal, POS receipt and the
/// transaction/proposal detail sheets. Renders disabled (non-tappable) when
/// [url] is null or [enabled] is false; [color] defaults to the tertiary text.
class ExplorerLink extends ConsumerWidget {
final String? url;
final Color? color;
final bool enabled;

const ExplorerLink({super.key, required this.url, this.color, this.enabled = true});

@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = ref.watch(l10nProvider);
final linkColor = color ?? context.colors.textTertiary;
final active = enabled && url != null;

return GestureDetector(
onTap: active ? () => openUrl(url!) : null,
child: Container(
padding: const EdgeInsets.only(bottom: 3),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: linkColor, width: 1)),
),
child: Text(
l10n.activityDetailViewExplorer,
style: context.themeText.smallParagraph?.copyWith(color: linkColor, fontWeight: FontWeight.w400),
),
),
);
}
}
16 changes: 15 additions & 1 deletion mobile-app/lib/v2/components/qr_scanner_page.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/providers/l10n_provider.dart';
import 'package:resonance_network_wallet/v2/components/quantus_icon_button.dart';
import 'package:resonance_network_wallet/v2/components/v2_app_bar.dart';
Expand All @@ -26,9 +28,13 @@ class _QrScannerPageState extends ConsumerState<QrScannerPage> {
}

void _onDetect(BarcodeCapture capture) {
if (_scanned) return;
final code = capture.barcodes.firstOrNull?.rawValue;
if (code == null || code.isEmpty) return;
_handleCode(code);
}

void _handleCode(String code) {
if (_scanned) return;
if (widget.validator != null && !widget.validator!(code)) return;
_scanned = true;
Navigator.pop(context, code);
Expand Down Expand Up @@ -84,6 +90,14 @@ class _QrScannerPageState extends ConsumerState<QrScannerPage> {
),
const SizedBox(width: 8),
_actionButton(icon: Icons.image_outlined, onTap: _pickImage, colors: colors),
if (kDebugMode) ...[
const SizedBox(width: 8),
_actionButton(
icon: Icons.bug_report,
onTap: () => _handleCode(AppConstants.debugTestAddress),
colors: colors,
),
],
],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class _AddHardwareAccountScreenState extends ConsumerState<AddHardwareAccountScr
}

void _fillDebugAddress() {
_address.text = 'qzn5St24cMsjE4JKYdXLBctusWj5zom67dnrW22SweAahLGeG';
_address.text = AppConstants.debugTestAddress;
if (_error != null) setState(() => _error = null);
}

Expand Down
27 changes: 6 additions & 21 deletions mobile-app/lib/v2/screens/activity/transaction_detail_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import 'package:resonance_network_wallet/providers/wallet_providers.dart';
import 'package:resonance_network_wallet/routes.dart';
import 'package:resonance_network_wallet/shared/extensions/current_route_extensions.dart';
import 'package:resonance_network_wallet/shared/extensions/transaction_event_extension.dart';
import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/v2/components/amount_display_with_conversion.dart';
import 'package:resonance_network_wallet/v2/components/bottom_sheet_container.dart';
import 'package:resonance_network_wallet/v2/components/explorer_link.dart';
import 'package:resonance_network_wallet/v2/theme/app_colors.dart';
import 'package:resonance_network_wallet/v2/theme/app_text_styles.dart';

Expand Down Expand Up @@ -101,7 +101,6 @@ class _TransactionDetailSheet extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = ref.watch(l10nProvider);
final colors = context.colors;
final text = context.themeText;

return BottomSheetContainer(
title: _title(l10n),
Expand Down Expand Up @@ -129,7 +128,7 @@ class _TransactionDetailSheet extends ConsumerWidget {
_DetailsSection(tx: tx, isSend: _isSend, activeAccountId: activeAccountId, colors: colors),
const SizedBox(height: 24),
Center(
child: _ExplorerLink(tx: tx, colors: colors, text: text),
child: _ExplorerLink(tx: tx, colors: colors),
),
const SizedBox(height: 8),
],
Expand Down Expand Up @@ -598,13 +597,11 @@ class _DetailRow extends StatelessWidget {
class _ExplorerLink extends ConsumerWidget {
final TransactionEvent tx;
final AppColorsV2 colors;
final AppTextTheme text;

const _ExplorerLink({required this.tx, required this.colors, required this.text});
const _ExplorerLink({required this.tx, required this.colors});

@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = ref.watch(l10nProvider);
final isPending =
tx is PendingTransactionEvent ||
tx is PendingMultisigCreationEvent ||
Expand All @@ -613,22 +610,10 @@ class _ExplorerLink extends ConsumerWidget {
tx is PendingMultisigCancellationEvent;
final color = isPending ? colors.accentOrange.withValues(alpha: 0.3) : colors.accentOrange;

return GestureDetector(
onTap: isPending ? null : () => _openExplorer(),
child: Container(
padding: const EdgeInsets.only(bottom: 2),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: color, width: 1)),
),
child: Text(
l10n.activityDetailViewExplorer,
style: text.smallParagraph?.copyWith(color: color, fontWeight: FontWeight.w400),
),
),
);
return ExplorerLink(url: _explorerUrl(), color: color, enabled: !isPending);
}

void _openExplorer() {
String? _explorerUrl() {
final isMinerReward = tx.isMinerReward;
final isMultisigCreated = tx.isMultisigCreated;
final isProposalCreated = tx.isProposalCreation;
Expand Down Expand Up @@ -666,6 +651,6 @@ class _ExplorerLink extends ConsumerWidget {
path = '$transactionType/${tx.blockHash}';
}

if (path != null) openUrl('${AppConstants.explorerEndpoint}/$path');
return path == null ? null : '${AppConstants.explorerEndpoint}/$path';
}
}
22 changes: 18 additions & 4 deletions mobile-app/lib/v2/screens/home/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import 'package:resonance_network_wallet/v2/screens/activity/transaction_detail_
import 'package:resonance_network_wallet/v2/screens/receive/receive_screen.dart';
import 'package:resonance_network_wallet/v2/screens/multisig/multisig_activity_section.dart';
import 'package:resonance_network_wallet/v2/screens/multisig/multisig_proposal_detail_sheet.dart';
import 'package:resonance_network_wallet/v2/screens/multisig/propose/propose_recipient_screen.dart';
import 'package:resonance_network_wallet/v2/screens/send/input_amount_screen.dart';
import 'package:resonance_network_wallet/v2/screens/send/multisig_propose_strategy.dart';
import 'package:resonance_network_wallet/v2/screens/send/regular_send_strategy.dart';
import 'package:resonance_network_wallet/v2/screens/send/select_recipient_screen.dart';
import 'package:resonance_network_wallet/v2/screens/settings/settings_screen.dart';
import 'package:resonance_network_wallet/v2/screens/pos/pos_amount_screen.dart';
Expand Down Expand Up @@ -96,7 +97,12 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
ref.read(paymentIntentProvider.notifier).state = null;

final pageRoute = MaterialPageRoute(
builder: (_) => InputAmountScreen(recipientAddress: payment.to, initialAmount: payment.amount, isPayMode: true),
builder: (_) => InputAmountScreen(
strategy: const RegularSendStrategy(),
recipientAddress: payment.to,
initialAmount: payment.amount,
isPayMode: true,
),
settings: inputAmountScreenRouteSettings,
);

Expand Down Expand Up @@ -322,7 +328,10 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
final sendCard = _actionCard(
iconAsset: 'assets/v2/action_send.svg',
label: l10n.homeSend,
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SelectRecipientScreen())),
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SelectRecipientScreen(strategy: RegularSendStrategy())),
),
);

final swapCard = _actionCard(
Expand Down Expand Up @@ -357,7 +366,12 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
_actionCard(
iconAsset: 'assets/v2/action_send.svg',
label: l10n.multisigProposeTitle,
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => ProposeRecipientScreen(msig: msig))),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SelectRecipientScreen(strategy: MultisigProposeStrategy(msig: msig)),
),
),
),
],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import 'package:resonance_network_wallet/providers/wallet_providers.dart';
import 'package:resonance_network_wallet/v2/components/multisig_expiry_value.dart';
import 'package:resonance_network_wallet/routes.dart';
import 'package:resonance_network_wallet/shared/extensions/current_route_extensions.dart';
import 'package:resonance_network_wallet/shared/utils/open_external_url.dart';
import 'package:resonance_network_wallet/v2/components/amount_display_with_conversion.dart';
import 'package:resonance_network_wallet/v2/components/bottom_sheet_container.dart';
import 'package:resonance_network_wallet/v2/components/detail_summary_row.dart';
import 'package:resonance_network_wallet/v2/components/explorer_link.dart';
import 'package:resonance_network_wallet/v2/components/loader.dart';
import 'package:resonance_network_wallet/v2/components/quantus_button.dart';
import 'package:resonance_network_wallet/v2/screens/multisig/multisig_approve_confirm_sheet.dart';
Expand Down Expand Up @@ -232,7 +232,10 @@ class _MultisigProposalDetailSheet extends ConsumerWidget {
isActionable: isActionable,
),
Center(
child: _ExplorerLink(proposal: liveProposal, colors: colors, text: text),
child: ExplorerLink(
url: '${AppConstants.explorerEndpoint}/multisig-proposals/${liveProposal.explorerProposalId}',
color: colors.accentOrange,
),
),
const SizedBox(height: 8),
],
Expand Down Expand Up @@ -627,30 +630,3 @@ class _AmountSection extends ConsumerWidget {
return AmountDisplayWithConversion(amountDisplay: amount);
}
}

class _ExplorerLink extends ConsumerWidget {
final MultisigProposal proposal;
final AppColorsV2 colors;
final AppTextTheme text;

const _ExplorerLink({required this.proposal, required this.colors, required this.text});

@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = ref.watch(l10nProvider);

return GestureDetector(
onTap: () => openUrl('${AppConstants.explorerEndpoint}/multisig-proposals/${proposal.explorerProposalId}'),
child: Container(
padding: const EdgeInsets.only(bottom: 2),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: colors.accentOrange, width: 1)),
),
child: Text(
l10n.activityDetailViewExplorer,
style: text.smallParagraph?.copyWith(color: colors.accentOrange, fontWeight: FontWeight.w400),
),
),
);
}
}
Loading
Loading