obeziyani vmeste sila

This commit is contained in:
wow22831 2024-10-14 14:10:45 +07:00
parent 0679edf1a5
commit 2b3d73e035
2 changed files with 105 additions and 13 deletions

View File

@ -2,26 +2,42 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: space-around; /* Распределение элементов с равным отступом */ justify-content: space-around;
height: 100vh; /* Высота на весь экран для центрирования */ height: 100vh;
text-align: center; text-align: center;
} }
.peer-id-container { .peer-id-container {
margin-bottom: 10px; margin-bottom: 10px;
display: flex;
align-items: center;
} }
.peer-id-text { .peer-id-text {
font-size: 16px; font-size: 16px;
color: #fff; color: #fff;
margin-right: 10px;
} }
.copy-button { .copy-button {
background: none; background: none;
border: none; border: none;
color: #007aff;
cursor: pointer; cursor: pointer;
padding: 0;
}
.copy-notification {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background-color: #4caf50;
color: white;
padding: 10px 20px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
font-size: 16px; font-size: 16px;
z-index: 1000;
} }
.input-container { .input-container {
@ -30,7 +46,7 @@
justify-content: center; justify-content: center;
margin-bottom: 10px; margin-bottom: 10px;
width: 100%; width: 100%;
max-width: 400px; /* Ограничение ширины для компактности */ max-width: 400px;
} }
.call-info-container { .call-info-container {
@ -43,13 +59,13 @@
.call-info-text { .call-info-text {
font-size: 18px; font-size: 18px;
margin-bottom: 5px; /* Небольшой отступ снизу для Peer ID */ margin-bottom: 5px;
color: #ffffff; color: #ffffff;
} }
.call-duration-text { .call-duration-text {
font-size: 18px; font-size: 18px;
margin-bottom: 15px; /* Отступ снизу для времени */ margin-bottom: 15px;
color: #ffffff; color: #ffffff;
} }
@ -66,7 +82,7 @@
} }
.custom-hang-up-button:hover svg { .custom-hang-up-button:hover svg {
transform: scale(1.1); /* Увеличение кнопки при наведении */ transform: scale(1.1);
} }
.call-button { .call-button {
@ -76,7 +92,14 @@
cursor: pointer; cursor: pointer;
font-size: 24px; font-size: 24px;
margin-left: 10px; margin-left: 10px;
padding: 10px; /* Увеличение кнопки для удобства */ padding: 15px;
border-radius: 50%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: background 0.3s ease;
}
.call-button:hover {
background: #005bb5;
} }
.panel-optimizer { .panel-optimizer {
@ -89,11 +112,17 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; /* Центрирование всего контента по высоте экрана */ height: 100vh;
} }
.drone-gif { .drone-gif {
width: 300px; /* Задайте нужный размер */ width: 300px;
height: auto; height: auto;
margin-bottom: 20px; margin-bottom: 20px;
} }
.screen-off {
background-color: #000;
color: #000;
opacity: 0;
}

View File

@ -18,6 +18,10 @@ const ExploreContainer: React.FC = () => {
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 [currentCallId, setCurrentCallId] = useState<string | null>(null);
const [connectionSpeed, setConnectionSpeed] = useState<string>("0 kbps");
const [activeCall, setActiveCall] = useState<MediaConnection | null>(null);
const [isScreenOff, setIsScreenOff] = useState(false);
const [isCopied, setIsCopied] = useState(false);
useEffect(() => { useEffect(() => {
const initializePeer = () => { const initializePeer = () => {
@ -41,9 +45,28 @@ const ExploreContainer: React.FC = () => {
return cleanup; return cleanup;
}, []); }, []);
useEffect(() => {
const handleProximity = (event: any) => {
if (event.value) {
// Если телефон близко к лицу, гасим экран
setIsScreenOff(true);
} else {
// Если телефон не близко, включаем экран
setIsScreenOff(false);
}
};
window.addEventListener('deviceproximity', handleProximity);
return () => {
window.removeEventListener('deviceproximity', handleProximity);
};
}, []);
const handleIncomingCall = useCallback((call: MediaConnection) => { const handleIncomingCall = useCallback((call: MediaConnection) => {
console.log('Получен входящий звонок'); console.log('Получен входящий звонок');
setCurrentCallId(call.peer); setCurrentCallId(call.peer);
setActiveCall(call);
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
setLocalStream(stream); setLocalStream(stream);
call.answer(stream); call.answer(stream);
@ -52,6 +75,10 @@ const ExploreContainer: React.FC = () => {
playAudio(remoteStream); playAudio(remoteStream);
startCallTimer(); startCallTimer();
setIsCallActive(true); setIsCallActive(true);
monitorConnectionSpeed(call);
});
call.on('close', () => {
endCall();
}); });
}).catch((err) => { }).catch((err) => {
console.error('Не удалось получить локальный поток для ответа', err); console.error('Не удалось получить локальный поток для ответа', err);
@ -65,6 +92,7 @@ const ExploreContainer: React.FC = () => {
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
setLocalStream(stream); setLocalStream(stream);
const call = peer.call(callString, stream); const call = peer.call(callString, stream);
setActiveCall(call);
setCurrentCallId(callString); setCurrentCallId(callString);
call.on('stream', (remoteStream: MediaStream) => { call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток во время вызова'); console.log('Получен удалённый поток во время вызова');
@ -72,6 +100,10 @@ const ExploreContainer: React.FC = () => {
setIsLoading(false); setIsLoading(false);
startCallTimer(); startCallTimer();
setIsCallActive(true); setIsCallActive(true);
monitorConnectionSpeed(call);
});
call.on('close', () => {
endCall();
}); });
}).catch((err) => { }).catch((err) => {
console.error('Не удалось получить локальный поток', err); console.error('Не удалось получить локальный поток', err);
@ -86,6 +118,9 @@ const ExploreContainer: React.FC = () => {
if (localStream) { if (localStream) {
localStream.getTracks().forEach(track => track.stop()); localStream.getTracks().forEach(track => track.stop());
} }
if (activeCall) {
activeCall.close();
}
setIsCallActive(false); setIsCallActive(false);
setIsEnable(true); setIsEnable(true);
if (callInterval) { if (callInterval) {
@ -93,7 +128,8 @@ const ExploreContainer: React.FC = () => {
} }
setCallDuration(0); setCallDuration(0);
setCurrentCallId(null); setCurrentCallId(null);
}, [localStream, callInterval]); setConnectionSpeed("0 kbps");
}, [localStream, callInterval, activeCall]);
const startCallTimer = useCallback(() => { const startCallTimer = useCallback(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
@ -112,11 +148,31 @@ const ExploreContainer: React.FC = () => {
}); });
}, []); }, []);
const monitorConnectionSpeed = useCallback((call: MediaConnection) => {
call.on('iceStateChanged', () => {
if (call.peerConnection) {
const stats = call.peerConnection.getStats();
stats.then((report) => {
report.forEach((result) => {
if (result.type === 'candidate-pair' && result.state === 'succeeded') {
const speed = `${Math.round(result.currentRoundTripTime * 1000)} kbps`;
setConnectionSpeed(speed);
}
});
});
}
});
}, []);
const copyPeerId = useCallback(() => { const copyPeerId = useCallback(() => {
if (connectionInfo) { if (connectionInfo) {
navigator.clipboard.writeText(connectionInfo) navigator.clipboard.writeText(connectionInfo)
.then(() => { .then(() => {
console.log('Peer ID скопирован в буфер обмена'); console.log('Peer ID скопирован в буфер обмена');
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
}) })
.catch((err) => { .catch((err) => {
console.error('Не удалось скопировать Peer ID', err); console.error('Не удалось скопировать Peer ID', err);
@ -126,12 +182,18 @@ const ExploreContainer: React.FC = () => {
return ( return (
<> <>
<IonContent fullscreen={true} className="ion-padding"> <IonContent fullscreen={true} className={`ion-padding ${isScreenOff ? 'screen-off' : ''}`}>
<div id="container"> <div id="container">
<div className="peer-id-container"> <div className="peer-id-container">
<IonText className="peer-id-text">Ваш Peer ID: {connectionInfo}</IonText> <IonText className="peer-id-text">Ваш Peer ID: {connectionInfo}</IonText>
<button className="copy-button" onClick={copyPeerId}>Копировать</button> <button className="copy-button" onClick={copyPeerId}>
<svg width="51" height="51" fill="none" stroke="#34554a" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M19.078 6H8.672A2.672 2.672 0 0 0 6 8.672v10.406a2.672 2.672 0 0 0 2.672 2.672h10.406a2.672 2.672 0 0 0 2.672-2.672V8.672A2.672 2.672 0 0 0 19.078 6Z"></path>
<path d="M17.977 6 18 4.875a2.633 2.633 0 0 0-2.625-2.625H5.25a3.009 3.009 0 0 0-3 3v10.125A2.633 2.633 0 0 0 4.875 18H6"></path>
</svg>
</button>
</div> </div>
{isCopied && <div className="copy-notification">Peer ID скопирован</div>}
<div className="input-container"> <div className="input-container">
<InputContainer <InputContainer
callString={callString} callString={callString}
@ -152,6 +214,7 @@ const ExploreContainer: React.FC = () => {
<img src="/monkey-monkey-with-drone.gif" alt="Monkey with Drone" className="drone-gif" /> <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-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>
<IonText className="connection-speed-text">Скорость соединения: {connectionSpeed}</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"> <svg width="51" height="51" fill="#cd1818" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<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> <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>