diff --git a/package-lock.json b/package-lock.json index 64c8073..e98a61d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index f2d02f4..786f45d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/CallContainer.tsx b/src/components/CallContainer.tsx new file mode 100644 index 0000000..a330166 --- /dev/null +++ b/src/components/CallContainer.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +// import './ExploreContainer.css'; + +const CallContainer: React.FC = () => { + + return ( +
+ +
+ ); +}; + +export default CallContainer; diff --git a/src/components/ExploreContainer.css b/src/components/ExploreContainer.css index c2c47cf..f1a7b31 100644 --- a/src/components/ExploreContainer.css +++ b/src/components/ExploreContainer.css @@ -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; -} \ No newline at end of file +} + +/* ExploreContainer.css */ +.call-button { + margin-left: auto; +} diff --git a/src/components/ExploreContainer.tsx b/src/components/ExploreContainer.tsx index 4e009f7..c583de6 100644 --- a/src/components/ExploreContainer.tsx +++ b/src/components/ExploreContainer.tsx @@ -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(null); + const [localStream, setLocalStream] = useState(null); + const [connectionInfo, setConnectionInfo] = useState(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 ( -
- Ready to create an app? -

Start with Ionic UI Components

-
+ <> + + + + + DEBUG MODE + + + + {connectionInfo} + {isLoading && ( +
+ . + . + . +
+ )} +
+ + + + + + {isLoading ? 'Звонок...' : 'Вызов'} + + + + ); }; export default ExploreContainer; + diff --git a/src/components/Input.tsx b/src/components/Input.tsx new file mode 100644 index 0000000..f722f2e --- /dev/null +++ b/src/components/Input.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +// Определение интерфейса для пропсов +interface InputContainerProps { + callString: string; + setCallString: (value: string) => void; + isEnable: boolean; +} + +// InputContainer - форма ввода для начала звонка +const InputContainer: React.FC = ({ callString, setCallString, isEnable }) => { + return ( +
+ setCallString(e.target.value)} + disabled={!isEnable} + placeholder="Enter call string" + /> +
+ ); +}; + +export default InputContainer; diff --git a/src/components/LastCall.tsx b/src/components/LastCall.tsx new file mode 100644 index 0000000..d5aee14 --- /dev/null +++ b/src/components/LastCall.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +// import './ExploreContainer.css'; + +const LastCall: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default LastCall; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index cf9bd29..c19648c 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,13 +8,13 @@ const Home: React.FC = () => { - Blank + Drone RTC - Blank + Drone RTC