react-peer-chat 0.3.10 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -22
- package/build/icons.js +5 -5
- package/build/index.d.ts +9 -3
- package/build/index.js +49 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# react-peer-chat
|
|
2
2
|
An easy to use react component for impleting peer-to-peer chatting using [peerjs](https://peerjs.com/) under the hood.
|
|
3
3
|
|
|
4
|
-
It is as easy as to import a React component!
|
|
5
4
|
## Features
|
|
6
5
|
- Peer-to-peer chat without need to have any knowledge about WebRTC
|
|
7
6
|
- Easy to use
|
|
8
7
|
- Supports text chat that persists on page reload
|
|
9
8
|
- Clear text chat on command
|
|
10
9
|
- Supports voice chat
|
|
10
|
+
- Multiple peer connections. See [multi peer usage](#Multi-Peer-Usage)
|
|
11
11
|
- Fully Customizable. See [usage with FoC](#Full-Customization)
|
|
12
12
|
## Installation
|
|
13
13
|
To install react-peer-chat
|
|
@@ -38,8 +38,26 @@ export default function App() {
|
|
|
38
38
|
return <div>
|
|
39
39
|
<Chat
|
|
40
40
|
name='John Doe'
|
|
41
|
-
peerId='
|
|
42
|
-
remotePeerId='
|
|
41
|
+
peerId='my-unique-id'
|
|
42
|
+
remotePeerId='remote-unique-id'
|
|
43
|
+
/>
|
|
44
|
+
{/* Text chat will be cleared when following button is clicked. */}
|
|
45
|
+
<button onClick={clearChat}>Clear Chat</button>
|
|
46
|
+
</div>
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
#### Multi Peer Usage
|
|
50
|
+
```jsx
|
|
51
|
+
import React from 'react';
|
|
52
|
+
import Chat, { clearChat } from 'react-peer-chat';
|
|
53
|
+
import 'react-peer-chat/build/styles.css';
|
|
54
|
+
|
|
55
|
+
export default function App() {
|
|
56
|
+
return <div>
|
|
57
|
+
<Chat
|
|
58
|
+
name='John Doe'
|
|
59
|
+
peerId='my-unique-id'
|
|
60
|
+
remotePeerId={['remote-unique-id-1', 'remote-unique-id-2', 'remote-unique-id-3']} // Array of remote peer ids
|
|
43
61
|
/>
|
|
44
62
|
{/* Text chat will be cleared when following button is clicked. */}
|
|
45
63
|
<button onClick={clearChat}>Clear Chat</button>
|
|
@@ -56,16 +74,15 @@ import 'react-peer-chat/build/styles.css';
|
|
|
56
74
|
export default function App() {
|
|
57
75
|
return <Chat
|
|
58
76
|
name='John Doe'
|
|
59
|
-
peerId='
|
|
60
|
-
remotePeerId='
|
|
61
|
-
dialogOptions={{
|
|
77
|
+
peerId='my-unique-id'
|
|
78
|
+
remotePeerId='remote-unique-id'
|
|
79
|
+
dialogOptions={{
|
|
62
80
|
position: 'left',
|
|
63
81
|
style: { padding: '4px' }
|
|
64
82
|
}}
|
|
65
83
|
props={{ title: 'React Peer Chat Component' }}
|
|
66
|
-
onError={() =>
|
|
67
|
-
|
|
68
|
-
}}
|
|
84
|
+
onError={() => console.error('Browser not supported!')}
|
|
85
|
+
onMicError={() => console.error('Microphone not accessible!')}
|
|
69
86
|
/>
|
|
70
87
|
}
|
|
71
88
|
```
|
|
@@ -79,13 +96,12 @@ import Chat from 'react-peer-chat'
|
|
|
79
96
|
export default function App() {
|
|
80
97
|
return <Chat
|
|
81
98
|
name='John Doe'
|
|
82
|
-
peerId='
|
|
83
|
-
remotePeerId='
|
|
84
|
-
onError={() =>
|
|
85
|
-
|
|
86
|
-
}}
|
|
99
|
+
peerId='my-unique-id'
|
|
100
|
+
remotePeerId='remote-unique-id'
|
|
101
|
+
onError={() => console.error('Browser not supported!')}
|
|
102
|
+
onMicError={() => console.error('Microphone not accessible!')}
|
|
87
103
|
>
|
|
88
|
-
{({
|
|
104
|
+
{({ remotePeers, messages, addMessage, audio, setAudio }) => (
|
|
89
105
|
<YourCustomComponent>
|
|
90
106
|
{...}
|
|
91
107
|
</YourCustomComponent>
|
|
@@ -97,14 +113,15 @@ export default function App() {
|
|
|
97
113
|
Here is the full API for the `<Chat>` component, these properties can be set on an instance of Chat:
|
|
98
114
|
| Parameter | Type | Required | Default | Description |
|
|
99
115
|
| - | - | - | - | - |
|
|
100
|
-
| `name` | `
|
|
101
|
-
| `peerId` | `
|
|
102
|
-
| `remotePeerId` | `
|
|
103
|
-
| `text` | `
|
|
104
|
-
| `voice` | `
|
|
116
|
+
| `name` | `string` | No | Anonymous User | Name of the peer which will be shown to the remote peer. |
|
|
117
|
+
| `peerId` | `string` | Yes | - | It is the unique id that is alloted to a peer. It uniquely identifies a peer from other peers. |
|
|
118
|
+
| `remotePeerId` | `string \| string[]` | No | - | It is the unique id (or array of unique ids) of the remote peer(s). If provided, the peer will try to connect to the remote peer(s). |
|
|
119
|
+
| `text` | `boolean` | No | `true` | Text chat will be enabled if this property is set to true. |
|
|
120
|
+
| `voice` | `boolean` | No | `true` | Voice chat will be enabled if this property is set to true. |
|
|
105
121
|
| `peerOptions` | [`PeerOptions`](#PeerOptions) | No | - | Options to customize peerjs Peer instance. |
|
|
106
122
|
| `dialogOptions` | [`DialogOptions`](#DialogOptions) | No | { position: 'center' } | Options to customize text dialog box styling. |
|
|
107
|
-
| `onError` | `Function` | No | `() => alert('
|
|
123
|
+
| `onError` | `Function` | No | `() => alert('Browser not supported! Try some other browser.')` | Function to be executed if browser doesn't support [WebRTC](https://webrtc.org/). |
|
|
124
|
+
| `onMicError` | `Function` | No | `() => alert('Microphone not accessible!')` | Function to be executed when microphone is not accessible. |
|
|
108
125
|
| `props` | `React.DetailedHTMLProps` | No | - | Props to customize the `<Chat>` component. |
|
|
109
126
|
| `children` | [`Children`](#Children) | No | - | Props to customize the `<Chat>` component. |
|
|
110
127
|
### Types
|
|
@@ -124,12 +141,13 @@ type DialogOptions = {
|
|
|
124
141
|
#### Children
|
|
125
142
|
```typescript
|
|
126
143
|
import { ReactNode } from 'react';
|
|
144
|
+
type RemotePeers = { [id: string]: string }
|
|
127
145
|
type Message = {
|
|
128
146
|
id: string;
|
|
129
147
|
text: string;
|
|
130
148
|
};
|
|
131
149
|
type ChildrenOptions = {
|
|
132
|
-
|
|
150
|
+
remotePeers?: RemotePeers;
|
|
133
151
|
messages?: Message[];
|
|
134
152
|
addMessage?: (message: Message, sendToRemotePeer?: boolean) => void;
|
|
135
153
|
audio?: boolean;
|
package/build/icons.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
export function BiSolidMessageDetail(props) {
|
|
3
|
-
return React.createElement("span", { className: 'rpc-icon-container',
|
|
3
|
+
return React.createElement("span", Object.assign({ className: 'rpc-icon-container' }, props),
|
|
4
4
|
React.createElement("svg", { fill: "currentColor", width: '1.5rem', height: '1.5rem', className: 'rpc-scale-down' },
|
|
5
5
|
React.createElement("path", { d: "M20 2H4c-1.103 0-2 .894-2 1.992v12.016C2 17.106 2.897 18 4 18h3v4l6.351-4H20c1.103 0 2-.894 2-1.992V3.992A1.998 1.998 0 0 0 20 2zm-6 11H7v-2h7v2zm3-4H7V7h10v2z" })));
|
|
6
6
|
}
|
|
7
7
|
export function BiSolidMessageX(props) {
|
|
8
|
-
return React.createElement("span", { className: 'rpc-icon-container',
|
|
8
|
+
return React.createElement("span", Object.assign({ className: 'rpc-icon-container' }, props),
|
|
9
9
|
React.createElement("svg", { fill: "currentColor", width: '1.5rem', height: '1.5rem', className: 'rpc-scale-down' },
|
|
10
10
|
React.createElement("path", { d: "M20 2H4c-1.103 0-2 .894-2 1.992v12.016C2 17.106 2.897 18 4 18h3v4l6.351-4H20c1.103 0 2-.894 2-1.992V3.992A1.998 1.998 0 0 0 20 2zm-3.293 11.293-1.414 1.414L12 11.414l-3.293 3.293-1.414-1.414L10.586 10 7.293 6.707l1.414-1.414L12 8.586l3.293-3.293 1.414 1.414L13.414 10l3.293 3.293z" })));
|
|
11
11
|
}
|
|
12
12
|
export function GrSend(props) {
|
|
13
|
-
return React.createElement("span", { className: 'rpc-icon-container',
|
|
13
|
+
return React.createElement("span", Object.assign({ className: 'rpc-icon-container' }, props),
|
|
14
14
|
React.createElement("svg", { fill: "currentColor", width: '1.5rem', height: '1.5rem', className: 'rpc-scale-down rpc-invert' },
|
|
15
15
|
React.createElement("path", { fill: "none", stroke: "#000", strokeWidth: 2, d: "M22,3 L2,11 L20.5,19 L22,3 Z M10,20.5 L13,16 M15.5,9.5 L9,14 L9.85884537,20.0119176 C9.93680292,20.5576204 10.0751625,20.5490248 10.1651297,20.009222 L11,15 L15.5,9.5 Z" })));
|
|
16
16
|
}
|
|
17
17
|
export function BsFillMicFill(props) {
|
|
18
|
-
return React.createElement("span", { className: 'rpc-icon-container',
|
|
18
|
+
return React.createElement("span", Object.assign({ className: 'rpc-icon-container' }, props),
|
|
19
19
|
React.createElement("svg", { fill: "currentColor", width: '1rem', height: '1rem' },
|
|
20
20
|
React.createElement("path", { d: "M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0V3z" }),
|
|
21
21
|
React.createElement("path", { d: "M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5z" })));
|
|
22
22
|
}
|
|
23
23
|
export function BsFillMicMuteFill(props) {
|
|
24
|
-
return React.createElement("span", { className: 'rpc-icon-container',
|
|
24
|
+
return React.createElement("span", Object.assign({ className: 'rpc-icon-container' }, props),
|
|
25
25
|
React.createElement("svg", { fill: "currentColor", width: '1rem', height: '1rem' },
|
|
26
26
|
React.createElement("path", { d: "M13 8c0 .564-.094 1.107-.266 1.613l-.814-.814A4.02 4.02 0 0 0 12 8V7a.5.5 0 0 1 1 0v1zm-5 4c.818 0 1.578-.245 2.212-.667l.718.719a4.973 4.973 0 0 1-2.43.923V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 1 0v1a4 4 0 0 0 4 4zm3-9v4.879L5.158 2.037A3.001 3.001 0 0 1 11 3z" }),
|
|
27
27
|
React.createElement("path", { d: "M9.486 10.607 5 6.12V8a3 3 0 0 0 4.486 2.607zm-7.84-9.253 12 12 .708-.708-12-12-.708.708z" })));
|
package/build/index.d.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import React, { CSSProperties, DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
import { PeerOptions as ImportedPeerOptions } from 'peerjs';
|
|
3
|
+
export type RemotePeerId = string | string[];
|
|
3
4
|
export type PeerOptions = ImportedPeerOptions;
|
|
4
5
|
export type DialogPosition = 'left' | 'center' | 'right';
|
|
5
6
|
export interface DialogOptions {
|
|
6
7
|
position?: DialogPosition;
|
|
7
8
|
style?: CSSProperties;
|
|
8
9
|
}
|
|
10
|
+
type RemotePeers = {
|
|
11
|
+
[id: string]: string;
|
|
12
|
+
};
|
|
9
13
|
export interface Message {
|
|
10
14
|
id: string;
|
|
11
15
|
text: string;
|
|
12
16
|
}
|
|
13
17
|
export interface ChildrenOptions {
|
|
14
|
-
|
|
18
|
+
remotePeers?: RemotePeers;
|
|
15
19
|
messages?: Message[];
|
|
16
20
|
addMessage?: (message: Message, sendToRemotePeer?: boolean) => void;
|
|
17
21
|
audio?: boolean;
|
|
@@ -22,14 +26,16 @@ export type Props = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivEle
|
|
|
22
26
|
export interface ChatProps {
|
|
23
27
|
name?: string;
|
|
24
28
|
peerId: string;
|
|
25
|
-
remotePeerId?:
|
|
29
|
+
remotePeerId?: RemotePeerId;
|
|
26
30
|
text?: boolean;
|
|
27
31
|
voice?: boolean;
|
|
28
32
|
peerOptions?: PeerOptions;
|
|
29
33
|
dialogOptions?: DialogOptions;
|
|
30
34
|
onError?: Function;
|
|
35
|
+
onMicError?: Function;
|
|
31
36
|
children?: Children;
|
|
32
37
|
props?: Props;
|
|
33
38
|
}
|
|
34
|
-
export
|
|
39
|
+
export type { IconProps } from './icons.js';
|
|
40
|
+
export default function Chat({ name, peerId, remotePeerId, peerOptions, text, voice, dialogOptions, onError, onMicError, children, props }: ChatProps): React.JSX.Element;
|
|
35
41
|
export declare const clearChat: () => void;
|
package/build/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import useStorage, { removeStorage } from './storage.js';
|
|
3
3
|
import { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './icons.js';
|
|
4
|
-
|
|
4
|
+
function closeConnection(conn) {
|
|
5
|
+
conn.removeAllListeners();
|
|
6
|
+
conn.close();
|
|
7
|
+
}
|
|
8
|
+
export default function Chat({ name, peerId, remotePeerId = [], peerOptions, text = true, voice = true, dialogOptions, onError = () => alert('Browser not supported! Try some other browser.'), onMicError = () => alert('Microphone not accessible!'), children, props = {} }) {
|
|
5
9
|
const [peer, setPeer] = useState();
|
|
6
10
|
const [notification, setNotification] = useState(false);
|
|
7
|
-
const [
|
|
11
|
+
const [remotePeers, setRemotePeers] = useStorage('rpc-remote-peer', {}, { save: true });
|
|
8
12
|
const [messages, setMessages] = useStorage('rpc-messages', [], { save: true });
|
|
9
|
-
const connRef = useRef();
|
|
13
|
+
const connRef = useRef({});
|
|
10
14
|
const [dialog, setDialog] = useState(false);
|
|
11
15
|
const dialogRef = useRef(null);
|
|
12
16
|
const containerRef = useRef(null);
|
|
@@ -14,54 +18,57 @@ export default function Chat({ name, peerId, remotePeerId, peerOptions, text = t
|
|
|
14
18
|
const [audio, setAudio] = useStorage('rpc-audio', false, { local: true, save: true });
|
|
15
19
|
const streamRef = useRef(null);
|
|
16
20
|
const localStream = useRef();
|
|
21
|
+
peerId = `rpc-${peerId}`;
|
|
22
|
+
if (typeof remotePeerId === 'string')
|
|
23
|
+
remotePeerId = [remotePeerId];
|
|
24
|
+
const remotePeerIds = remotePeerId.map(id => `rpc-${id}`);
|
|
17
25
|
const handleRemoteStream = (remoteStream) => streamRef.current.srcObject = remoteStream;
|
|
18
26
|
function addMessage(message, sendToRemotePeer = false) {
|
|
19
|
-
var _a
|
|
20
|
-
setMessages(
|
|
27
|
+
var _a;
|
|
28
|
+
setMessages(prev => prev.concat(message));
|
|
21
29
|
if (sendToRemotePeer)
|
|
22
|
-
(
|
|
23
|
-
else if (!((
|
|
30
|
+
Object.values(connRef.current).forEach(conn => conn.send({ type: 'message', message }));
|
|
31
|
+
else if (!((_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.open))
|
|
24
32
|
setNotification(true);
|
|
25
33
|
}
|
|
26
34
|
function handleConnection(conn) {
|
|
27
|
-
connRef.current = conn;
|
|
35
|
+
connRef.current[conn.peer] = conn;
|
|
28
36
|
conn.on('open', () => {
|
|
29
|
-
conn.on('data', ({ type, message, remotePeerName
|
|
37
|
+
conn.on('data', ({ type, message, remotePeerName }) => {
|
|
30
38
|
if (type === 'message')
|
|
31
39
|
addMessage(message);
|
|
32
|
-
else if (type === 'init')
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return messages;
|
|
37
|
-
return old;
|
|
40
|
+
else if (type === 'init')
|
|
41
|
+
setRemotePeers(prev => {
|
|
42
|
+
prev[conn.peer] = remotePeerName || 'Anonymous User';
|
|
43
|
+
return prev;
|
|
38
44
|
});
|
|
39
|
-
}
|
|
40
45
|
});
|
|
41
|
-
conn.send({ type: 'init', remotePeerName: name
|
|
46
|
+
conn.send({ type: 'init', remotePeerName: name });
|
|
42
47
|
});
|
|
48
|
+
conn.on('close', conn.removeAllListeners);
|
|
43
49
|
}
|
|
44
50
|
useEffect(() => {
|
|
45
51
|
if (!text && !audio) {
|
|
46
52
|
setPeer(undefined);
|
|
47
53
|
return;
|
|
48
54
|
}
|
|
49
|
-
(async function
|
|
50
|
-
const { Peer } = await import('peerjs');
|
|
51
|
-
|
|
55
|
+
(async function () {
|
|
56
|
+
const { Peer, util: { supports: { audioVideo, data } } } = await import('peerjs');
|
|
57
|
+
if (!data || !audioVideo)
|
|
58
|
+
return onError();
|
|
59
|
+
const peer = new Peer(peerId, peerOptions);
|
|
52
60
|
setPeer(peer);
|
|
53
61
|
})();
|
|
54
62
|
}, [audio]);
|
|
55
63
|
useEffect(() => {
|
|
56
64
|
if (!peer)
|
|
57
65
|
return;
|
|
58
|
-
let
|
|
66
|
+
let calls = {};
|
|
59
67
|
peer.on('open', () => {
|
|
60
|
-
|
|
61
|
-
remotePeerId = `rpc-${remotePeerId}`;
|
|
68
|
+
remotePeerIds.forEach(id => {
|
|
62
69
|
if (text)
|
|
63
|
-
handleConnection(peer.connect(
|
|
64
|
-
}
|
|
70
|
+
handleConnection(peer.connect(id));
|
|
71
|
+
});
|
|
65
72
|
if (audio) {
|
|
66
73
|
const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
|
67
74
|
try {
|
|
@@ -74,32 +81,32 @@ export default function Chat({ name, peerId, remotePeerId, peerOptions, text = t
|
|
|
74
81
|
}
|
|
75
82
|
}, (stream) => {
|
|
76
83
|
localStream.current = stream;
|
|
77
|
-
|
|
78
|
-
call = peer.call(
|
|
84
|
+
remotePeerIds.forEach(id => {
|
|
85
|
+
const call = peer.call(id, stream);
|
|
79
86
|
call.on('stream', handleRemoteStream);
|
|
80
87
|
call.on('close', call.removeAllListeners);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
calls[id] = call;
|
|
89
|
+
});
|
|
90
|
+
peer.on('call', call => {
|
|
84
91
|
call.answer(stream);
|
|
85
92
|
call.on('stream', handleRemoteStream);
|
|
86
93
|
call.on('close', call.removeAllListeners);
|
|
94
|
+
calls[call.peer] = call;
|
|
87
95
|
});
|
|
88
|
-
},
|
|
96
|
+
}, onMicError);
|
|
89
97
|
}
|
|
90
98
|
catch (_a) {
|
|
91
|
-
|
|
99
|
+
onMicError();
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
});
|
|
95
103
|
peer.on('connection', handleConnection);
|
|
96
104
|
return () => {
|
|
97
|
-
var _a
|
|
105
|
+
var _a;
|
|
98
106
|
(_a = localStream.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach(track => track.stop());
|
|
99
|
-
(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
call === null || call === void 0 ? void 0 : call.close();
|
|
107
|
+
Object.values(connRef.current).forEach(closeConnection);
|
|
108
|
+
connRef.current = {};
|
|
109
|
+
Object.values(calls).forEach(closeConnection);
|
|
103
110
|
peer.removeAllListeners();
|
|
104
111
|
peer.destroy();
|
|
105
112
|
};
|
|
@@ -115,9 +122,9 @@ export default function Chat({ name, peerId, remotePeerId, peerOptions, text = t
|
|
|
115
122
|
const container = containerRef.current;
|
|
116
123
|
if (container)
|
|
117
124
|
container.scrollTop = container.scrollHeight;
|
|
118
|
-
}, [dialog,
|
|
119
|
-
return React.createElement("div", { className: 'rpc-main rpc-font',
|
|
120
|
-
typeof children === 'function' ? children({
|
|
125
|
+
}, [dialog, remotePeers, messages]);
|
|
126
|
+
return React.createElement("div", Object.assign({ className: 'rpc-main rpc-font' }, props),
|
|
127
|
+
typeof children === 'function' ? children({ remotePeers, messages, addMessage, audio, setAudio }) : React.createElement(React.Fragment, null,
|
|
121
128
|
text && React.createElement("div", { className: 'rpc-dialog-container' },
|
|
122
129
|
dialog ? React.createElement(BiSolidMessageX, { title: 'Close chat', onClick: () => setDialog(false) }) : React.createElement("div", { className: 'rpc-notification' },
|
|
123
130
|
React.createElement(BiSolidMessageDetail, { title: 'Open chat', onClick: () => {
|
|
@@ -131,7 +138,7 @@ export default function Chat({ name, peerId, remotePeerId, peerOptions, text = t
|
|
|
131
138
|
React.createElement("div", null,
|
|
132
139
|
React.createElement("div", { ref: containerRef, className: 'rpc-message-container' }, messages.map(({ id, text }, i) => React.createElement("div", { key: i },
|
|
133
140
|
React.createElement("strong", null,
|
|
134
|
-
id === peerId ? 'You' :
|
|
141
|
+
id === peerId ? 'You' : remotePeers[id],
|
|
135
142
|
": "),
|
|
136
143
|
React.createElement("span", null, text)))),
|
|
137
144
|
React.createElement("hr", { className: 'rpc-hr' }),
|
|
@@ -147,7 +154,7 @@ export default function Chat({ name, peerId, remotePeerId, peerOptions, text = t
|
|
|
147
154
|
React.createElement("input", { ref: inputRef, className: 'rpc-input rpc-font', placeholder: 'Enter a message' }),
|
|
148
155
|
React.createElement("button", { type: 'submit', className: 'rpc-button' },
|
|
149
156
|
React.createElement(GrSend, { title: 'Send message' })))))),
|
|
150
|
-
voice && React.createElement("div", null, audio ? React.createElement(BsFillMicFill, { title:
|
|
157
|
+
voice && React.createElement("div", null, audio ? React.createElement(BsFillMicFill, { title: 'Turn mic off', onClick: () => setAudio(false) }) : React.createElement(BsFillMicMuteFill, { title: 'Turn mic on', onClick: () => setAudio(true) }))),
|
|
151
158
|
voice && audio && React.createElement("audio", { ref: streamRef, autoPlay: true, style: { display: 'none' } }));
|
|
152
159
|
}
|
|
153
160
|
export const clearChat = () => {
|