structure improvements.

ui_ux_rollout_merge_audio_video_call
Sikander Saleem 2 weeks ago
parent 9be42623ef
commit 11c6e2411e

@ -26,28 +26,19 @@ import 'package:flutter_callkit_incoming/entities/entities.dart';
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
try {
log('📱 [BACKGROUND] Data payload: ${message.data}', name: 'FirebaseNotificationManger');
final notificationType = message.data['notificationType'] as String? ??
message.data['type'] as String?;
final notificationType = message.data['notificationType'] as String? ?? message.data['type'] as String?;
final transactionType = message.data['transactionType'] as String?;
log('📱 [BACKGROUND] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
// Handle incoming call notifications
if (notificationType == 'incoming_call' || transactionType == 'call') {
// Extract call data
final callId = message.data['callId'] as String? ??
message.data['sessionId'] as String? ??
DateTime.now().millisecondsSinceEpoch.toString();
final callId = message.data['callId'] as String? ?? message.data['sessionId'] as String? ?? DateTime.now().millisecondsSinceEpoch.toString();
final callerId = message.data['callerId'] as String? ??
message.data['callerEmployeeNumber'] as String? ??
message.data['sourceUserId'] as String? ??
'unknown';
final callerId = message.data['callerId'] as String? ?? message.data['callerEmployeeNumber'] as String? ?? message.data['sourceUserId'] as String? ?? 'unknown';
final callerName = message.data['callerName'] as String? ??
message.data['callerUserName'] as String? ??
message.data['userName'] as String? ??
'Unknown Caller';
final callerName = message.data['callerName'] as String? ?? message.data['callerUserName'] as String? ?? message.data['userName'] as String? ?? 'Unknown Caller';
final callerEmployeeNumber = message.data['callerEmployeeNumber'] as String? ?? callerId;
@ -59,14 +50,13 @@ Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
return false;
}();
final moduleId = message.data['moduleId'] as String? ??
message.data['applicationId']?.toString();
final moduleId = message.data['moduleId'] as String? ?? message.data['applicationId']?.toString();
final referenceId = message.data['referenceId'] as String?;
final conversationId = message.data['conversationId'] as String?;
// CRITICAL FIX: DO NOT initialize any communication services in background isolate
// Only persist call data and show CallKit
///TODO need to find other solution to this after testing
///TODO need to find other solution to this after testing
await PendingCallStorage.savePendingCall(
callId: callId,
callerId: callerId,
@ -141,8 +131,7 @@ Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
} catch (e, stackTrace) {
log('❌ [BACKGROUND] Error handling background message: $e',
name: 'FirebaseNotificationManger', error: e, stackTrace: stackTrace);
log('❌ [BACKGROUND] Error handling background message: $e', name: 'FirebaseNotificationManger', error: e, stackTrace: stackTrace);
}
}
@ -188,8 +177,7 @@ class FirebaseNotificationManger {
//print("pushToken:$token");
}
static void _onMessageReceived(h_push.RemoteMessage remoteMessage) {
}
static void _onMessageReceived(h_push.RemoteMessage remoteMessage) {}
static void _onMessageReceiveError(Object error) {
log('❌ [HUAWEI] Message receive error: ${error.toString()}', name: 'FirebaseNotificationManger');
@ -214,27 +202,18 @@ class FirebaseNotificationManger {
log('📱 [HANDLE_MESSAGE] Full message data: $messageData', name: 'FirebaseNotificationManger');
// FIXED: Check if this is a call notification first - CHECK BOTH 'notificationType' AND 'type'
final notificationType = messageData['notificationType'] as String? ??
messageData['type'] as String?; // Backend uses 'type'
final notificationType = messageData['notificationType'] as String? ?? messageData['type'] as String?; // Backend uses 'type'
final transactionType = messageData['transactionType'] as String?;
log('📱 [HANDLE_MESSAGE] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
if (notificationType == 'incoming_call' || transactionType == 'call') {
// CRITICAL FIX: Show CallKit UI in foreground just like background
final callId = messageData['callId'] as String? ??
messageData['sessionId'] as String? ??
DateTime.now().millisecondsSinceEpoch.toString();
final callId = messageData['callId'] as String? ?? messageData['sessionId'] as String? ?? DateTime.now().millisecondsSinceEpoch.toString();
final callerId = messageData['callerId'] as String? ??
messageData['callerEmployeeNumber'] as String? ??
messageData['sourceUserId'] as String? ??
'unknown';
final callerId = messageData['callerId'] as String? ?? messageData['callerEmployeeNumber'] as String? ?? messageData['sourceUserId'] as String? ?? 'unknown';
final callerName = messageData['callerName'] as String? ??
messageData['callerUserName'] as String? ??
messageData['userName'] as String? ??
'Unknown Caller';
final callerName = messageData['callerName'] as String? ?? messageData['callerUserName'] as String? ?? messageData['userName'] as String? ?? 'Unknown Caller';
final callerEmployeeNumber = messageData['callerEmployeeNumber'] as String? ?? callerId;
@ -246,8 +225,7 @@ class FirebaseNotificationManger {
return false;
}();
final moduleId = messageData['moduleId'] as String? ??
messageData['applicationId']?.toString();
final moduleId = messageData['moduleId'] as String? ?? messageData['applicationId']?.toString();
final referenceId = messageData['referenceId'] as String?;
final conversationId = messageData['conversationId'] as String?;
@ -432,7 +410,6 @@ class FirebaseNotificationManger {
}
static initialized(BuildContext context) async {
//TOD0 add platform check here also
if (!(await isGoogleServicesAvailable()) && Platform.isAndroid) {
log('📱 [INIT] Using Huawei Push Services', name: 'FirebaseNotificationManger');
@ -515,8 +492,7 @@ class FirebaseNotificationManger {
FirebaseMessaging.instance.getInitialMessage().then((initialMessage) {
if (initialMessage != null) {
// Check if it's a call notification
final notificationType = initialMessage.data['notificationType'] as String? ??
initialMessage.data['type'] as String?;
final notificationType = initialMessage.data['notificationType'] as String? ?? initialMessage.data['type'] as String?;
final transactionType = initialMessage.data['transactionType'] as String?;
log('📱 [FCM_INITIAL] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
log('📱 [FCM_INITIAL] Transaction Type: $transactionType', name: 'FirebaseNotificationManger');
@ -545,8 +521,7 @@ class FirebaseNotificationManger {
log('📱 [FCM_FOREGROUND] Body: ${message.notification?.body}', name: 'FirebaseNotificationManger');
// FIXED: Check if it's a call notification - CHECK BOTH 'notificationType' AND 'type'
final notificationType = message.data['notificationType'] as String? ??
message.data['type'] as String?; // Backend uses 'type'
final notificationType = message.data['notificationType'] as String? ?? message.data['type'] as String?; // Backend uses 'type'
log('📱 [FCM_FOREGROUND] Notification Type: $notificationType', name: 'FirebaseNotificationManger');
if (notificationType == 'incoming_call') {
@ -563,19 +538,11 @@ class FirebaseNotificationManger {
log(' - applicationId: ${message.data['applicationId']}', name: 'FirebaseNotificationManger');
// CRITICAL FIX: Show CallKit UI in foreground
final callId = message.data['callId'] as String? ??
message.data['sessionId'] as String? ??
DateTime.now().millisecondsSinceEpoch.toString();
final callId = message.data['callId'] as String? ?? message.data['sessionId'] as String? ?? DateTime.now().millisecondsSinceEpoch.toString();
final callerId = message.data['callerId'] as String? ??
message.data['callerEmployeeNumber'] as String? ??
message.data['sourceUserId'] as String? ??
'unknown';
final callerId = message.data['callerId'] as String? ?? message.data['callerEmployeeNumber'] as String? ?? message.data['sourceUserId'] as String? ?? 'unknown';
final callerName = message.data['callerName'] as String? ??
message.data['callerUserName'] as String? ??
message.data['userName'] as String? ??
'Unknown Caller';
final callerName = message.data['callerName'] as String? ?? message.data['callerUserName'] as String? ?? message.data['userName'] as String? ?? 'Unknown Caller';
final callerEmployeeNumber = message.data['callerEmployeeNumber'] as String? ?? callerId;
@ -587,8 +554,7 @@ class FirebaseNotificationManger {
return false;
}();
final moduleId = message.data['moduleId'] as String? ??
message.data['applicationId']?.toString();
final moduleId = message.data['moduleId'] as String? ?? message.data['applicationId']?.toString();
final referenceId = message.data['referenceId'] as String?;
final conversationId = message.data['conversationId'] as String?;
@ -666,16 +632,12 @@ class FirebaseNotificationManger {
if (Platform.isAndroid) {
if (message.data["notificationType"] != 'NurseConfirmArrive') {
NotificationManger.showNotification(
title: message.notification?.title ?? "",
subtext: message.notification?.body ?? "",
hashcode: int.tryParse("1234") ?? 1,
payload: json.encode(message.data),
context: context);
title: message.notification?.title ?? "", subtext: message.notification?.body ?? "", hashcode: int.tryParse("1234") ?? 1, payload: json.encode(message.data), context: context);
}
}
return;
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
log('📱 [FCM_TAP] Notification tapped', name: 'FirebaseNotificationManger');
@ -697,7 +659,7 @@ class FirebaseNotificationManger {
}
log('═══════════════════════════════════════════', name: 'FirebaseNotificationManger');
});
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
log('✅ [INIT] FirebaseNotificationManger initialized successfully', name: 'FirebaseNotificationManger');

@ -37,6 +37,7 @@ import 'package:test_sa/modules/asset_inventory_module/provider/asset_inventory_
import 'package:test_sa/modules/cm_module/cm_detail_provider.dart';
import 'package:test_sa/modules/cm_module/create_cm_request.dart';
import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
import 'package:test_sa/modules/cx_module/chat/services/call_manager.dart';
import 'package:test_sa/modules/cx_module/chat/services/signalr_service.dart';
import 'package:test_sa/modules/demo_module/create_demo_request_page.dart';
import 'package:test_sa/modules/demo_module/provider/demo_period_lookup_provider.dart';
@ -228,7 +229,7 @@ class MyApp extends StatelessWidget {
return MultiProvider(
providers: [
// ============================================================
// CORE PROVIDERS (10) - Always instantiated at app launch
// CORE PROVIDERS (11) - Always instantiated at app launch
// These are critical for app functionality
// ============================================================
ChangeNotifierProvider(create: (_) => UserProvider()),
@ -241,6 +242,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => DepartmentsProvider()),
ChangeNotifierProvider(create: (_) => NullableLoadingProvider()),
ChangeNotifierProvider(create: (_) => ChatProvider()),
// HYBRID: CallManager uses Provider for UI, static instance for background calls
ChangeNotifierProvider(create: (_) => CallManager()),
// ============================================================
// LAZY LOADED PROVIDERS (107) - Created only when accessed

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
@ -22,7 +23,8 @@ class _AudioCallPageState extends State<AudioCallPage> {
@override
void initState() {
super.initState();
_callManager = CallManager();
// HYBRID: Get CallManager from Provider (UI layer)
_callManager = Provider.of<CallManager>(context, listen: false);
// Listen for call end and navigate back
_callManager.addListener(_checkCallStatus);

@ -3,7 +3,7 @@ import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:test_sa/core/di/service_locator.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
@ -27,8 +27,8 @@ class _VideoCallPageState extends State<VideoCallPage> {
@override
void initState() {
super.initState();
_callManager = getIt<CallManager>();
// _callManager = CallManager();
// HYBRID: Get CallManager from Provider (UI layer)
_callManager = Provider.of<CallManager>(context, listen: false);
// Listen for call end
_callManager.addListener(_checkCallStatus);

@ -95,16 +95,25 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// These getters provide read-only access to call state for UI
// All call operations should go through CallManager directly
CallManager get _callManager => getIt<CallManager>();
CallStatus get callStatus => _callManager.callStatus;
CallSession? get currentCall => _callManager.currentCall;
Duration get callDuration => _callManager.callDuration;
bool get isMuted => _callManager.isMuted;
bool get isSpeakerOn => _callManager.isSpeakerOn;
bool get isCameraOn => _callManager.isCameraOn;
bool get isPeerMuted => _callManager.isPeerMuted;
bool get isPeerCameraOn => _callManager.isPeerCameraOn;
bool get isCallInProgress => _callManager.isCallInProgress;
WebRTCService? get webrtcService => _callManager.webrtcService;
// For backwards compatibility with UI components
@ -120,6 +129,7 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
log(' [CALL STATUS] Call status is now managed by CallManager', name: 'ChatProvider');
notifyListeners();
}
/// OPTIMIZATION: Improved connection disposal to prevent memory leaks
/// This properly handles errors and ensures connection is always cleaned up
Future<void> _disposeConnection() async {
@ -127,10 +137,10 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// SignalR is now a SINGLETON managed by SignalRService
// It's shared between ChatProvider and CallManager
// Closing it here would break ongoing calls and call setup
// Just clear the local reference
_chatHubConnection = null;
if (kDebugMode) {
print('🔌 [ChatProvider] Cleared local SignalR reference (connection remains active)');
}
@ -141,7 +151,7 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// CRITICAL FIX: DO NOT dispose the SignalR connection
// Just clear ChatProvider's local state
// The SignalRService singleton will manage the connection lifecycle
_chatHubConnection = null;
@ -154,7 +164,7 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
sender = null;
recipient = null;
ChatApiClient().chatLoginResponse = null;
log('✅ [ChatProvider] Chat state reset (SignalR connection preserved)', name: 'ChatProvider');
}
@ -165,13 +175,13 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// CRITICAL FIX: DO NOT close SignalR connection on dispose
// The connection is a singleton and may be used by other parts of the app
// Only clear local state
_chatHubConnection = null;
if (kDebugMode) {
print('✅ ChatProvider disposed (SignalR connection preserved)');
}
super.dispose();
}
@ -204,7 +214,7 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
log('✅ Got chatParticipantModel: ${chatParticipantModel?.toJson()}');
try {
sender = chatParticipantModel?.participants?.firstWhere(
(participant) => participant.userId?.toLowerCase() == myId.toLowerCase()
(participant) => participant.userId?.toLowerCase() == myId.toLowerCase()
);
} catch (e) {
log('⚠️ Sender NOT FOUND for myId: $myId. Error: $e');
@ -213,7 +223,7 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
try {
recipient = chatParticipantModel?.participants?.firstWhere(
(participant) => participant.userId?.toLowerCase() == assigneeEmployeeNumber.toLowerCase()
(participant) => participant.userId?.toLowerCase() == assigneeEmployeeNumber.toLowerCase()
);
log('✅ recipient found: userId=${recipient?.userId}, userName=${recipient?.userName}, employeeNumber=${recipient?.employeeNumber}');
} catch (e) {
@ -230,7 +240,6 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
myEmployeeNumber: myId,
);
}
} catch (ex) {
if (kDebugMode) {
print('⚠️ Error in getUserAutoLoginTokenSilent: $ex');
@ -389,7 +398,6 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// CRITICAL FIX: Reference the singleton connection, don't create a new one
chatHubConnection = signalRService.hubConnection;
_registerChatEventHandlers();
} catch (e, stackTrace) {
if (kDebugMode) {
print('⚠️ Error building SignalR connection: $e');
@ -447,8 +455,8 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
final context = navigatorKey.currentContext;
if (context != null) {
CallErrorHandler.showGenericError(
context,
'Unable to initialize calling system. Please try again.'
context,
'Unable to initialize calling system. Please try again.'
);
}
return;
@ -653,7 +661,8 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
// ==================== UTILITY METHODS ====================
/// Reset unread message count
Future<bool> resetCount({int? moduleId, int? referenceNo, String? userId}) async { // Fixed: userId is String? not int?
Future<bool> resetCount({int? moduleId, int? referenceNo, String? userId}) async {
// Fixed: userId is String? not int?
try {
if (chatLoginResponse != null && sender != null && recipient != null) {
await ChatApiClient().resetCountApi(
@ -678,7 +687,8 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
}
/// Upload attachments
Future<List<ChatAttachment>?> uploadAttachments(String username, File file, String conversationId) async { // Fixed: match actual usage
Future<List<ChatAttachment>?> uploadAttachments(String username, File file, String conversationId) async {
// Fixed: match actual usage
try {
if (chatLoginResponse == null || chatParticipantModel == null) {
if (kDebugMode) {
@ -711,7 +721,8 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin {
}
/// Get unread messages
Future<List<UnReadMessage>> getUnReadMessages(String employeeId) async { // Fixed: accept employeeId and return List<UnReadMessage>
Future<List<UnReadMessage>> getUnReadMessages(String employeeId) async {
// Fixed: accept employeeId and return List<UnReadMessage>
try {
if (chatLoginResponse == null) {
if (kDebugMode) {

@ -19,40 +19,47 @@ import 'package:test_sa/modules/cx_module/chat/services/pending_call_storage.dar
import 'package:test_sa/modules/cx_module/chat/services/call_coordinator.dart';
class CallManager extends ChangeNotifier {
static CallManager? _staticInstance;
static final CallManager _instance = CallManager._internal();
factory CallManager() => _instance;
factory CallManager() {
_staticInstance = _instance;
return _instance;
}
CallManager._internal();
// Services - Retrieved from DI
static CallManager get instance {
if (_staticInstance == null) {
throw StateError('CallManager not initialized. Ensure Provider<CallManager> is registered in main.dart');
}
return _staticInstance!;
}
static bool get isInitialized => _staticInstance != null;
SignalRService get _signalRService => getIt<SignalRService>();
WebRTCService get _webrtcService => getIt<WebRTCService>();
final CallKitService _callKitService = CallKitService();
// Public getter for WebRTC service (used by call pages and ChatProvider)
WebRTCService get webrtcService => _webrtcService;
// Call state
CallSession? _currentCall;
CallStatus _callStatus = CallStatus.idle;
Duration _callDuration = Duration.zero;
Timer? _callDurationTimer;
Timer? _callTimeoutTimer;
// Call controls
bool _isMuted = false;
bool _isSpeakerOn = false;
bool _isCameraOn = true;
bool _isPeerMuted = false;
bool _isPeerCameraOn = true;
// Initialization state
bool _callKitInitialized = false;
bool _handlersRegistered = false;
// Module context (for SignalR invocations)
String? _userId;
String? _authToken;
String? _moduleId;
@ -60,26 +67,17 @@ class CallManager extends ChangeNotifier {
String? _conversationId;
String? _myEmployeeNumber;
// CRITICAL FIX: Static credential cache for background calls
// This allows incoming calls to work even when app is in background
static String? _cachedUserId;
static String? _cachedAuthToken;
static String? _cachedEmployeeNumber;
// CRITICAL: Pending call data for background acceptance
// This preserves the COMPLETE notification payload
Map<String, dynamic>? _pendingCallData;
// CRITICAL FIX: Navigation pending flag for background calls
// When app is in background and call is accepted, we set this flag
// The main UI will check this flag when it resumes and navigate
bool _navigationPending = false;
// CRITICAL FIX: WebRTC initialization synchronization
Completer<void>? _webrtcInitCompleter;
String? _pendingOfferSdp; // Queue for offer that arrives during initialization
String? _pendingOfferSdp;
// Getters
CallSession? get currentCall => _currentCall;
CallStatus get callStatus => _callStatus;
@ -98,10 +96,8 @@ class CallManager extends ChangeNotifier {
bool get isCallInProgress => _callStatus != CallStatus.idle;
// CRITICAL: Public getter for navigation pending flag
bool get isNavigationPending => _navigationPending;
/// Initialize CallManager with user credentials
Future<void> initialize({
required String userId,
required String authToken,
@ -111,14 +107,11 @@ class CallManager extends ChangeNotifier {
String? employeeNumber,
}) async {
try {
// CRITICAL FIX: Cache credentials for background call handling
_cachedUserId = userId;
_cachedAuthToken = authToken;
_cachedEmployeeNumber = employeeNumber;
// CRITICAL FIX: Don't re-initialize if already initialized with same USER credentials
if (_userId == userId && _authToken == authToken && _myEmployeeNumber?.toLowerCase() == employeeNumber?.toLowerCase() && _handlersRegistered) {
// User is the same, just update conversation context without reconnecting
final conversationChanged = _conversationId != conversationId;
final moduleChanged = _moduleId != moduleId;
final referenceChanged = _referenceId != referenceId;
@ -144,7 +137,6 @@ class CallManager extends ChangeNotifier {
return;
}
// Store module context
_userId = userId;
_authToken = authToken;
_moduleId = moduleId;
@ -152,23 +144,16 @@ class CallManager extends ChangeNotifier {
_conversationId = conversationId;
_myEmployeeNumber = employeeNumber;
// Initialize SignalR connection
final connected = await _signalRService.initialize(
userId: userId,
authToken: authToken,
conversationId: conversationId,
);
final connected = await _signalRService.initialize(userId: userId, authToken: authToken, conversationId: conversationId);
if (!connected) {
throw Exception('Failed to initialize SignalR connection');
}
// Initialize CallKit
if (!_callKitInitialized) {
await _initializeCallKit();
}
// Register call event handlers
if (!_handlersRegistered) {
_registerCallHandlers();
}
@ -178,7 +163,6 @@ class CallManager extends ChangeNotifier {
}
}
/// Initialize CallKit service
Future<void> _initializeCallKit() async {
if (_callKitInitialized) {
return;
@ -439,17 +423,8 @@ class CallManager extends ChangeNotifier {
/// Accept incoming call
Future<void> acceptCall() async {
try {
log('═══════════════════════════════════════════', name: 'CallManager');
log('✅ [ACCEPT] ▶️ ACCEPT CALL STARTED', name: 'CallManager');
log('✅ [ACCEPT] Timestamp: ${DateTime.now().toIso8601String()}', name: 'CallManager');
// CRITICAL: Handle background acceptance scenario
if (_currentCall == null && _pendingCallData != null) {
log('🔧 [ACCEPT] No current call but pending data exists - restoring from background', name: 'CallManager');
log(' Pending call ID: ${_pendingCallData!['callId']}', name: 'CallManager');
log(' Pending caller: ${_pendingCallData!['callerName']}', name: 'CallManager');
// Restore call session from pending data
_currentCall = CallSession(
callId: _pendingCallData!['callId'] as String,
type: (_pendingCallData!['isVideoCall'] as bool) ? CallType.video : CallType.audio,
@ -459,14 +434,10 @@ class CallManager extends ChangeNotifier {
peerAvatar: null,
startTime: DateTime.now(),
);
_updateCallStatus(CallStatus.incomingRinging);
log('✅ [ACCEPT] Call session restored from pending data', name: 'CallManager');
// Ensure CallManager is initialized with pending call context
if (_userId == null || _authToken == null) {
log('⚠️ [ACCEPT] CallManager not initialized, attempting auto-initialization...', name: 'CallManager');
if (_cachedUserId != null && _cachedAuthToken != null && _cachedEmployeeNumber != null) {
await initialize(
userId: _cachedUserId!,
@ -476,7 +447,6 @@ class CallManager extends ChangeNotifier {
referenceId: _pendingCallData!['referenceId'] as String?,
employeeNumber: _cachedEmployeeNumber,
);
log('✅ [ACCEPT] CallManager initialized for background call', name: 'CallManager');
} else {
log('❌ [ACCEPT] Cannot initialize - no cached credentials', name: 'CallManager');
await _cleanup();
@ -765,9 +735,7 @@ class CallManager extends ChangeNotifier {
_setupWebRTCCallbacks();
// Initialize with timeout protection
final initFuture = _currentCall!.type == CallType.audio
? _webrtcService.initializeForAudioCall()
: _webrtcService.initializeForVideoCall();
final initFuture = _currentCall!.type == CallType.audio ? _webrtcService.initializeForAudioCall() : _webrtcService.initializeForVideoCall();
// Add 10 second timeout for initialization
await initFuture.timeout(

Loading…
Cancel
Save