voice v2
This commit is contained in:
@ -5,11 +5,15 @@ import { setVoiceState, getVoiceState } from './voiceStore';
|
||||
|
||||
type PeerMap = Map<string, RTCPeerConnection>;
|
||||
|
||||
export function useVoiceRoom(roomId: string, username: string) {
|
||||
export function useVoiceRoom(username: string) {
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const peersRef = useRef<PeerMap>(new Map());
|
||||
const streamRef = useRef<MediaStream | null>(null);
|
||||
|
||||
const currentRoomIdRef = useRef<string | null>(null);
|
||||
|
||||
const reconnectTimeout = useRef<number | null>(null);
|
||||
|
||||
// const [connected, setConnected] = useState(false);
|
||||
// const [participants, setParticipants] = useState<string[]>([]);
|
||||
// const [muted, setMuted] = useState(false);
|
||||
@ -17,75 +21,111 @@ export function useVoiceRoom(roomId: string, username: string) {
|
||||
const pendingIceRef = useRef<Map<string, RTCIceCandidateInit[]>>(new Map());
|
||||
|
||||
// --- connect ---
|
||||
const connect = useCallback(async () => {
|
||||
if (wsRef.current) return;
|
||||
const connect = useCallback(
|
||||
async (roomId: string) => {
|
||||
if (wsRef.current) return;
|
||||
|
||||
// 1. микрофон
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true,
|
||||
autoGainControl: true,
|
||||
},
|
||||
});
|
||||
streamRef.current = stream;
|
||||
currentRoomIdRef.current = roomId;
|
||||
|
||||
// 2. websocket
|
||||
const ws = new WebSocket(
|
||||
`wss://minecraft.api.popa-popa.ru/ws/voice?room_id=${roomId}&username=${username}`,
|
||||
);
|
||||
wsRef.current = ws;
|
||||
|
||||
ws.onopen = () => {
|
||||
setVoiceState({
|
||||
connected: true,
|
||||
participants: [username],
|
||||
// 1. микрофон
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
echoCancellation: true,
|
||||
noiseSuppression: true,
|
||||
autoGainControl: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
streamRef.current = stream;
|
||||
|
||||
ws.onclose = () => {
|
||||
setVoiceState({
|
||||
connected: false,
|
||||
participants: [],
|
||||
muted: false,
|
||||
});
|
||||
cleanup();
|
||||
};
|
||||
// 2. websocket
|
||||
const ws = new WebSocket(
|
||||
`wss://minecraft.api.popa-popa.ru/ws/voice?room_id=${roomId}&username=${username}`,
|
||||
);
|
||||
wsRef.current = ws;
|
||||
|
||||
// ws.onclose = () => {
|
||||
// cleanup();
|
||||
// setConnected(false);
|
||||
// };
|
||||
ws.onopen = () => {
|
||||
setVoiceState({
|
||||
connected: true,
|
||||
shouldBeConnected: true,
|
||||
participants: [username],
|
||||
});
|
||||
};
|
||||
|
||||
ws.onmessage = async (ev) => {
|
||||
const msg: WSMessage = JSON.parse(ev.data);
|
||||
ws.onclose = () => {
|
||||
cleanup();
|
||||
|
||||
if (msg.type === 'join' && msg.user !== username) {
|
||||
await createPeer(msg.user, false);
|
||||
setVoiceState({ connected: false });
|
||||
|
||||
if (getVoiceState().shouldBeConnected) {
|
||||
reconnectTimeout.current = window.setTimeout(() => {
|
||||
const lastRoomId = currentRoomIdRef.current;
|
||||
if (lastRoomId) {
|
||||
connect(lastRoomId);
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
};
|
||||
|
||||
// ws.onclose = () => {
|
||||
// cleanup();
|
||||
// setConnected(false);
|
||||
// };
|
||||
|
||||
ws.onmessage = async (ev) => {
|
||||
const msg: WSMessage = JSON.parse(ev.data);
|
||||
|
||||
if (msg.type === 'join' && msg.user !== username) {
|
||||
await createPeer(msg.user, false);
|
||||
|
||||
const { participants } = getVoiceState();
|
||||
if (!participants.includes(msg.user)) {
|
||||
setVoiceState({
|
||||
participants: [...participants, msg.user],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.type === 'leave') {
|
||||
removePeer(msg.user);
|
||||
|
||||
const { participants } = getVoiceState();
|
||||
if (!participants.includes(msg.user)) {
|
||||
setVoiceState({
|
||||
participants: [...participants, msg.user],
|
||||
participants: getVoiceState().participants.filter(
|
||||
(u) => u !== msg.user,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.type === 'leave') {
|
||||
removePeer(msg.user);
|
||||
if (msg.type === 'signal') {
|
||||
await handleSignal(msg.from, msg.data);
|
||||
}
|
||||
|
||||
setVoiceState({
|
||||
participants: getVoiceState().participants.filter(
|
||||
(u) => u !== msg.user,
|
||||
),
|
||||
});
|
||||
}
|
||||
if (msg.type === 'users') {
|
||||
const current = getVoiceState().participants;
|
||||
const next = msg.users;
|
||||
|
||||
if (msg.type === 'signal') {
|
||||
await handleSignal(msg.from, msg.data);
|
||||
}
|
||||
};
|
||||
}, [roomId, username]);
|
||||
// 1. удаляем ушедших
|
||||
for (const user of current) {
|
||||
if (!next.includes(user)) {
|
||||
removePeer(user);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. создаём peer для новых
|
||||
for (const user of next) {
|
||||
if (user !== username && !peersRef.current.has(user)) {
|
||||
await createPeer(user, true);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. обновляем store
|
||||
setVoiceState({
|
||||
participants: next,
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
[username],
|
||||
);
|
||||
|
||||
// --- create peer ---
|
||||
const createPeer = async (user: string, polite: boolean) => {
|
||||
@ -232,16 +272,22 @@ export function useVoiceRoom(roomId: string, username: string) {
|
||||
wsRef.current?.close();
|
||||
wsRef.current = null;
|
||||
|
||||
if (reconnectTimeout.current) {
|
||||
clearTimeout(reconnectTimeout.current);
|
||||
reconnectTimeout.current = null;
|
||||
}
|
||||
|
||||
document.querySelectorAll('audio[data-user]').forEach((a) => a.remove());
|
||||
};
|
||||
|
||||
const disconnect = () => {
|
||||
cleanup();
|
||||
setVoiceState({
|
||||
connected: false,
|
||||
shouldBeConnected: false,
|
||||
participants: [],
|
||||
muted: false,
|
||||
});
|
||||
cleanup();
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user