-
Notifications
You must be signed in to change notification settings - Fork 195
Description
import 'dart:async';
import 'dart:developer';
import 'package:auth_repo/auth_repo.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:local_data/local_data.dart';
import 'package:socket_io_client/socket_io_client.dart' as io_client;
/// {@template socket_source}
/// Package to handle Socket.IO calls
/// {@endtemplate}
class SocketSource {
/// {@macro socket_source}
SocketSource._(
this._token,
this._prefs,
this._appBuildConfig,
this._storeId,
);
final String _token;
final String _storeId;
// ignore: unused_field
final SharedPrefs _prefs;
final AppBuildConfig _appBuildConfig;
/// Public factory to initialize the SocketSource
static Future init(
String socketUrl, {
required SharedPrefs prefs,
required AppBuildConfig appBuildConfig,
required String storeId,
bool alwaysConnected = false,
String? token,
}) async {
final socketApi = SocketSource._(
token ?? '',
prefs,
appBuildConfig,
storeId,
);
await socketApi._init(
socketUrl: socketUrl,
alwaysConnected: alwaysConnected,
);
return socketApi;
}
late io_client.Socket _socket;
// Stream controllers to manage connection status and incoming messages
final _socketState = StreamController();
final _socketController =
StreamController<Map<dynamic, dynamic>?>.broadcast();
/// Initialize the Socket.IO connection
Future _init({
required String socketUrl,
bool alwaysConnected = false,
}) async {
log('Initializing socket with URL: $socketUrl');
InternetConnectionChecker().onStatusChange.listen((event) {
switch (event) {
case InternetConnectionStatus.connected:
log('Internet connected');
log('Socket URL: $socketUrl');
try {
_socket = io_client.io(
socketUrl,
io_client.OptionBuilder()
.setTimeout(10000)
.setReconnectionDelay(5000)
.setReconnectionDelayMax(10000)
.enableAutoConnect()
.enableForceNewConnection()
.enableReconnection()
.setTransports(['websocket']).build(),
);
// Initialize socket event handlers
_setupSocketEventHandlers();
} catch (e, stackTrace) {
log('Socket initialization error: $e');
log('Stack trace: $stackTrace');
}
if (!_socket.connected) {
_connectToSocket();
} else {
log('Socket already connected');
_socketState.add(true);
}
case InternetConnectionStatus.disconnected:
log('Internet disconnected');
_socketState.add(false);
_socket.disconnect();
}
});
final hasConnection = await InternetConnectionChecker().hasConnection;
if (hasConnection && alwaysConnected) {
_connectToSocket();
}
}
void _setupSocketEventHandlers() {
socket
..onConnect(() {
log('Socket connected successfully');
_handleConnection();
debugSocketState();
socketState.add(true);
})
..onDisconnect(() {
log('Socket disconnected');
socketState.add(false);
})
..onConnectError((error) {
log('Socket connection error: $error');
})
..onError((error) {
log('Socket error: $error');
})
..onReconnect(() {
log('Socket reconnected');
_handleConnection();
debugSocketState();
socketState.add(true);
})
..onReconnectError((error) {
log('Socket reconnection error: $error');
})
..onReconnectFailed(() {
log('Socket reconnection failed');
});
}
/// Connect to the Socket.IO server
void _connectToSocket() {
try {
log('Attempting to connect to socket...');
_socket.connect();
} catch (e) {
log('Error connecting to socket: $e');
}
}
void _handleConnection() {
try {
// First emit the merchant data
final merchantData = {
'token': _token,
'appId': _appBuildConfig.appId,
'sender': 'Jules',
'storeId': _storeId,
};
log('Emitting addMerchant event with data: $merchantData');
emit('addMerchant', merchantData);
// Then set up all event listeners
_socket
..on('orderAddTime', (data) {
log('Received orderAddTime: $data');
_emitToController('orderAddTime', data);
})
..on('pendingOrderCount', (data) {
log('Received pendingOrderCount: $data');
_emitToController('pendingOrderCount', data);
})
..on('storeOrder', (data) {
log('Received storeOrder: $data');
_emitToController('storeOrder', data);
})
..on('orderLimitAlert', (data) {
log('Received orderLimitAlert: $data');
_emitToController('orderLimitAlert', data);
})
..on('addMerchant', (data) {
log('Received addMerchant: $data');
_emitToController('addMerchant', data);
})
..on('acceptOrder', (data) {
log('Received acceptOrder: $data');
_emitToController('acceptOrder', data);
})
..on('statusChange', (data) {
log('Received statusChange: $data');
_emitToController('statusChange', data);
})
..on('updateStore', (data) {
log('Received updateStore: $data');
_emitToController('updateStore', data);
});
} catch (e, stackTrace) {
log('Error in _handleConnection: $e');
log('Stack trace: $stackTrace');
}
}
void _emitToController(String event, dynamic data) {
final response = <String, dynamic>{
'event': event,
'data': data,
};
_socketController.add(response);
}
/// Socket connection status stream
Stream get hasSocketConnection async* {
yield false; // Initial state
yield* _socketState.stream;
}
/// Stream to listen for messages from the socket
Stream<Map<dynamic, dynamic>?> get channelStream async* {
yield* _socketController.stream;
}
/// Emit data to a Socket.IO event
Future emit(String event, dynamic data) async {
try {
final hasConnection = await InternetConnectionChecker().hasConnection;
if (!hasConnection) {
log('Cannot emit - No internet connection');
return;
}
if (_socket.connected) {
log('Emitting event: $event');
log('Event data: $data');
_socket.emit(event, data);
} else {
log('Socket not connected - attempting to reconnect');
_connectToSocket();
}
} catch (e) {
log('Error emitting event: $e');
}
}
/// Disconnect from the Socket.IO server
void disconnect() {
try {
_socket.disconnect();
_socketState.add(false);
log('Socket disconnected successfully');
} catch (e) {
log('Error disconnecting socket: $e');
}
}
/// Dispose method to clean up resources
void dispose() {
try {
disconnect();
_socketController.close();
_socketState.close();
log('Socket resources disposed successfully');
} catch (e) {
log('Error disposing socket resources: $e');
}
}
///
void debugSocketState() {
try {
log('=== Socket Debug Information ===');
log('Socket Connected: ${_socket.connected}');
log('Socket ID: ${_socket.id}');
log('Active Transport: ${_socket.io.engine.transport?.name}');
log('Attempted Events:');
_socket.emit('debug_events', {
'token': _token,
'appId': _appBuildConfig.appId,
'storeId': _storeId,
});
// Test emit for each event to verify server handling
final testEvents = [
'orderAddTime',
'pendingOrderCount',
'storeOrder',
'orderLimitAlert',
// 'acceptOrder',
'statusChange',
'updateStore',
];
for (final event in testEvents) {
_socket.emit(event, {
'test': true,
'event': event,
'token': _token,
'appId': _appBuildConfig.appId,
'storeId': _storeId,
});
log('Emitted test for event: $event');
}
} catch (e) {
log('Error in debugSocketState: $e');
}
}
}
the first event addMerchant which I emit after successful connection is responding correctly but these others are not triggered i.e storeOrder