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
6 changes: 4 additions & 2 deletions lib/core/app_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import 'package:mostro/features/chat/screens/chat_room_screen.dart';
import 'package:mostro/features/chat/screens/chat_rooms_screen.dart';
import 'package:mostro/features/disputes/screens/dispute_chat_screen.dart';
import 'package:mostro/features/rate/screens/rate_counterpart_screen.dart';
import 'package:mostro/features/settings/screens/connect_wallet_screen.dart';
import 'package:mostro/features/settings/screens/wallet_settings_screen.dart';
import 'package:mostro/features/trades/screens/trade_detail_screen.dart';
import 'package:mostro/features/trades/screens/trades_screen.dart';
import 'package:mostro/features/walkthrough/providers/first_run_provider.dart';
Expand Down Expand Up @@ -178,11 +180,11 @@ final GoRouter appRouter = GoRouter(
),
GoRoute(
path: AppRoute.walletSettings,
builder: (_, __) => const _Stub('Wallet Settings'),
builder: (_, __) => const WalletSettingsScreen(),
),
GoRoute(
path: AppRoute.connectWallet,
builder: (_, __) => const _Stub('Connect Wallet'),
builder: (_, __) => const ConnectWalletScreen(),
),
GoRoute(
path: AppRoute.rateUser,
Expand Down
31 changes: 31 additions & 0 deletions lib/features/order/screens/add_lightning_invoice_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'package:go_router/go_router.dart';

import 'package:mostro/core/app_routes.dart';
import 'package:mostro/core/app_theme.dart';
import 'package:mostro/features/settings/providers/nwc_provider.dart';
import 'package:mostro/shared/widgets/nwc_invoice_widget.dart';

/// Add Lightning Invoice screen — Route `/add_invoice/:orderId`.
///
Expand All @@ -13,9 +15,12 @@ class AddLightningInvoiceScreen extends ConsumerStatefulWidget {
const AddLightningInvoiceScreen({
super.key,
required this.orderId,
this.amountSats,
});

final String orderId;
/// Sats amount for the invoice. `null` until the trade provider resolves it.
final int? amountSats;

@override
ConsumerState<AddLightningInvoiceScreen> createState() =>
Expand All @@ -26,6 +31,8 @@ class _AddLightningInvoiceScreenState
extends ConsumerState<AddLightningInvoiceScreen> {
final _invoiceController = TextEditingController();
bool _submitting = false;
/// `true` when NWC is connected but generation failed → show manual form.
bool _manualMode = false;

@override
void dispose() {
Expand Down Expand Up @@ -63,6 +70,30 @@ class _AddLightningInvoiceScreenState
final cardBg = colors?.backgroundCard ?? const Color(0xFF1E2230);
final inputBg = colors?.backgroundInput ?? const Color(0xFF252A3A);

final isWalletConnected = ref.watch(isWalletConnectedProvider);

// If NWC wallet is connected, amount is known, and we haven't fallen back
// to manual, show the auto-invoice widget instead of the manual form.
final sats = widget.amountSats;
if (isWalletConnected && !_manualMode && sats != null && sats > 0) {
return Scaffold(
appBar: AppBar(title: const Text('Add Invoice')),
body: Padding(
padding: const EdgeInsets.all(AppSpacing.lg),
child: Center(
child: NwcInvoiceWidget(
amountSats: sats,
onInvoiceConfirmed: (invoice) {
_invoiceController.text = invoice;
_submit();
},
onFallbackToManual: () => setState(() => _manualMode = true),
),
),
),
);
}

return Scaffold(
appBar: AppBar(title: const Text('Add Invoice')),
body: Padding(
Expand Down
25 changes: 25 additions & 0 deletions lib/features/order/screens/pay_lightning_invoice_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import 'package:share_plus/share_plus.dart';

import 'package:mostro/core/app_routes.dart';
import 'package:mostro/core/app_theme.dart';
import 'package:mostro/features/settings/providers/nwc_provider.dart';
import 'package:mostro/shared/widgets/nwc_payment_widget.dart';

/// Pay Lightning Invoice screen — Route `/pay_invoice/:orderId`.
///
Expand All @@ -33,6 +35,8 @@ class _PayLightningInvoiceScreenState
'jyzpnpryz4jns7qs9qyyssqjrvz0waerp2g3kx6k2neqfmfp2sxlm0n3m';

bool _waiting = false;
/// `true` when NWC is connected but payment failed → show QR fallback.
bool _manualMode = false;

void _simulatePaymentDetected() {
setState(() => _waiting = true);
Expand All @@ -49,6 +53,27 @@ class _PayLightningInvoiceScreenState
final green = colors?.mostroGreen ?? const Color(0xFF8CC63F);
final cardBg = colors?.backgroundCard ?? const Color(0xFF1E2230);

final isWalletConnected = ref.watch(isWalletConnectedProvider);

// If NWC wallet is connected and payment hasn't failed yet, show auto-pay.
if (isWalletConnected && !_manualMode) {
return Scaffold(
appBar: AppBar(title: const Text('Pay Lightning Invoice')),
body: Padding(
padding: const EdgeInsets.all(AppSpacing.lg),
child: Center(
child: NwcPaymentWidget(
bolt11: _mockInvoice,
// TODO(bridge): pass real sats amount from trade provider.
amountSats: 0,
onPaymentSuccess: _simulatePaymentDetected,
onFallbackToManual: () => setState(() => _manualMode = true),
),
),
),
);
}

return Scaffold(
appBar: AppBar(title: const Text('Pay Lightning Invoice')),
body: Padding(
Expand Down
68 changes: 68 additions & 0 deletions lib/features/settings/providers/nwc_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';

// Sentinel for copyWith nullable fields.
const _unset = Object();

/// Wallet connection state held in memory.
///
/// `null` → no wallet connected.
/// non-null → wallet connected; contains pubkey + relay URLs + optional balance.
class NwcWalletState {
NwcWalletState({
required this.walletPubkey,
required List<String> relayUrls,
this.walletName,
this.balanceSats,
}) : relayUrls = List.unmodifiable(relayUrls);

final String walletPubkey;
/// Immutable list of NWC relay URLs.
final List<String> relayUrls;
final String? walletName;
final int? balanceSats;

NwcWalletState copyWith({
String? walletPubkey,
List<String>? relayUrls,
String? walletName,
Object? balanceSats = _unset,
}) =>
NwcWalletState(
walletPubkey: walletPubkey ?? this.walletPubkey,
relayUrls: relayUrls ?? this.relayUrls,
walletName: walletName ?? this.walletName,
balanceSats: identical(balanceSats, _unset)
? this.balanceSats
: balanceSats as int?,
);
}

// ── Notifier ───────────────────────────────────────────────────────────────────

class NwcNotifier extends StateNotifier<NwcWalletState?> {
NwcNotifier() : super(null);

/// Store wallet state after a successful `connect_wallet` call.
void setConnected(NwcWalletState wallet) => state = wallet;

/// Clear wallet state after `disconnect_wallet`.
void setDisconnected() => state = null;

/// Update balance from a `get_balance` result.
void updateBalance(int? sats) {
final current = state;
if (current == null) return;
state = current.copyWith(balanceSats: sats);
}
}

// ── Providers ─────────────────────────────────────────────────────────────────

/// Wallet connection state. `null` when no wallet is connected.
final nwcProvider =
StateNotifierProvider<NwcNotifier, NwcWalletState?>((ref) => NwcNotifier());

/// Convenience: `true` when a wallet is connected.
final isWalletConnectedProvider = Provider<bool>(
(ref) => ref.watch(nwcProvider) != null,
);
Loading