Compare commits

..

No commits in common. "3c3bbe3eeb4793ee0919adb86279640ed1ee3657" and "4bcafdf3cca7cb53a513ba80f025986503731efa" have entirely different histories.

7 changed files with 97 additions and 299 deletions

View File

@ -1,31 +0,0 @@
#DEV
build:
docker compose build
dev: build
docker compose up -d --force-recreate
devf: dev
docker compose logs -f
up:
docker compose up -d --force-recreate
upf: up
docker compose logs -f
logs:
docker compose logs -f
stop:
"!!!!!!!!!!!!!!!!!EXTERMINATUS!!!!!!!!!!!!!!!!!"
docker compose stop
start:
docker compose start
drop:
docker-compose down --volumes

View File

@ -20,11 +20,11 @@ services:
- "8100:8100" - "8100:8100"
volumes: volumes:
- .:/app - .:/app
# deploy: deploy:
# resources: resources:
# limits: limits:
# cpus: "0.5" cpus: "0.5"
# memory: "512M" memory: "512M"
app2: app2:
build: . build: .
@ -36,8 +36,8 @@ services:
- "8101:8100" - "8101:8100"
volumes: volumes:
- .:/app - .:/app
# deploy: deploy:
# resources: resources:
# limits: limits:
# cpus: "0.5" cpus: "0.5"
# memory: "512M" memory: "512M"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 MiB

View File

@ -1,4 +0,0 @@
#full-screen-modal {
--height: 100%;
--width: 100%;
}

View File

@ -1,70 +1,16 @@
import React, { useState, useEffect } from 'react'; import React from 'react';
import { // import './ExploreContainer.css';
IonButton,
IonModal,
IonHeader,
IonToolbar,
IonTitle,
IonContent,
} from '@ionic/react';
import './CallContainer.css';
import Peer from 'peerjs';
interface InputContainerPerr {
peer: Peer;
isEnable: boolean;
}
// setCalling => bool : true - звонок включен, false - звонок выключен
//
const CallContainer: React.FC = () => { const CallContainer: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false); // Растягивался на весь экран
const [callTime, setCallTime] = useState(0); // По середине: кнопки: завершить звонок, вывод сколько времени идет звонок (обнулять при завершении)
const [callInterval, setCallInterval] = useState<NodeJS.Timeout | null>(null); // Тебе поможет ionic modal
const handleCallStart = () => {
setIsModalOpen(true);
setCallTime(0);
const interval = setInterval(() => {
setCallTime((prevTime) => prevTime + 1);
}, 1000);
setCallInterval(interval);
};
const handleCallEnd = () => {
setIsModalOpen(false);
if (callInterval) {
clearInterval(callInterval);
}
setCallInterval(null);
};
useEffect(() => {
// Очистка интервала при размонтировании компонента
return () => {
if (callInterval) {
clearInterval(callInterval);
}
};
}, [callInterval]);
return ( return (
<div id="container"> <div id="container">
<IonButton onClick={handleCallStart}>Звонок...</IonButton>
<IonModal isOpen={isModalOpen} onDidDismiss={handleCallEnd} id="full-screen-modal">
<IonHeader>
<IonToolbar>
<IonTitle>Звонок идет...</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent className="ion-padding">
<div style={{ textAlign: 'center', marginTop: '20%' }}>
<h2>Время звонка: {callTime} секунд</h2>
<IonButton color="danger" onClick={handleCallEnd}>
Завершить звонок
</IonButton>
</div>
</IonContent>
</IonModal>
</div> </div>
); );
}; };

View File

@ -1,88 +1,23 @@
/* ExploreContainer.css */
#container { #container {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
} }
.peer-id-container { #container strong {
display: flex; font-size: 20px;
align-items: center; line-height: 26px;
margin-bottom: 20px;
} }
.peer-id-text { #container p {
margin-right: 10px;
font-size: 16px; font-size: 16px;
color: #fff; line-height: 22px;
color: #8c8c8c;
margin: 0;
} }
.copy-button { #container a {
background: none; text-decoration: none;
border: none;
color: #007aff;
cursor: pointer;
font-size: 16px;
}
.input-container {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
width: 100%;
}
.call-info-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
.call-info-text {
font-size: 18px;
margin-bottom: 10px;
color: #ffffff;
}
.call-duration-text {
font-size: 18px;
margin-bottom: 20px;
color: #ffffff;
}
.hang-up-button {
background: none;
border: none;
color: #ff3b30; /* Красная трубка */
font-size: 24px;
} }
/* ExploreContainer.css */
.call-button { .call-button {
background: none; margin-left: auto;
border: none;
color: #007aff;
cursor: pointer;
font-size: 24px;
margin-left: 10px;
}
.call-button ion-icon {
font-size: 30px;
color: #007aff;
}
.panel-optimizer {
display: flex;
align-items: center;
justify-content: center;
}
.drone-gif {
width: 100px; /* Задайте нужный размер */
height: auto;
margin-bottom: 20px;
} }

View File

@ -1,11 +1,8 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import './ExploreContainer.css'; import './ExploreContainer.css';
import InputContainer from './Input'; import InputContainer from './Input';
import { import { IonButton, IonContent, IonFooter, IonToolbar, IonPage, IonHeader, IonTitle, IonToast, IonText } from '@ionic/react';
IonButton, IonContent, IonFooter, IonToolbar, IonText, IonModal, IonIcon, IonHeader, IonTitle import Peer from 'peerjs';
} from '@ionic/react';
import { call as callIcon, close as hangUpIcon } from 'ionicons/icons';
import Peer, { MediaConnection } from 'peerjs';
const ExploreContainer: React.FC = () => { const ExploreContainer: React.FC = () => {
const [callString, setCallString] = useState(""); const [callString, setCallString] = useState("");
@ -14,64 +11,58 @@ const ExploreContainer: React.FC = () => {
const [localStream, setLocalStream] = useState<MediaStream | null>(null); const [localStream, setLocalStream] = useState<MediaStream | null>(null);
const [connectionInfo, setConnectionInfo] = useState<string | null>(null); const [connectionInfo, setConnectionInfo] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isCallActive, setIsCallActive] = useState(false);
const [callDuration, setCallDuration] = useState<number>(0);
const [callInterval, setCallInterval] = useState<NodeJS.Timeout | null>(null);
const [currentCallId, setCurrentCallId] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
const initializePeer = () => { // Инициализация Peer без сервера, только для локальной сети
const newPeer = new Peer({ debug: 3 }); const newPeer = new Peer({
setPeer(newPeer); debug: 3,
newPeer.on('open', (id) => {
console.log(`Peer успешно открыт с ID: ${id}`);
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);
}); });
setPeer(newPeer);
newPeer.on('open', (id) => {
console.log(`Peer успешно открыт с ID: ${id}`);
setConnectionInfo(`Ваш Peer ID: ${id}`);
});
newPeer.on('call', (call) => {
console.log('Получен входящий звонок');
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
setLocalStream(stream);
call.answer(stream);
call.on('stream', (remoteStream) => {
console.log('Получен удалённый поток');
playAudio(remoteStream);
});
}).catch((err) => {
console.error('Не удалось получить локальный поток для ответа', err);
});
});
newPeer.on('error', (err) => {
console.error('Ошибка Peer:', err);
});
return () => {
if (peer) {
console.log('Уничтожение Peer');
peer.destroy();
}
};
}, []); }, []);
const makeCall = useCallback(() => { const makeCall = () => {
if (peer && callString) { if (peer && callString) {
setIsLoading(true); setIsLoading(true);
console.log(`Попытка звонка на: ${callString}`); console.log(`Попытка звонка на: ${callString}`);
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);
setCurrentCallId(callString); call.on('stream', (remoteStream) => {
call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток во время вызова'); console.log('Получен удалённый поток во время вызова');
playAudio(remoteStream); playAudio(remoteStream);
setIsLoading(false); setIsLoading(false);
startCallTimer();
setIsCallActive(true);
}); });
}).catch((err) => { }).catch((err) => {
console.error('Не удалось получить локальный поток', err); console.error('Не удалось получить локальный поток', err);
@ -80,29 +71,9 @@ const ExploreContainer: React.FC = () => {
} else { } else {
console.warn('Звонок невозможен: отсутствует Peer или callString'); console.warn('Звонок невозможен: отсутствует Peer или callString');
} }
}, [peer, callString]); };
const endCall = useCallback(() => { const playAudio = (stream: MediaStream) => {
if (localStream) {
localStream.getTracks().forEach(track => track.stop());
}
setIsCallActive(false);
setIsEnable(true);
if (callInterval) {
clearInterval(callInterval);
}
setCallDuration(0);
setCurrentCallId(null);
}, [localStream, callInterval]);
const startCallTimer = useCallback(() => {
const interval = setInterval(() => {
setCallDuration(prevDuration => prevDuration + 1);
}, 1000);
setCallInterval(interval);
}, []);
const playAudio = useCallback((stream: MediaStream) => {
const audioElement = document.createElement('audio'); const audioElement = document.createElement('audio');
audioElement.srcObject = stream; audioElement.srcObject = stream;
audioElement.play().then(() => { audioElement.play().then(() => {
@ -110,58 +81,38 @@ const ExploreContainer: React.FC = () => {
}).catch((error) => { }).catch((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"> <IonHeader>
<div className="peer-id-container"> <IonToolbar>
<IonText className="peer-id-text">Ваш Peer ID: {connectionInfo}</IonText> <IonTitle>
<button className="copy-button" onClick={copyPeerId}>Копировать</button> DEBUG MODE
</IonTitle>
</IonToolbar>
</IonHeader>
<IonText>{connectionInfo}</IonText>
{isLoading && (
<div className="loading-animation">
<span>.</span>
<span>.</span>
<span>.</span>
</div> </div>
</div> )}
{/* Модальное окно для звонка */}
<IonModal isOpen={isCallActive}>
<IonContent className="ion-padding">
<div className="call-info-container">
{/* Добавляем гифку */}
<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>
<IonButton color="danger" onClick={endCall} className="hang-up-button">
<IonIcon icon={hangUpIcon} />
</IonButton>
</div>
</IonContent>
</IonModal>
</IonContent> </IonContent>
<IonFooter> <IonFooter>
<IonToolbar className='panel-optimizer'> <IonToolbar className='panel-optimizer'>
<div className="input-container"> <InputContainer
<InputContainer callString={callString}
callString={callString} setCallString={setCallString}
setCallString={setCallString} isEnable={isEnable}
isEnable={isEnable} />
/> <IonButton className="call-button" onClick={makeCall} disabled={!isEnable || !callString || isLoading} slot="end">
<IonButton className="call-button" onClick={makeCall} disabled={!isEnable || !callString || isLoading}> {isLoading ? 'Звонок...' : 'Вызов'}
<IonIcon icon={callIcon} /> </IonButton>
</IonButton>
</div>
</IonToolbar> </IonToolbar>
</IonFooter> </IonFooter>
</> </>
@ -169,3 +120,4 @@ const ExploreContainer: React.FC = () => {
}; };
export default ExploreContainer; export default ExploreContainer;