-
Notifications
You must be signed in to change notification settings - Fork 64
Open
Description
For some reason, when you close your app and reopen it after some time, you cannot reconnect to the socket on Android unless you clear data on the app is this normal or it is from my code this is the init file
import useUserStore from '@/store/userStore';
import { Pusher, PusherEvent } from '@pusher/pusher-websocket-react-native';
import { broadcastClient } from './axiosClient';
const pusher = Pusher.getInstance();
let isPusherInitialized = false;
let isPusherConnected = false;
let connectPromise: Promise<void> | null = null;
export const initPusher = async () => {
try {
// Check if already initialized
await pusher.reset();
if (isPusherInitialized) {
console.log('Pusher already initialized');
return;
}
try {
if (pusher?.connectionState === 'CONNECTED') {
await pusher?.disconnect();
}
} catch (_) {}
const { token } = useUserStore.getState();
if (!token) {
return;
}
await pusher.init({
apiKey: process.env.EXPO_PUBLIC_PUSHER_API_KEY ?? '',
cluster: process.env.EXPO_PUBLIC_CLUSTER ?? '',
useTLS: true,
authorizerTimeoutInSeconds: 120,
onSubscriptionError(channelName, message, e) {
console.log(`Subscription error on ${channelName}: ${message}`);
console.log('Error details:', e);
},
onConnectionStateChange(currentState, previousState) {
console.log('Pusher state:', previousState, '→', currentState);
// SDK uses upper‑case connection states like "CONNECTED"
const state = (currentState as string | undefined)?.toUpperCase?.();
isPusherConnected = state === 'CONNECTED';
},
onError(message, code, e) {
console.log('Pusher error:', message, code);
},
onDecryptionFailure(eventName, reason) {
console.log('Decryption failure:', reason);
},
async onAuthorizer(channelName, socketId) {
try {
const res = await broadcastClient.post('', {
channel_name: channelName,
socket_id: socketId,
});
console.log('Authorization response:', res.data);
return res.data;
} catch (error) {
console.error('Authorization error:', JSON.stringify(error));
throw error; // Re-throw to let Pusher handle it
}
},
});
isPusherInitialized = true;
// Ensure a single connect in flight and await it
if (!connectPromise) {
connectPromise = pusher
.connect()
.catch((err) => {
console.error('Pusher connect() failed:', err);
isPusherConnected = false;
throw err;
})
.finally(() => {
// allow future reconnect attempts
connectPromise = null;
});
}
await connectPromise;
console.log('Pusher connected successfully');
} catch (error) {
console.error('Failed to initialize Pusher:', error);
isPusherInitialized = false;
isPusherConnected = false;
throw error;
}
};
export const subscribeToChannel = async (
channelName: string,
onEvent: (event: PusherEvent) => void
) => {
try {
// CRITICAL: Ensure Pusher is initialized AND connected
if (!isPusherInitialized) {
console.warn('Pusher not initialized, initializing now...');
await initPusher();
}
// If a connect() is in progress, wait for it
if (connectPromise) {
console.log('Awaiting existing Pusher connectPromise before subscribing...');
await connectPromise;
}
// Wait for connection if not connected yet (with timeout)
if (!isPusherConnected) {
console.log('Waiting for Pusher connection...');
await new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (isPusherConnected) {
clearInterval(checkInterval);
resolve(true);
}
}, 100);
// Timeout after 10 seconds
setTimeout(() => {
console.warn('Pusher connection wait timed out after 10s');
clearInterval(checkInterval);
resolve(false);
}, 10000);
});
}
// Validate channel name
if (!channelName || channelName.trim() === '') {
throw new Error('Invalid channel name');
}
await pusher.subscribe({
channelName: channelName.trim(),
onEvent: (event) => {
console.log('Pusher event:', event);
onEvent(event);
},
});
console.log('Subscribed to channel:', channelName);
} catch (error) {
console.error('Subscription error for', channelName, ':', error);
throw error;
}
};
export const triggerClientEvent = async (channelName: string, eventName: string, data: any) => {
try {
if (!isPusherInitialized) {
await initPusher();
}
if (connectPromise) {
console.log('Awaiting existing Pusher connectPromise before triggering event...');
await connectPromise;
}
if (!isPusherConnected) {
console.log('Waiting for Pusher connection...');
await new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (isPusherConnected) {
clearInterval(checkInterval);
resolve(true);
}
}, 100);
// Timeout after 10 seconds
setTimeout(() => {
console.warn('Pusher connection wait timed out after 10s (triggerClientEvent)');
clearInterval(checkInterval);
resolve(false);
}, 10000);
});
}
if (!eventName.startsWith('client-')) {
throw new Error('Client event names MUST start with "client-"');
}
await pusher.trigger({
channelName,
eventName,
data: JSON.stringify(data),
});
console.log('Client event sent:', eventName, data);
} catch (err) {
console.error('Failed to trigger client event:', err);
}
};
export const unSubscribeToChannel = async (channelName: string) => {
try {
if (!isPusherInitialized || !channelName) {
console.log('Skipping unsubscribe - not initialized or invalid channel');
return;
}
await pusher.unsubscribe({
channelName,
});
console.log('Unsubscribed from channel:', channelName);
} catch (error) {
console.error('Unsubscribe error for', channelName, ':', error);
}
};
export const disconnectPusher = async () => {
try {
if (isPusherConnected && isPusherInitialized) {
await pusher.disconnect();
connectPromise = null;
isPusherConnected = false;
console.log('Pusher disconnected successfully');
} else {
console.log('Pusher already disconnected');
}
} catch (error) {
console.warn('Failed to disconnect pusher:', error);
isPusherConnected = false;
}
};
// Helper function to check connection status
export const isPusherReady = () => {
return isPusherInitialized && isPusherConnected;
};
export default pusher;
Metadata
Metadata
Assignees
Labels
No labels