uaaa_monkey

This commit is contained in:
wow22831 2024-10-14 05:30:25 +07:00
parent 4bcafdf3cc
commit 8c2efe1dde
7 changed files with 305 additions and 103 deletions

31
Makefile Normal file
View File

@ -0,0 +1,31 @@
#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"
volumes:
- .:/app
deploy:
resources:
limits:
cpus: "0.5"
memory: "512M"
# deploy:
# resources:
# limits:
# cpus: "0.5"
# memory: "512M"
app2:
build: .
@ -36,8 +36,8 @@ services:
- "8101:8100"
volumes:
- .:/app
deploy:
resources:
limits:
cpus: "0.5"
memory: "512M"
# deploy:
# resources:
# limits:
# cpus: "0.5"
# memory: "512M"

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 MiB

View File

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

View File

@ -1,16 +1,70 @@
import React from 'react';
// import './ExploreContainer.css';
import React, { useState, useEffect } from 'react';
import {
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 = () => {
// Растягивался на весь экран
// По середине: кнопки: завершить звонок, вывод сколько времени идет звонок (обнулять при завершении)
// Тебе поможет ionic modal
const [isModalOpen, setIsModalOpen] = useState(false);
const [callTime, setCallTime] = useState(0);
const [callInterval, setCallInterval] = useState<NodeJS.Timeout | null>(null);
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 (
<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>
);
};

View File

@ -1,23 +1,88 @@
#container {
}
#container strong {
font-size: 20px;
line-height: 26px;
}
#container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
#container a {
text-decoration: none;
}
/* ExploreContainer.css */
.call-button {
margin-left: auto;
#container {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.peer-id-container {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.peer-id-text {
margin-right: 10px;
font-size: 16px;
color: #fff;
}
.copy-button {
background: 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;
}
.call-button {
background: none;
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,8 +1,11 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import './ExploreContainer.css';
import InputContainer from './Input';
import { IonButton, IonContent, IonFooter, IonToolbar, IonPage, IonHeader, IonTitle, IonToast, IonText } from '@ionic/react';
import Peer from 'peerjs';
import {
IonButton, IonContent, IonFooter, IonToolbar, IonText, IonModal, IonIcon, IonHeader, IonTitle
} from '@ionic/react';
import { call as callIcon, close as hangUpIcon } from 'ionicons/icons';
import Peer, { MediaConnection } from 'peerjs';
const ExploreContainer: React.FC = () => {
const [callString, setCallString] = useState("");
@ -11,58 +14,64 @@ const ExploreContainer: React.FC = () => {
const [localStream, setLocalStream] = useState<MediaStream | null>(null);
const [connectionInfo, setConnectionInfo] = useState<string | null>(null);
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(() => {
// Инициализация Peer без сервера, только для локальной сети
const newPeer = new Peer({
debug: 3,
});
const initializePeer = () => {
const newPeer = new Peer({ debug: 3 });
setPeer(newPeer);
newPeer.on('open', (id) => {
console.log(`Peer успешно открыт с ID: ${id}`);
setConnectionInfo(`Ваш Peer ID: ${id}`);
setConnectionInfo(id);
});
newPeer.on('call', (call) => {
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) => {
call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток');
playAudio(remoteStream);
startCallTimer();
setIsCallActive(true);
});
}).catch((err) => {
console.error('Не удалось получить локальный поток для ответа', err);
});
});
newPeer.on('error', (err) => {
console.error('Ошибка Peer:', err);
});
return () => {
if (peer) {
console.log('Уничтожение Peer');
peer.destroy();
}
};
}, []);
const makeCall = () => {
const makeCall = useCallback(() => {
if (peer && callString) {
setIsLoading(true);
console.log(`Попытка звонка на: ${callString}`);
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
setLocalStream(stream);
const call = peer.call(callString, stream);
call.on('stream', (remoteStream) => {
setCurrentCallId(callString);
call.on('stream', (remoteStream: MediaStream) => {
console.log('Получен удалённый поток во время вызова');
playAudio(remoteStream);
setIsLoading(false);
startCallTimer();
setIsCallActive(true);
});
}).catch((err) => {
console.error('Не удалось получить локальный поток', err);
@ -71,9 +80,29 @@ const ExploreContainer: React.FC = () => {
} else {
console.warn('Звонок невозможен: отсутствует Peer или callString');
}
};
}, [peer, callString]);
const playAudio = (stream: MediaStream) => {
const endCall = useCallback(() => {
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');
audioElement.srcObject = stream;
audioElement.play().then(() => {
@ -81,38 +110,58 @@ const ExploreContainer: React.FC = () => {
}).catch((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 (
<>
<IonContent fullscreen={true} className="ion-padding">
<IonHeader>
<IonToolbar>
<IonTitle>
DEBUG MODE
</IonTitle>
</IonToolbar>
</IonHeader>
<IonText>{connectionInfo}</IonText>
{isLoading && (
<div className="loading-animation">
<span>.</span>
<span>.</span>
<span>.</span>
<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>
{/* Модальное окно для звонка */}
<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>
<IonFooter>
<IonToolbar className='panel-optimizer'>
<div className="input-container">
<InputContainer
callString={callString}
setCallString={setCallString}
isEnable={isEnable}
/>
<IonButton className="call-button" onClick={makeCall} disabled={!isEnable || !callString || isLoading} slot="end">
{isLoading ? 'Звонок...' : 'Вызов'}
<IonButton className="call-button" onClick={makeCall} disabled={!isEnable || !callString || isLoading}>
<IonIcon icon={callIcon} />
</IonButton>
</div>
</IonToolbar>
</IonFooter>
</>
@ -120,4 +169,3 @@ const ExploreContainer: React.FC = () => {
};
export default ExploreContainer;