This commit is contained in:
aurinex
2026-01-02 19:22:11 +05:00
parent a76a8b5656
commit 8520f2120d
14 changed files with 709 additions and 106 deletions

View File

@ -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 {