Files
FunConnect/client/ui/src/hooks/useWebSocket.ts

148 lines
3.9 KiB
TypeScript
Raw Normal View History

import { useEffect, useRef, useCallback, useState } from 'react';
import { listen } from '@tauri-apps/api/event';
import { useAuthStore } from '../stores/authStore';
type SignalingEvent =
| 'friend:request_received'
| 'friend:request_accepted'
| 'friend:online'
| 'friend:offline'
| 'room:invite_received'
| 'room:member_joined'
| 'room:member_left'
| 'network:signaling'
| 'network:status_changed';
interface EventHandler<T = unknown> {
event: SignalingEvent;
handler: (payload: T) => void;
}
export function useSignalingEvents(handlers: EventHandler[]) {
useEffect(() => {
const unsubscribes: (() => void)[] = [];
for (const { event, handler } of handlers) {
listen(event, (e) => handler(e.payload)).then((unsub) => {
unsubscribes.push(unsub);
});
}
return () => {
unsubscribes.forEach((unsub) => unsub());
};
}, [handlers]);
}
export function useNetworkStatus() {
const [status, setStatus] = useState<{
type: 'p2p' | 'relay' | 'disconnected';
port?: number;
connectAddr?: string;
}>({ type: 'disconnected' });
useEffect(() => {
const unsub = listen<{ type: string; port?: number; connect_addr?: string }>(
'network:status_changed',
(event) => {
setStatus({
type: event.payload.type as 'p2p' | 'relay' | 'disconnected',
port: event.payload.port,
connectAddr: event.payload.connect_addr,
});
}
);
return () => {
unsub.then((fn) => fn());
};
}, []);
return status;
}
export function useFriendEvents(
onRequest?: (from: string, username: string) => void,
onAccepted?: (from: string, username: string) => void,
onOnline?: (userId: string) => void,
onOffline?: (userId: string) => void
) {
useEffect(() => {
const unsubscribes: Promise<() => void>[] = [];
if (onRequest) {
unsubscribes.push(
listen<{ from: string; username: string }>('friend:request_received', (e) => {
onRequest(e.payload.from, e.payload.username);
})
);
}
if (onAccepted) {
unsubscribes.push(
listen<{ from: string; username: string }>('friend:request_accepted', (e) => {
onAccepted(e.payload.from, e.payload.username);
})
);
}
if (onOnline) {
unsubscribes.push(
listen<{ user_id: string }>('friend:online', (e) => {
onOnline(e.payload.user_id);
})
);
}
if (onOffline) {
unsubscribes.push(
listen<{ user_id: string }>('friend:offline', (e) => {
onOffline(e.payload.user_id);
})
);
}
return () => {
unsubscribes.forEach((p) => p.then((fn) => fn()));
};
}, [onRequest, onAccepted, onOnline, onOffline]);
}
export function useRoomEvents(
onInvite?: (from: string, roomId: string, roomName: string) => void,
onMemberJoined?: (roomId: string, userId: string, username: string) => void,
onMemberLeft?: (roomId: string, userId: string) => void
) {
useEffect(() => {
const unsubscribes: Promise<() => void>[] = [];
if (onInvite) {
unsubscribes.push(
listen<{ from: string; room_id: string; room_name: string }>('room:invite_received', (e) => {
onInvite(e.payload.from, e.payload.room_id, e.payload.room_name);
})
);
}
if (onMemberJoined) {
unsubscribes.push(
listen<{ room_id: string; user_id: string; username: string }>('room:member_joined', (e) => {
onMemberJoined(e.payload.room_id, e.payload.user_id, e.payload.username);
})
);
}
if (onMemberLeft) {
unsubscribes.push(
listen<{ room_id: string; user_id: string }>('room:member_left', (e) => {
onMemberLeft(e.payload.room_id, e.payload.user_id);
})
);
}
return () => {
unsubscribes.forEach((p) => p.then((fn) => fn()));
};
}, [onInvite, onMemberJoined, onMemberLeft]);
}