Validate stale chat requests, show info instead of auto-dismiss

- Add validateIncomingRequest() — checks session status with backend
- Home screen validates before showing sheet (on resume + listener)
- IncomingRequestSheet shows "cancelled/accepted by other" message
  instead of silently dismissing when request becomes stale

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 16:40:52 +08:00
parent 212e1e8ac6
commit e3da863f3c
3 changed files with 73 additions and 2 deletions

View File

@@ -130,6 +130,22 @@ class ChatRequest extends _$ChatRequest {
}
}
/// Check if the current incoming request is still valid (pending_acceptance).
/// If stale, reset to listening state.
Future<void> validateIncomingRequest() async {
if (state is! ChatRequestIncomingData) return;
final sessionId = (state as ChatRequestIncomingData).sessionId;
try {
final response = await _apiClient.get('/api/shared/chat/$sessionId/info');
final status = response['data']?['status'] as String?;
if (status != 'pending_acceptance') {
state = const ChatRequestListeningData();
}
} catch (_) {
state = const ChatRequestListeningData();
}
}
Future<void> accept(String sessionId) async {
state = const ChatRequestAcceptingData();
try {

View File

@@ -8,6 +8,18 @@ class IncomingRequestSheet extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final requestState = ref.watch(chatRequestProvider);
// Request is still active — show accept/decline
if (requestState is ChatRequestIncomingData) {
return _buildActiveRequest(context, ref);
}
// Request was taken by another mitra or cancelled — show info
return _buildStaleRequest(context);
}
Widget _buildActiveRequest(BuildContext context, WidgetRef ref) {
return Container(
padding: const EdgeInsets.all(24),
child: Column(
@@ -52,4 +64,35 @@ class IncomingRequestSheet extends ConsumerWidget {
),
);
}
Widget _buildStaleRequest(BuildContext context) {
return Container(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.info_outline, size: 48, color: Colors.orange),
const SizedBox(height: 16),
const Text(
'Permintaan tidak tersedia',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
'Permintaan ini sudah dibatalkan oleh customer atau diterima oleh Bestie lain.',
style: TextStyle(fontSize: 14, color: Colors.grey),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
),
],
),
);
}
}

View File

@@ -32,7 +32,13 @@ class _HomeScreenState extends ConsumerState<HomeScreen> with WidgetsBindingObse
if (state == AppLifecycleState.resumed) {
final chatState = ref.read(chatRequestProvider);
if (chatState is ChatRequestIncomingData) {
_showIncomingRequest(chatState.sessionId);
// Validate the request is still pending before showing
ref.read(chatRequestProvider.notifier).validateIncomingRequest().then((_) {
final current = ref.read(chatRequestProvider);
if (current is ChatRequestIncomingData) {
_showIncomingRequest(current.sessionId);
}
});
}
}
}
@@ -65,7 +71,13 @@ class _HomeScreenState extends ConsumerState<HomeScreen> with WidgetsBindingObse
// Listen for incoming chat requests
ref.listen(chatRequestProvider, (prev, next) {
if (next is ChatRequestIncomingData) {
_showIncomingRequest(next.sessionId);
// Validate request is still pending before showing sheet
ref.read(chatRequestProvider.notifier).validateIncomingRequest().then((_) {
final current = ref.read(chatRequestProvider);
if (current is ChatRequestIncomingData) {
_showIncomingRequest(current.sessionId);
}
});
} else if (next is ChatRequestAcceptedData) {
final session = next.session;
final sessionId = session['session_id'] as String? ?? session['id'] as String;