import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/chat/active_session_notifier.dart'; import '../../../core/pairing/pairing_notifier.dart'; import '../../../core/theme/halo_tokens.dart'; import '../../../core/theme/widgets/widgets.dart'; /// Phase 4 Stage 5 — S9 Match-found screen. /// /// Reskinned from the v4 mock (`v4.jsx::S9MatchV4`). Shows the matched /// bestie's orb + a small online status dot, the matched-line copy, and a /// primary CTA `mulai sesi {N} menit →`. The duration is read from the active /// session payload (which the pairing notifier kicks via /// `activeSessionProvider.refresh()` on the WS `paired` event). /// /// `PairingActiveData` is the auto-advance signal — fired by the notifier /// ~2s after WS `paired` lands. The same advance is also reachable manually /// via the CTA in case the user is faster than the auto-advance timer. class BestieFoundScreen extends ConsumerStatefulWidget { final String sessionId; final String mitraName; const BestieFoundScreen({ super.key, required this.sessionId, required this.mitraName, }); @override ConsumerState createState() => _BestieFoundScreenState(); } class _BestieFoundScreenState extends ConsumerState { @override void initState() { super.initState(); ref.listenManual(pairingProvider, (prev, next) { if (!mounted) return; if (next is PairingActiveData) { context.go('/chat/session/${next.sessionId}', extra: next.mitraName); } }); } void _enterChat() { context.go('/chat/session/${widget.sessionId}', extra: widget.mitraName); } @override Widget build(BuildContext context) { final activeSession = ref.watch(activeSessionProvider).valueOrNull; final durationMinutes = activeSession?.session?['duration_minutes'] as int?; final ctaLabel = durationMinutes != null ? 'mulai sesi $durationMinutes menit →' : 'mulai sesi →'; final subtitle = durationMinutes != null ? 'siap nemenin kamu $durationMinutes menit ke depan. cerita aja pelan-pelan ya 🤍' : 'siap nemenin kamu. cerita aja pelan-pelan ya 🤍'; return Scaffold( backgroundColor: HaloTokens.bg, body: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB( HaloSpacing.s24, HaloSpacing.s24, HaloSpacing.s24, HaloSpacing.s24, ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded( child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Stack( children: [ HaloOrb( size: 140, seed: widget.mitraName.hashCode, label: widget.mitraName, ), Positioned( right: 4, bottom: 4, child: Container( width: 28, height: 28, decoration: BoxDecoration( shape: BoxShape.circle, color: HaloTokens.success, border: Border.all( color: HaloTokens.bg, width: 3, ), ), ), ), ], ), const SizedBox(height: HaloSpacing.s20), const Text( '◦ MATCHED ◦', style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 12, fontWeight: FontWeight.w700, letterSpacing: 1.6, color: HaloTokens.brand, ), ), const SizedBox(height: HaloSpacing.s8), Text( 'halo, aku bestie ${widget.mitraName}', textAlign: TextAlign.center, style: const TextStyle( fontFamily: HaloTokens.fontDisplay, fontSize: 26, height: 32 / 26, fontWeight: FontWeight.w700, color: HaloTokens.brandDark, ), ), const SizedBox(height: HaloSpacing.s8), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 280), child: Text( subtitle, textAlign: TextAlign.center, style: const TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 14, height: 22 / 14, color: HaloTokens.inkSoft, ), ), ), ], ), ), ), HaloButton( label: ctaLabel, fullWidth: true, size: HaloButtonSize.lg, onPressed: _enterChat, ), ], ), ), ), ); } }