Phase 3.1: Local notification for WS chat requests, router fix, cleanup
- Show local notification (sound + vibrate) when chat_request arrives via WebSocket while mitra app is backgrounded - Add NotificationService.showLocalNotification() for programmatic use - Fix router redirect: don't redirect auth routes to splash during loading - Handle binary/string WebSocket frames in ChatRequestNotifier - Remove debug logging from backend and Flutter - Control center: mitra ping config UI - Both apps: dynamic ping, FCM deep-linking, unread badges Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
import '../api/api_client.dart';
|
||||
import '../api/api_client_provider.dart';
|
||||
import '../constants.dart';
|
||||
import '../notifications/notification_service.dart';
|
||||
|
||||
part 'chat_request_notifier.g.dart';
|
||||
|
||||
@@ -77,9 +78,12 @@ class ChatRequest extends _$ChatRequest {
|
||||
|
||||
_wsSubscription = _channel!.stream.listen(
|
||||
(raw) {
|
||||
final data = jsonDecode(raw as String) as Map<String, dynamic>;
|
||||
try {
|
||||
final text = raw is String ? raw : String.fromCharCodes(raw as List<int>);
|
||||
final data = jsonDecode(text) as Map<String, dynamic>;
|
||||
if (data['type'] == WsMessage.authOk) return;
|
||||
_onRequestReceived(data);
|
||||
} catch (_) {}
|
||||
},
|
||||
onError: (_) => _onConnectionError(),
|
||||
onDone: () => _onConnectionError(),
|
||||
@@ -105,7 +109,14 @@ class ChatRequest extends _$ChatRequest {
|
||||
final type = data['type'] as String?;
|
||||
|
||||
if (type == WsMessage.chatRequest) {
|
||||
state = ChatRequestIncomingData(data['session_id'] as String);
|
||||
final sessionId = data['session_id'] as String;
|
||||
state = ChatRequestIncomingData(sessionId);
|
||||
// Show local notification so mitra is alerted even when app is backgrounded
|
||||
NotificationService.showLocalNotification(
|
||||
title: 'Permintaan Chat Baru',
|
||||
body: 'Ada pelanggan yang ingin curhat! Ketuk untuk menerima.',
|
||||
data: {'type': 'chat_request', 'session_id': sessionId, 'action': 'open_accept'},
|
||||
);
|
||||
} else if (type == WsMessage.chatRequestClosed) {
|
||||
if (state is ChatRequestIncomingData) {
|
||||
state = const ChatRequestListeningData();
|
||||
|
||||
@@ -83,6 +83,36 @@ class NotificationService {
|
||||
_navigateFromMessage(message.data);
|
||||
}
|
||||
|
||||
/// Show a local notification programmatically (e.g. from WebSocket while backgrounded)
|
||||
static Future<void> showLocalNotification({
|
||||
required String title,
|
||||
required String body,
|
||||
Map<String, dynamic>? data,
|
||||
}) async {
|
||||
await _localNotifications.show(
|
||||
id: DateTime.now().millisecondsSinceEpoch % 100000,
|
||||
title: title,
|
||||
body: body,
|
||||
notificationDetails: NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
_channel.id,
|
||||
_channel.name,
|
||||
channelDescription: _channel.description,
|
||||
importance: Importance.high,
|
||||
priority: Priority.high,
|
||||
playSound: true,
|
||||
enableVibration: true,
|
||||
),
|
||||
iOS: const DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
),
|
||||
),
|
||||
payload: data != null ? jsonEncode(data) : null,
|
||||
);
|
||||
}
|
||||
|
||||
static void _navigateFromMessage(Map<String, dynamic> data) {
|
||||
if (_router == null) return;
|
||||
final sessionId = data['session_id'] as String?;
|
||||
|
||||
Reference in New Issue
Block a user