Replace native Twilio Voice SDK with WebView-based softphone
Rewrites the mobile app from a native Twilio Voice SDK integration (Android Telecom/ConnectionService) to a thin WebView shell that loads a standalone browser phone page from WordPress. This eliminates the buggy Android phone account registration, fixes frequent logouts by using 7-day WP session cookies instead of JWT tokens, and maintains all existing call features (dialpad, queues, hold, transfer, requeue, recording, caller ID, agent status). Server-side: - Add class-twp-mobile-phone-page.php: standalone /twp-phone/ endpoint with mobile-optimized UI, dark mode, tab navigation, and Flutter WebView JS bridge - Extend auth cookie to 7 days for phone agents - Add WP AJAX handler for FCM token registration (cookie auth) Flutter app (v2.0.0): - Replace 18 native files with 5-file WebView shell - Login via wp-login.php in WebView (auto-detect redirect on success) - Full-screen WebView with auto microphone grant for WebRTC - FCM push notifications preserved for queue alerts - Remove: twilio_voice, dio, provider, JWT auth, SSE, native call UI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,12 +3,11 @@ import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'api_client.dart';
|
||||
|
||||
/// Notification ID for queue alerts (fixed so we can cancel it).
|
||||
const int _queueAlertNotificationId = 9001;
|
||||
|
||||
/// Background handler — must be top-level function.
|
||||
/// Background handler -- must be top-level function.
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
await Firebase.initializeApp();
|
||||
@@ -21,7 +20,6 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
final plugin = FlutterLocalNotificationsPlugin();
|
||||
await plugin.cancel(_queueAlertNotificationId);
|
||||
}
|
||||
// VoIP pushes handled natively by twilio_voice plugin.
|
||||
}
|
||||
|
||||
/// Show an insistent queue alert notification (works from background handler too).
|
||||
@@ -57,8 +55,12 @@ Future<void> _showQueueAlertNotification(Map<String, dynamic> data) async {
|
||||
);
|
||||
}
|
||||
|
||||
/// Push notification service for queue alerts and general notifications.
|
||||
///
|
||||
/// FCM token registration is handled via the WebView JavaScript bridge
|
||||
/// instead of a REST API call. The token is exposed via [fcmToken] and
|
||||
/// injected into the web page by [PhoneScreen].
|
||||
class PushNotificationService {
|
||||
final ApiClient _api;
|
||||
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
|
||||
final FlutterLocalNotificationsPlugin _localNotifications =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
@@ -66,8 +68,6 @@ class PushNotificationService {
|
||||
|
||||
String? get fcmToken => _fcmToken;
|
||||
|
||||
PushNotificationService(this._api);
|
||||
|
||||
Future<void> initialize() async {
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
|
||||
@@ -84,43 +84,37 @@ class PushNotificationService {
|
||||
const initSettings = InitializationSettings(android: androidSettings);
|
||||
await _localNotifications.initialize(initSettings);
|
||||
|
||||
// Get and register FCM token
|
||||
// Get FCM token
|
||||
final token = await _messaging.getToken();
|
||||
debugPrint('FCM token: ${token != null ? "${token.substring(0, 20)}..." : "NULL"}');
|
||||
debugPrint(
|
||||
'FCM token: ${token != null ? "${token.substring(0, 20)}..." : "NULL"}');
|
||||
if (token != null) {
|
||||
_fcmToken = token;
|
||||
await _registerToken(token);
|
||||
} else {
|
||||
debugPrint('FCM: Failed to get token - Firebase may not be configured correctly');
|
||||
debugPrint(
|
||||
'FCM: Failed to get token - Firebase may not be configured correctly');
|
||||
}
|
||||
|
||||
// Listen for token refresh
|
||||
_messaging.onTokenRefresh.listen(_registerToken);
|
||||
_messaging.onTokenRefresh.listen((token) {
|
||||
_fcmToken = token;
|
||||
});
|
||||
|
||||
// Handle foreground messages (non-VoIP)
|
||||
// Handle foreground messages
|
||||
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
|
||||
}
|
||||
|
||||
Future<void> _registerToken(String token) async {
|
||||
try {
|
||||
await _api.dio.post('/fcm/register', data: {'fcm_token': token});
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
void _handleForegroundMessage(RemoteMessage message) {
|
||||
final data = message.data;
|
||||
final type = data['type'];
|
||||
|
||||
// VoIP incoming_call is handled by twilio_voice natively
|
||||
if (type == 'incoming_call') return;
|
||||
|
||||
// Queue alert — show insistent notification
|
||||
// Queue alert -- show insistent notification
|
||||
if (type == 'queue_alert') {
|
||||
_showQueueAlertNotification(data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue alert cancel — dismiss notification
|
||||
// Queue alert cancel -- dismiss notification
|
||||
if (type == 'queue_alert_cancel') {
|
||||
_localNotifications.cancel(_queueAlertNotificationId);
|
||||
return;
|
||||
@@ -142,7 +136,7 @@ class PushNotificationService {
|
||||
);
|
||||
}
|
||||
|
||||
/// Cancel any active queue alert (called when agent accepts a call in-app).
|
||||
/// Cancel any active queue alert.
|
||||
void cancelQueueAlert() {
|
||||
_localNotifications.cancel(_queueAlertNotificationId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user