This commit is contained in:
wow22831 2024-10-24 21:00:29 +07:00
parent b5e1d5ddf3
commit f146f46eb2

View File

@ -8,137 +8,116 @@ import { call as callIcon, close as hangUpIcon } from 'ionicons/icons';
import Peer, { MediaConnection } from 'peerjs'; import Peer, { MediaConnection } from 'peerjs';
const ExploreContainer: React.FC = () => { const ExploreContainer: React.FC = () => {
const [callString, setCallString] = useState(""); const [callIp, setCallIp] = useState<string>("");
const [isEnable, setIsEnable] = useState(true);
const [peer, setPeer] = useState<Peer | null>(null); const [peer, setPeer] = useState<Peer | null>(null);
const [localStream, setLocalStream] = useState<MediaStream | null>(null); const [localStream, setLocalStream] = useState<MediaStream | null>(null);
const [connectionInfo, setConnectionInfo] = useState<string | null>(null); const [remoteStream, setRemoteStream] = useState<MediaStream | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isCallActive, setIsCallActive] = useState<boolean>(false);
const [isCallActive, setIsCallActive] = useState(false);
const [callDuration, setCallDuration] = useState<number>(0); const [callDuration, setCallDuration] = useState<number>(0);
const [callInterval, setCallInterval] = useState<NodeJS.Timeout | null>(null); const [callInterval, setCallInterval] = useState<NodeJS.Timeout | null>(null);
const [currentCallId, setCurrentCallId] = useState<string | null>(null); const [currentCall, setCurrentCall] = useState<MediaConnection | null>(null);
useEffect(() => { useEffect(() => {
const initializePeer = () => { const peerId = Math.floor(Math.random() * 10000).toString();
const newPeer = new Peer({ debug: 3 }); const newPeer: Peer = new Peer(peerId, {
setPeer(newPeer); host: 'localhost',
port: 8100, // Используемый порт
newPeer.on('open', (id) => { path: '/peerjs',
console.log(`Peer успешно открыт с ID: ${id}`); debug: 3
setConnectionInfo(id);
});
newPeer.on('call', (call: MediaConnection) => handleIncomingCall(call));
newPeer.on('error', (err) => console.error('Ошибка Peer:', err));
return () => {
newPeer.destroy();
};
};
const cleanup = initializePeer();
return cleanup;
}, []);
const handleIncomingCall = useCallback((call: MediaConnection) => {
console.log('Получен входящий звонок');
setCurrentCallId(call.peer);
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
setLocalStream(stream);
call.answer(stream);
call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток');
playAudio(remoteStream);
startCallTimer();
setIsCallActive(true);
});
}).catch((err) => {
console.error('Не удалось получить локальный поток для ответа', err);
}); });
}, []);
const makeCall = useCallback(() => { newPeer.on('call', (call: MediaConnection) => {
if (peer && callString) { console.log('Получен входящий звонок');
setIsLoading(true); navigator.mediaDevices.getUserMedia({ audio: true }).then((stream: MediaStream) => {
console.log(`Попытка звонка на: ${callString}`);
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
setLocalStream(stream); setLocalStream(stream);
const call = peer.call(callString, stream); call.answer(stream);
setCurrentCallId(callString);
call.on('stream', (remoteStream: MediaStream) => { call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток во время вызова'); console.log('Получен удалённый поток');
setRemoteStream(remoteStream);
playAudio(remoteStream); playAudio(remoteStream);
setIsLoading(false);
startCallTimer(); startCallTimer();
setIsCallActive(true); setIsCallActive(true);
}); });
}).catch((err) => { setCurrentCall(call);
}).catch((err: Error) => {
console.error('Не удалось получить локальный поток для ответа', err);
});
});
setPeer(newPeer);
return () => {
newPeer.destroy();
};
}, []);
const makeCall = useCallback(() => {
if (peer && callIp) {
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream: MediaStream) => {
setLocalStream(stream);
const call: MediaConnection = peer.call(callIp, stream);
call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток во время вызова');
setRemoteStream(remoteStream);
playAudio(remoteStream);
startCallTimer();
setIsCallActive(true);
});
setCurrentCall(call);
}).catch((err: Error) => {
console.error('Не удалось получить локальный поток', err); console.error('Не удалось получить локальный поток', err);
setIsLoading(false);
}); });
} else { } else {
console.warn('Звонок невозможен: отсутствует Peer или callString'); console.warn('Звонок невозможен: отсутствует Peer или callIp');
} }
}, [peer, callString]); }, [peer, callIp]);
const endCall = useCallback(() => { const endCall = useCallback(() => {
if (localStream) { if (localStream) {
localStream.getTracks().forEach(track => track.stop()); localStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
}
if (remoteStream) {
remoteStream.getTracks().forEach((track: MediaStreamTrack) => track.stop());
}
if (currentCall) {
currentCall.close();
} }
setIsCallActive(false); setIsCallActive(false);
setIsEnable(true);
if (callInterval) { if (callInterval) {
clearInterval(callInterval); clearInterval(callInterval);
} }
setCallDuration(0); setCallDuration(0);
setCurrentCallId(null); setCurrentCall(null);
}, [localStream, callInterval]); }, [localStream, remoteStream, currentCall, callInterval]);
const startCallTimer = useCallback(() => { const startCallTimer = useCallback(() => {
const interval = setInterval(() => { const interval: NodeJS.Timeout = setInterval(() => {
setCallDuration(prevDuration => prevDuration + 1); setCallDuration((prevDuration: number) => prevDuration + 1);
}, 1000); }, 1000);
setCallInterval(interval); setCallInterval(interval);
}, []); }, []);
const playAudio = useCallback((stream: MediaStream) => { const playAudio = useCallback((stream: MediaStream) => {
const audioElement = document.createElement('audio'); const audioElement: HTMLAudioElement = document.createElement('audio');
audioElement.srcObject = stream; audioElement.srcObject = stream;
audioElement.play().then(() => { audioElement.play().then(() => {
console.log('Аудио начато воспроизведение'); console.log('Аудио начато воспроизведение');
}).catch((error) => { }).catch((error: Error) => {
console.error('Ошибка воспроизведения аудио', error); console.error('Ошибка воспроизведения аудио', error);
}); });
}, []); }, []);
const copyPeerId = useCallback(() => {
if (connectionInfo) {
navigator.clipboard.writeText(connectionInfo)
.then(() => {
console.log('Peer ID скопирован в буфер обмена');
})
.catch((err) => {
console.error('Не удалось скопировать Peer ID', err);
});
}
}, [connectionInfo]);
return ( return (
<> <>
<IonContent fullscreen={true} className="ion-padding"> <IonContent fullscreen={true} className="ion-padding">
<div id="container"> <div id="container">
<div className="peer-id-container">
<IonText className="peer-id-text">Ваш Peer ID: {connectionInfo}</IonText>
<button className="copy-button" onClick={copyPeerId}>Копировать</button>
</div>
<div className="input-container"> <div className="input-container">
<InputContainer <InputContainer
callString={callString} callString={callIp}
setCallString={setCallString} setCallString={setCallIp}
isEnable={isEnable} isEnable={!isCallActive}
/> />
<IonButton className="call-button" onClick={makeCall} disabled={!isEnable || !callString || isLoading}> <IonButton className="call-button" onClick={makeCall} disabled={!callIp || isCallActive}>
<IonIcon icon={callIcon} /> <IonIcon icon={callIcon} />
</IonButton> </IonButton>
</div> </div>
@ -148,14 +127,10 @@ const ExploreContainer: React.FC = () => {
<IonModal isOpen={isCallActive}> <IonModal isOpen={isCallActive}>
<IonContent className="ion-padding"> <IonContent className="ion-padding">
<div className="call-info-container"> <div className="call-info-container">
{/* Добавляем гифку */} <IonText className="call-info-text">IP: {callIp}</IonText>
<img src="/monkey-monkey-with-drone.gif" alt="Monkey with Drone" className="drone-gif" />
<IonText className="call-info-text">Peer ID: {currentCallId}</IonText>
<IonText className="call-duration-text">{Math.floor(callDuration / 60)}:{callDuration % 60}</IonText> <IonText className="call-duration-text">{Math.floor(callDuration / 60)}:{callDuration % 60}</IonText>
<button onClick={endCall} className="custom-hang-up-button"> <button onClick={endCall} className="custom-hang-up-button">
<svg width="51" height="51" fill="#cd1818" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <IonIcon icon={hangUpIcon} />
<path d="M18.327 22.5c-.915 0-2.2-.331-4.125-1.407-2.34-1.312-4.15-2.524-6.478-4.846-2.245-2.243-3.337-3.695-4.865-6.476C1.132 6.63 1.426 4.984 1.755 4.28c.392-.842.97-1.345 1.718-1.844a8.263 8.263 0 0 1 1.343-.712l.13-.057c.231-.105.583-.263 1.028-.094.297.112.562.34.978.75.852.84 2.015 2.71 2.445 3.63.288.619.479 1.028.48 1.486 0 .537-.27.95-.598 1.397l-.182.242c-.356.469-.435.604-.383.846.104.486.884 1.933 2.165 3.212 1.281 1.278 2.686 2.008 3.174 2.112.253.054.39-.027.875-.397.069-.053.14-.107.215-.162.5-.372.894-.635 1.418-.635h.003c.456 0 .847.198 1.493.524.844.426 2.771 1.575 3.616 2.427.412.415.64.679.753.976.169.447.01.797-.094 1.031l-.057.129a8.27 8.27 0 0 1-.716 1.34c-.499.745-1.004 1.322-1.846 1.714a3.16 3.16 0 0 1-1.386.304Z"></path>
</svg>
</button> </button>
</div> </div>
</IonContent> </IonContent>
@ -165,4 +140,4 @@ const ExploreContainer: React.FC = () => {
); );
}; };
export default ExploreContainer; export default ExploreContainer;