147 lines
4.0 KiB
TypeScript
147 lines
4.0 KiB
TypeScript
import { io, Socket } from 'socket.io-client';
|
|
import { Store } from '@reduxjs/toolkit';
|
|
import { SocketEvents } from '@utils/constants';
|
|
import { DeliveryOffer } from '@interfaces';
|
|
import { setDeliveryOffer } from '@store/commonReducers/delivery';
|
|
import { RouteNames } from '@utils/constants';
|
|
|
|
// Must match the REST API base URL (without trailing slash)
|
|
const BASE_URL = 'https://157a-202-8-116-13.ngrok-free.app';
|
|
|
|
type Callback = (data: any) => void;
|
|
|
|
/**
|
|
* Global singleton socket service powered by socket.io-client.
|
|
*
|
|
* Usage:
|
|
* bootstrapSocketService(store, navRef) // call once in App.tsx
|
|
* socketService.connect(token) // when user authenticates
|
|
* socketService.disconnect() // on logout
|
|
*/
|
|
class SocketService {
|
|
private socket: Socket | null = null;
|
|
private store: Store | null = null;
|
|
private navigationRef: any = null;
|
|
|
|
/**
|
|
* Must be called once at app startup so the service can dispatch actions
|
|
* and navigate globally when a `delivery_offer` event arrives.
|
|
* Each parameter is only updated if a non-null value is provided, allowing
|
|
* the store and navigationRef to be injected independently.
|
|
*/
|
|
bootstrap(store: Store | null, navigationRef: any) {
|
|
if (store) {
|
|
this.store = store;
|
|
}
|
|
if (navigationRef) {
|
|
this.navigationRef = navigationRef;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open the socket connection with JWT auth.
|
|
*/
|
|
connect(token: string) {
|
|
if (this.socket?.connected) {
|
|
console.log('[Socket] Already connected');
|
|
return;
|
|
}
|
|
|
|
this.socket = io(BASE_URL, {
|
|
auth: { token },
|
|
transports: ['websocket'],
|
|
reconnection: true,
|
|
reconnectionAttempts: Infinity,
|
|
reconnectionDelay: 2000,
|
|
});
|
|
|
|
this.socket.on('connect', () => {
|
|
console.log('[Socket] Connected — id:', this.socket?.id);
|
|
});
|
|
|
|
this.socket.on('connect_error', (err: Error) => {
|
|
console.error('[Socket] Connection error:', err.message);
|
|
});
|
|
|
|
this.socket.on('disconnect', (reason: string) => {
|
|
console.log('[Socket] Disconnected:', reason);
|
|
});
|
|
|
|
// ── Listen for delivery offers ─────────────────────────────────────
|
|
this.socket.on(SocketEvents.DeliveryOffer, (data: DeliveryOffer) => {
|
|
console.log('[Socket] delivery_offer received:', data);
|
|
|
|
if (this.store) {
|
|
this.store.dispatch(setDeliveryOffer(data));
|
|
}
|
|
|
|
// Navigate to NewJobRequest screen regardless of current screen
|
|
if (this.navigationRef?.isReady()) {
|
|
this.navigationRef.navigate(RouteNames.NewJobRequest);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gracefully close the socket connection.
|
|
*/
|
|
disconnect() {
|
|
if (this.socket) {
|
|
this.socket.removeAllListeners();
|
|
this.socket.disconnect();
|
|
this.socket = null;
|
|
console.log('[Socket] Disconnected (manual)');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Notify the server that the partner is available for deliveries.
|
|
*/
|
|
goOnline() {
|
|
this.socket?.emit(SocketEvents.GoOnline);
|
|
console.log('[Socket] Emitted go_online');
|
|
}
|
|
|
|
/**
|
|
* Notify the server that the partner is no longer available.
|
|
*/
|
|
goOffline() {
|
|
this.socket?.emit(SocketEvents.GoOffline);
|
|
console.log('[Socket] Emitted go_offline');
|
|
}
|
|
|
|
/**
|
|
* Send a location update to the server.
|
|
*/
|
|
updateLocation(coords: { latitude: number; longitude: number }) {
|
|
this.socket?.emit(SocketEvents.LocationUpdate, coords);
|
|
}
|
|
|
|
/**
|
|
* Register a custom listener on the underlying socket.
|
|
*/
|
|
on(event: string, callback: Callback) {
|
|
this.socket?.on(event, callback);
|
|
}
|
|
|
|
/**
|
|
* Remove a custom listener from the underlying socket.
|
|
*/
|
|
off(event: string, callback?: Callback) {
|
|
if (callback) {
|
|
this.socket?.off(event, callback);
|
|
} else {
|
|
this.socket?.removeAllListeners(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the socket is currently connected.
|
|
*/
|
|
get connected(): boolean {
|
|
return this.socket?.connected ?? false;
|
|
}
|
|
}
|
|
|
|
export const socketService = new SocketService();
|