basic call

This commit is contained in:
moxitech 2024-10-14 02:26:55 +07:00
parent 335f8c6502
commit a0aed1eeb7
8 changed files with 373 additions and 14 deletions

196
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "abonent_terminal_application",
"version": "0.0.1",
"dependencies": {
"@ionic-native/media-capture": "^5.36.0",
"@ionic/react": "^7.0.0",
"@ionic/react-router": "^7.0.0",
"@testing-library/jest-dom": "^5.11.9",
@ -21,11 +22,15 @@
"@types/react-router-dom": "^5.1.7",
"history": "^4.9.0",
"ionicons": "^7.0.0",
"os": "^0.1.2",
"os-browserify": "^0.3.0",
"peerjs": "^1.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.0",
"socket.io-client": "^4.8.0",
"typescript": "^4.1.3",
"web-vitals": "^0.2.4",
"workbox-background-sync": "^5.1.4",
@ -2437,6 +2442,30 @@
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"deprecated": "Use @eslint/object-schema instead"
},
"node_modules/@ionic-native/core": {
"version": "5.36.0",
"resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-5.36.0.tgz",
"integrity": "sha512-lOrkktadlKYbYf1LrDyAtsu1JnQ0oCCdkOU7iHQ8oXnNOkMwobFfD2m62F1CoOr0u9LIkpYnZSPjng8lZbmbNw==",
"peer": true,
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"rxjs": "^5.5.0 || ^6.5.0"
}
},
"node_modules/@ionic-native/media-capture": {
"version": "5.36.0",
"resolved": "https://registry.npmjs.org/@ionic-native/media-capture/-/media-capture-5.36.0.tgz",
"integrity": "sha512-JBXZYsiIYO95bycF6EU8JBEai6UXdEx3dLnkXzJSYgYt8ynWceSr7Z/nPeuYfScEiKI3JA5FmaUGUsTkyHXOnw==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@ionic-native/core": "^5.1.0",
"rxjs": "^5.5.0 || ^6.5.0"
}
},
"node_modules/@ionic/core": {
"version": "7.8.6",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.8.6.tgz",
@ -2993,6 +3022,14 @@
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
"integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="
},
"node_modules/@msgpack/msgpack": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
"integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==",
"engines": {
"node": ">= 10"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -3210,6 +3247,11 @@
"@sinonjs/commons": "^1.7.0"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
},
"node_modules/@stencil/core": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.22.1.tgz",
@ -3642,6 +3684,11 @@
"@types/node": "*"
}
},
"node_modules/@types/cordova": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz",
"integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg=="
},
"node_modules/@types/eslint": {
"version": "8.56.12",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
@ -6672,6 +6719,46 @@
"node": ">= 0.8"
}
},
"node_modules/engine.io-client": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
"integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.1.1"
}
},
"node_modules/engine.io-client/node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/engine.io-parser": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/enhanced-resolve": {
"version": "5.17.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
@ -11377,6 +11464,16 @@
"node": ">= 0.8.0"
}
},
"node_modules/os": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/os/-/os-0.1.2.tgz",
"integrity": "sha512-ZoXJkvAnljwvc56MbvhtKVWmSkzV712k42Is2mA0+0KTSRakq5XXuXpjZjgAt9ctzl51ojhQWakQQpmOvXWfjQ=="
},
"node_modules/os-browserify": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
"integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@ -11554,6 +11651,36 @@
"node": ">=8"
}
},
"node_modules/peerjs": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.5.4.tgz",
"integrity": "sha512-yFsoLMnurJKlQbx6kVSBpOp+AlNldY1JQS2BrSsHLKCZnq6t7saHleuHM5svuLNbQkUJXHLF3sKOJB1K0xulOw==",
"dependencies": {
"@msgpack/msgpack": "^2.8.0",
"eventemitter3": "^4.0.7",
"peerjs-js-binarypack": "^2.1.0",
"webrtc-adapter": "^9.0.0"
},
"engines": {
"node": ">= 14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/peer"
}
},
"node_modules/peerjs-js-binarypack": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-2.1.0.tgz",
"integrity": "sha512-YIwCC+pTzp3Bi8jPI9UFKO0t0SLo6xALnHkiNt/iUFmUUZG0fEEmEyFKvjsDKweiFitzHRyhuh6NvyJZ4nNxMg==",
"engines": {
"node": ">= 14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/peer"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@ -13829,6 +13956,24 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"peer": true,
"dependencies": {
"tslib": "^1.9.0"
},
"engines": {
"npm": ">=2.0.0"
}
},
"node_modules/rxjs/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"peer": true
},
"node_modules/safe-array-concat": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
@ -14006,6 +14151,11 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/sdp": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz",
"integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw=="
},
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -14267,6 +14417,32 @@
"node": ">=8"
}
},
"node_modules/socket.io-client": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz",
"integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.6.1",
"socket.io-parser": "~4.2.4"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
@ -16036,6 +16212,18 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/webrtc-adapter": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.1.tgz",
"integrity": "sha512-1AQO+d4ElfVSXyzNVTOewgGT/tAomwwztX/6e3totvyyzXPvXIIuUUjAmyZGbKBKbZOXauuJooZm3g6IuFuiNQ==",
"dependencies": {
"sdp": "^3.2.0"
},
"engines": {
"node": ">=6.0.0",
"npm": ">=3.10.0"
}
},
"node_modules/websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
@ -16729,6 +16917,14 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -3,6 +3,7 @@
"version": "0.0.1",
"private": true,
"dependencies": {
"@ionic-native/media-capture": "^5.36.0",
"@ionic/react": "^7.0.0",
"@ionic/react-router": "^7.0.0",
"@testing-library/jest-dom": "^5.11.9",
@ -16,11 +17,15 @@
"@types/react-router-dom": "^5.1.7",
"history": "^4.9.0",
"ionicons": "^7.0.0",
"os": "^0.1.2",
"os-browserify": "^0.3.0",
"peerjs": "^1.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.0",
"socket.io-client": "^4.8.0",
"typescript": "^4.1.3",
"web-vitals": "^0.2.4",
"workbox-background-sync": "^5.1.4",

View File

@ -0,0 +1,13 @@
import React from 'react';
// import './ExploreContainer.css';
const CallContainer: React.FC = () => {
return (
<div id="container">
</div>
);
};
export default CallContainer;

View File

@ -1,10 +1,4 @@
#container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
#container strong {
@ -21,4 +15,9 @@
#container a {
text-decoration: none;
}
}
/* ExploreContainer.css */
.call-button {
margin-left: auto;
}

View File

@ -1,13 +1,122 @@
import React from 'react';
import React, { useState, useEffect } 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';
const ExploreContainer: React.FC = () => {
const [callString, setCallString] = useState("");
const [isEnable, setIsEnable] = useState(true);
const [peer, setPeer] = useState<Peer | null>(null);
const [localStream, setLocalStream] = useState<MediaStream | null>(null);
const [connectionInfo, setConnectionInfo] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
// Инициализация Peer без сервера, только для локальной сети
const newPeer = new Peer({
debug: 3,
});
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 = () => {
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) => {
console.log('Получен удалённый поток во время вызова');
playAudio(remoteStream);
setIsLoading(false);
});
}).catch((err) => {
console.error('Не удалось получить локальный поток', err);
setIsLoading(false);
});
} else {
console.warn('Звонок невозможен: отсутствует Peer или callString');
}
};
const playAudio = (stream: MediaStream) => {
const audioElement = document.createElement('audio');
audioElement.srcObject = stream;
audioElement.play().then(() => {
console.log('Аудио начато воспроизведение');
}).catch((error) => {
console.error('Ошибка воспроизведения аудио', error);
});
};
return (
<div id="container">
<strong>Ready to create an app?</strong>
<p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
</div>
<>
<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>
)}
</IonContent>
<IonFooter>
<IonToolbar className='panel-optimizer'>
<InputContainer
callString={callString}
setCallString={setCallString}
isEnable={isEnable}
/>
<IonButton className="call-button" onClick={makeCall} disabled={!isEnable || !callString || isLoading} slot="end">
{isLoading ? 'Звонок...' : 'Вызов'}
</IonButton>
</IonToolbar>
</IonFooter>
</>
);
};
export default ExploreContainer;

25
src/components/Input.tsx Normal file
View File

@ -0,0 +1,25 @@
import React from 'react';
// Определение интерфейса для пропсов
interface InputContainerProps {
callString: string;
setCallString: (value: string) => void;
isEnable: boolean;
}
// InputContainer - форма ввода для начала звонка
const InputContainer: React.FC<InputContainerProps> = ({ callString, setCallString, isEnable }) => {
return (
<div id="container">
<input
type="text"
value={callString}
onChange={(e) => setCallString(e.target.value)}
disabled={!isEnable}
placeholder="Enter call string"
/>
</div>
);
};
export default InputContainer;

View File

@ -0,0 +1,12 @@
import React from 'react';
// import './ExploreContainer.css';
const LastCall: React.FC = () => {
return (
<div id="container">
</div>
);
};
export default LastCall;

View File

@ -8,13 +8,13 @@ const Home: React.FC = () => {
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Blank</IonTitle>
<IonTitle>Drone RTC</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Blank</IonTitle>
<IonTitle size="large">Drone RTC</IonTitle>
</IonToolbar>
</IonHeader>
<ExploreContainer />