react-peer-chat 0.8.4 → 0.8.5
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 +62 -35
- package/dist/chunks/{chunk-4CBQRGQ3.js → chunk-NXKKI6WY.js} +4 -21
- package/dist/icons.d.ts +6 -6
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +4 -13
- package/dist/index.js +120 -118
- package/dist/types.d.ts +29 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,8 +10,8 @@ A simple-to-use React component for implementing peer-to-peer chatting, powered
|
|
|
10
10
|
- Recovers old chats upon reconnection
|
|
11
11
|
- Option to clear chat on command
|
|
12
12
|
- Supports audio/voice chat
|
|
13
|
-
- Multiple peer connections. See [multi-peer usage](#
|
|
14
|
-
- Fully customizable. See [usage with FaC](#
|
|
13
|
+
- Multiple peer connections. See [multi-peer usage](#multi-peer-usage)
|
|
14
|
+
- Fully customizable. See [usage with FaC](#full-customization)
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -79,6 +79,8 @@ export default function App() {
|
|
|
79
79
|
}
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
+
> **Note:** The `remotePeerId` prop is read at mount and whenever `peerId` changes. Changes to `remotePeerId` alone (without `peerId` changing) won't establish new connections. In peer-to-peer chat scenarios, new peers should connect to existing peers by providing their IDs at mount time, rather than existing peers updating this prop dynamically.
|
|
83
|
+
|
|
82
84
|
#### Partial Customization
|
|
83
85
|
|
|
84
86
|
Use the props provided by the `<Chat>` component for customization.
|
|
@@ -289,36 +291,51 @@ export default function App() {
|
|
|
289
291
|
### useChat Hook
|
|
290
292
|
|
|
291
293
|
Here is the full API for the `useChat` hook, these options can be passed as paramerters to the hook:
|
|
292
|
-
|
|
293
|
-
|
|
|
294
|
-
|
|
|
295
|
-
| `
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
298
|
-
| `
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `
|
|
294
|
+
|
|
295
|
+
| Parameter | Type | Required | Default | Description |
|
|
296
|
+
| ------------------- | --------------------------------------------- | -------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
297
|
+
| `name` | `string` | No | Anonymous User | Name of the peer which will be shown to the remote peer. |
|
|
298
|
+
| `peerId` | `string` | Yes | - | It is the unique id that is alloted to a peer. It uniquely identifies a peer from other peers. |
|
|
299
|
+
| `remotePeerId` | `string \| string[]` | No | - | Unique id(s) of remote peer(s) to connect to. Read at mount and when `peerId` changes; changes to this prop alone won't create new connections. |
|
|
300
|
+
| `text` | `boolean` | No | `true` | Text chat will be enabled if this property is set to true. |
|
|
301
|
+
| `recoverChat` | `boolean` | No | `false` | Old chats will be recovered upon reconnecting with the same peer(s). |
|
|
302
|
+
| `audio` | `boolean` | No | `true` | Voice chat will be enabled if this property is set to true. |
|
|
303
|
+
| `peerOptions` | [`PeerOptions`](#peeroptions) | No | - | Options to customize peerjs Peer instance. |
|
|
304
|
+
| `onError` | [`ErrorHandler`](#errorhandler) | No | `() => alert('Browser not supported! Try some other browser.')` | Function to be executed if browser doesn't support `WebRTC` |
|
|
305
|
+
| `onMicError` | [`ErrorHandler`](#errorhandler) | No | `() => alert('Microphone not accessible!')` | Function to be executed when microphone is not accessible. |
|
|
306
|
+
| `onMessageSent` | [`MessageEventHandler`](#messageeventhandler) | No | - | Function to be executed when a text message is sent to other peers. |
|
|
307
|
+
| `onMessageReceived` | [`MessageEventHandler`](#messageeventhandler) | No | - | Function to be executed when a text message is received from other peers. |
|
|
305
308
|
|
|
306
309
|
### Chat Component
|
|
307
310
|
|
|
308
311
|
Here is the full API for the `<Chat>` component, these properties can be set on an instance of `<Chat>`. It contains all the parameters
|
|
309
312
|
that are listed in [useChat Hook API Reference](#usechat-hook-1) along with the following parameters:
|
|
310
|
-
|
|
311
|
-
|
|
|
312
|
-
|
|
|
313
|
-
| `
|
|
314
|
-
| `
|
|
313
|
+
|
|
314
|
+
| Parameter | Type | Required | Default | Description |
|
|
315
|
+
| --------------- | --------------------------------- | -------- | ---------------------- | --------------------------------------------- |
|
|
316
|
+
| `dialogOptions` | [`DialogOptions`](#dialogoptions) | No | { position: 'center' } | Options to customize text dialog box styling. |
|
|
317
|
+
| `props` | [`DivProps`](#divprops) | No | - | Props to customize the `<Chat>` component. |
|
|
318
|
+
| `children` | [`Children`](#children) | No | - | See [usage with FaC](#full-customization) |
|
|
315
319
|
|
|
316
320
|
### Types
|
|
317
321
|
|
|
318
|
-
####
|
|
322
|
+
#### Children
|
|
319
323
|
|
|
320
324
|
```typescript
|
|
321
|
-
import {
|
|
325
|
+
import { ReactNode } from "react";
|
|
326
|
+
type RemotePeers = { [id: string]: string };
|
|
327
|
+
type Message = {
|
|
328
|
+
id: string;
|
|
329
|
+
text: string;
|
|
330
|
+
};
|
|
331
|
+
type ChildrenOptions = {
|
|
332
|
+
remotePeers?: RemotePeers;
|
|
333
|
+
messages?: Message[];
|
|
334
|
+
sendMessage?: (message: Message) => void;
|
|
335
|
+
audio?: boolean;
|
|
336
|
+
setAudio?: (audio: boolean) => void;
|
|
337
|
+
};
|
|
338
|
+
type Children = (childrenOptions: ChildrenOptions) => ReactNode;
|
|
322
339
|
```
|
|
323
340
|
|
|
324
341
|
#### DialogOptions
|
|
@@ -332,25 +349,35 @@ type DialogOptions = {
|
|
|
332
349
|
};
|
|
333
350
|
```
|
|
334
351
|
|
|
335
|
-
####
|
|
352
|
+
#### DivProps
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
import { DetailedHTMLProps, HTMLAttributes } from "react";
|
|
356
|
+
type DivProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
#### ErrorHandler
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
type ErrorHandler = () => void;
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### MessageEventHandler
|
|
336
366
|
|
|
337
367
|
```typescript
|
|
338
|
-
import { ReactNode } from "react";
|
|
339
|
-
type RemotePeers = { [id: string]: string };
|
|
340
368
|
type Message = {
|
|
341
369
|
id: string;
|
|
342
370
|
text: string;
|
|
343
371
|
};
|
|
344
|
-
type
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
};
|
|
351
|
-
type Children = (childrenOptions: ChildrenOptions) => ReactNode;
|
|
372
|
+
type MessageEventHandler = (message: Message) => void;
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### PeerOptions
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { PeerOptions } from "peerjs";
|
|
352
379
|
```
|
|
353
380
|
|
|
354
|
-
##
|
|
381
|
+
## License
|
|
355
382
|
|
|
356
|
-
[
|
|
383
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defProps = Object.defineProperties;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
6
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -16,6 +18,7 @@ var __spreadValues = (a, b) => {
|
|
|
16
18
|
}
|
|
17
19
|
return a;
|
|
18
20
|
};
|
|
21
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
19
22
|
var __objRest = (source, exclude) => {
|
|
20
23
|
var target = {};
|
|
21
24
|
for (var prop in source)
|
|
@@ -28,26 +31,6 @@ var __objRest = (source, exclude) => {
|
|
|
28
31
|
}
|
|
29
32
|
return target;
|
|
30
33
|
};
|
|
31
|
-
var __async = (__this, __arguments, generator) => {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
var fulfilled = (value) => {
|
|
34
|
-
try {
|
|
35
|
-
step(generator.next(value));
|
|
36
|
-
} catch (e) {
|
|
37
|
-
reject(e);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
var rejected = (value) => {
|
|
41
|
-
try {
|
|
42
|
-
step(generator.throw(value));
|
|
43
|
-
} catch (e) {
|
|
44
|
-
reject(e);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
48
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
34
|
function BiSolidMessageDetail(props) {
|
|
52
35
|
return /* @__PURE__ */ React.createElement("span", __spreadValues({ className: "rpc-icon-container" }, props), /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 24 24", width: "1.25rem", height: "1.25rem" }, /* @__PURE__ */ 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" })));
|
|
53
36
|
}
|
|
@@ -72,4 +55,4 @@ function BsFillMicMuteFill(props) {
|
|
|
72
55
|
return /* @__PURE__ */ React.createElement("span", __spreadValues({ className: "rpc-icon-container" }, props), /* @__PURE__ */ React.createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "1.25rem", height: "1.25rem" }, /* @__PURE__ */ 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" }), /* @__PURE__ */ 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" })));
|
|
73
56
|
}
|
|
74
57
|
|
|
75
|
-
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend,
|
|
58
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend, __objRest, __spreadProps, __spreadValues };
|
package/dist/icons.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { IconProps } from './types.js';
|
|
3
3
|
import 'peerjs';
|
|
4
4
|
|
|
5
|
-
declare function BiSolidMessageDetail(props: IconProps):
|
|
6
|
-
declare function BiSolidMessageX(props: IconProps):
|
|
7
|
-
declare function GrSend(props: IconProps):
|
|
8
|
-
declare function BsFillMicFill(props: IconProps):
|
|
9
|
-
declare function BsFillMicMuteFill(props: IconProps):
|
|
5
|
+
declare function BiSolidMessageDetail(props: IconProps): React.JSX.Element;
|
|
6
|
+
declare function BiSolidMessageX(props: IconProps): React.JSX.Element;
|
|
7
|
+
declare function GrSend(props: IconProps): React.JSX.Element;
|
|
8
|
+
declare function BsFillMicFill(props: IconProps): React.JSX.Element;
|
|
9
|
+
declare function BsFillMicMuteFill(props: IconProps): React.JSX.Element;
|
|
10
10
|
|
|
11
11
|
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend };
|
package/dist/icons.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-
|
|
1
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-NXKKI6WY.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import { useChatProps, RemotePeers, Message, ChatProps } from './types.js';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { UseChatProps, UseChatReturn, ChatProps } from './types.js';
|
|
4
3
|
import 'peerjs';
|
|
5
4
|
|
|
6
|
-
declare function useChat({
|
|
7
|
-
peerId: string;
|
|
8
|
-
audioStreamRef: React.RefObject<HTMLMediaElement | null>;
|
|
9
|
-
remotePeers: RemotePeers;
|
|
10
|
-
messages: Message[];
|
|
11
|
-
sendMessage: (message: Message) => void;
|
|
12
|
-
audio: boolean;
|
|
13
|
-
setAudio: (value: boolean | ((old: boolean) => boolean)) => void;
|
|
14
|
-
};
|
|
5
|
+
declare function useChat({ peerId, name, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived, }: UseChatProps): UseChatReturn;
|
|
15
6
|
|
|
16
7
|
declare const clearChat: () => void;
|
|
17
8
|
|
|
18
|
-
declare function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps):
|
|
9
|
+
declare function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps): React.JSX.Element;
|
|
19
10
|
|
|
20
11
|
export { clearChat, Chat as default, useChat };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { __spreadValues,
|
|
2
|
-
import React, { useRef,
|
|
1
|
+
import { __spreadValues, __objRest, BiSolidMessageX, BiSolidMessageDetail, GrSend, BsFillMicFill, BsFillMicMuteFill, __spreadProps } from './chunks/chunk-NXKKI6WY.js';
|
|
2
|
+
import React, { useState, useRef, useMemo, useEffect } from 'react';
|
|
3
3
|
|
|
4
4
|
// src/constants.ts
|
|
5
5
|
var turnAccounts = [
|
|
@@ -10,21 +10,25 @@ var turnAccounts = [
|
|
|
10
10
|
{ username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
|
|
11
11
|
{ username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" }
|
|
12
12
|
];
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
var defaults = {
|
|
14
|
+
config: {
|
|
15
|
+
iceServers: [
|
|
16
|
+
{
|
|
17
|
+
urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"]
|
|
18
|
+
}
|
|
19
|
+
].concat(
|
|
20
|
+
turnAccounts.map((account) => __spreadValues({
|
|
21
|
+
urls: [
|
|
22
|
+
"turn:standard.relay.metered.ca:80",
|
|
23
|
+
"turn:standard.relay.metered.ca:80?transport=tcp",
|
|
24
|
+
"turn:standard.relay.metered.ca:443",
|
|
25
|
+
"turns:standard.relay.metered.ca:443?transport=tcp"
|
|
26
|
+
]
|
|
27
|
+
}, account))
|
|
28
|
+
)
|
|
29
|
+
},
|
|
30
|
+
peerOptions: {},
|
|
31
|
+
remotePeerId: []
|
|
28
32
|
};
|
|
29
33
|
|
|
30
34
|
// src/lib/connection.ts
|
|
@@ -34,38 +38,41 @@ function closeConnection(conn) {
|
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
// src/lib/storage.ts
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
var getStorage = (key, fallbackValue, local = false) => {
|
|
40
|
-
let value = (local ? localStorage : sessionStorage).getItem(key);
|
|
41
|
-
try {
|
|
42
|
-
if (!value) throw new Error("Value doesn't exist");
|
|
43
|
-
value = JSON.parse(value);
|
|
44
|
-
} catch (e) {
|
|
45
|
-
if (fallbackValue !== void 0) {
|
|
46
|
-
value = fallbackValue;
|
|
47
|
-
setStorage(key, value, local);
|
|
48
|
-
} else {
|
|
49
|
-
value = null;
|
|
50
|
-
removeStorage(key, local);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
};
|
|
41
|
+
var getStorageInstance = (local = true) => local ? localStorage : sessionStorage;
|
|
42
|
+
var removeStorage = (key, local = true) => getStorageInstance(local).removeItem(key);
|
|
55
43
|
var clearChat = () => {
|
|
56
44
|
removeStorage("rpc-remote-peer");
|
|
57
45
|
removeStorage("rpc-messages");
|
|
58
46
|
};
|
|
47
|
+
var setStorage = (key, value, local = true) => getStorageInstance(local).setItem(key, JSON.stringify(value));
|
|
48
|
+
function getStorage(key, fallbackValue, local = true) {
|
|
49
|
+
const value = getStorageInstance(local).getItem(key);
|
|
50
|
+
if (value) {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(value);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
removeStorage(key, local);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (fallbackValue !== void 0) setStorage(key, fallbackValue, local);
|
|
58
|
+
return fallbackValue;
|
|
59
|
+
}
|
|
59
60
|
|
|
60
61
|
// src/lib/utils.ts
|
|
61
62
|
var addPrefix = (str) => `rpc-${str}`;
|
|
62
63
|
|
|
64
|
+
// src/lib/react.ts
|
|
65
|
+
function isSetStateFunction(v) {
|
|
66
|
+
return typeof v === "function";
|
|
67
|
+
}
|
|
68
|
+
|
|
63
69
|
// src/hooks.ts
|
|
70
|
+
var { config: defaultConfig, peerOptions: defaultPeerOptions, remotePeerId: defaultRemotePeerId } = defaults;
|
|
64
71
|
function useChat({
|
|
65
|
-
name,
|
|
66
72
|
peerId,
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
name = "Anonymous User",
|
|
74
|
+
remotePeerId = defaultRemotePeerId,
|
|
75
|
+
peerOptions = defaultPeerOptions,
|
|
69
76
|
text = true,
|
|
70
77
|
recoverChat = false,
|
|
71
78
|
audio: allowed = true,
|
|
@@ -74,26 +81,25 @@ function useChat({
|
|
|
74
81
|
onMessageSent,
|
|
75
82
|
onMessageReceived
|
|
76
83
|
}) {
|
|
84
|
+
const [peer, setPeer] = useState();
|
|
77
85
|
const [audio, setAudio] = useAudio(allowed);
|
|
78
|
-
const audioStreamRef = useRef(null);
|
|
79
86
|
const connRef = useRef({});
|
|
80
|
-
const
|
|
87
|
+
const localStreamRef = useRef(null);
|
|
88
|
+
const audioStreamRef = useRef(null);
|
|
89
|
+
const callsRef = useRef({});
|
|
81
90
|
const [messages, setMessages, addMessage] = useMessages();
|
|
82
|
-
const [peer, setPeer] = useState();
|
|
83
91
|
const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
const { completePeerId, completeRemotePeerIds } = useMemo(() => {
|
|
93
|
+
const remotePeerIds = Array.isArray(remotePeerId) ? remotePeerId : [remotePeerId];
|
|
94
|
+
return { completePeerId: addPrefix(peerId), completeRemotePeerIds: remotePeerIds.map(addPrefix) };
|
|
95
|
+
}, [peerId]);
|
|
87
96
|
function handleConnection(conn) {
|
|
88
97
|
connRef.current[conn.peer] = conn;
|
|
89
98
|
conn.on("open", () => {
|
|
90
99
|
conn.on("data", ({ message, messages: messages2, remotePeerName, type }) => {
|
|
91
100
|
if (type === "message") receiveMessage(message);
|
|
92
101
|
else if (type === "init") {
|
|
93
|
-
setRemotePeers((prev) => {
|
|
94
|
-
prev[conn.peer] = remotePeerName || "Anonymous User";
|
|
95
|
-
return prev;
|
|
96
|
-
});
|
|
102
|
+
setRemotePeers((prev) => __spreadProps(__spreadValues({}, prev), { [conn.peer]: remotePeerName }));
|
|
97
103
|
if (recoverChat) setMessages((old) => messages2.length > old.length ? messages2 : old);
|
|
98
104
|
}
|
|
99
105
|
});
|
|
@@ -118,80 +124,76 @@ function useChat({
|
|
|
118
124
|
onMessageSent == null ? void 0 : onMessageSent(message);
|
|
119
125
|
}
|
|
120
126
|
useEffect(() => {
|
|
121
|
-
if (!text && !audio)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
util: {
|
|
130
|
-
supports: { audioVideo, data }
|
|
131
|
-
}
|
|
132
|
-
} = yield import('peerjs');
|
|
127
|
+
if (!text && !audio) return;
|
|
128
|
+
import('peerjs').then(
|
|
129
|
+
({
|
|
130
|
+
Peer,
|
|
131
|
+
util: {
|
|
132
|
+
supports: { audioVideo, data }
|
|
133
|
+
}
|
|
134
|
+
}) => {
|
|
133
135
|
if (!data || !audioVideo) return onError();
|
|
134
|
-
const peer2 = new Peer(
|
|
136
|
+
const peer2 = new Peer(completePeerId, __spreadValues({ config: defaultConfig }, peerOptions));
|
|
137
|
+
peer2.on("connection", handleConnection);
|
|
135
138
|
setPeer(peer2);
|
|
139
|
+
}
|
|
140
|
+
);
|
|
141
|
+
return () => {
|
|
142
|
+
setPeer((prev) => {
|
|
143
|
+
prev == null ? void 0 : prev.removeAllListeners();
|
|
144
|
+
prev == null ? void 0 : prev.destroy();
|
|
145
|
+
return void 0;
|
|
136
146
|
});
|
|
137
|
-
}
|
|
138
|
-
}, [
|
|
147
|
+
};
|
|
148
|
+
}, [completePeerId]);
|
|
139
149
|
useEffect(() => {
|
|
140
|
-
if (!peer) return;
|
|
141
|
-
|
|
142
|
-
peer.
|
|
143
|
-
|
|
144
|
-
if (text) handleConnection(peer.connect(id));
|
|
145
|
-
});
|
|
146
|
-
if (audio) {
|
|
147
|
-
const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
|
148
|
-
try {
|
|
149
|
-
getUserMedia(
|
|
150
|
-
{
|
|
151
|
-
video: false,
|
|
152
|
-
audio: {
|
|
153
|
-
autoGainControl: false,
|
|
154
|
-
// Disable automatic gain control
|
|
155
|
-
noiseSuppression: true,
|
|
156
|
-
// Enable noise suppression
|
|
157
|
-
echoCancellation: true
|
|
158
|
-
// Enable echo cancellation
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
(stream) => {
|
|
162
|
-
localStream.current = stream;
|
|
163
|
-
remotePeerIds.forEach((id) => {
|
|
164
|
-
const call = peer.call(id, stream);
|
|
165
|
-
call.on("stream", handleRemoteStream);
|
|
166
|
-
call.on("close", call.removeAllListeners);
|
|
167
|
-
calls[id] = call;
|
|
168
|
-
});
|
|
169
|
-
peer.on("call", (call) => {
|
|
170
|
-
call.answer(stream);
|
|
171
|
-
call.on("stream", handleRemoteStream);
|
|
172
|
-
call.on("close", call.removeAllListeners);
|
|
173
|
-
calls[call.peer] = call;
|
|
174
|
-
});
|
|
175
|
-
},
|
|
176
|
-
handleError
|
|
177
|
-
);
|
|
178
|
-
} catch (e) {
|
|
179
|
-
handleError();
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
peer.on("connection", handleConnection);
|
|
150
|
+
if (!text || !peer) return;
|
|
151
|
+
const handleOpen = () => completeRemotePeerIds.forEach((id) => handleConnection(peer.connect(id)));
|
|
152
|
+
if (peer.open) handleOpen();
|
|
153
|
+
else peer.once("open", handleOpen);
|
|
184
154
|
return () => {
|
|
185
|
-
var _a;
|
|
186
|
-
(_a = localStream.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
187
155
|
Object.values(connRef.current).forEach(closeConnection);
|
|
188
156
|
connRef.current = {};
|
|
189
|
-
Object.values(calls).forEach(closeConnection);
|
|
190
|
-
peer.removeAllListeners();
|
|
191
|
-
peer.destroy();
|
|
192
157
|
};
|
|
193
|
-
}, [peer]);
|
|
194
|
-
|
|
158
|
+
}, [text, peer]);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (!audio || !peer) return;
|
|
161
|
+
const setupAudio = () => navigator.mediaDevices.getUserMedia({
|
|
162
|
+
video: false,
|
|
163
|
+
audio: {
|
|
164
|
+
autoGainControl: false,
|
|
165
|
+
// Disable automatic gain control
|
|
166
|
+
noiseSuppression: true,
|
|
167
|
+
// Enable noise suppression
|
|
168
|
+
echoCancellation: true
|
|
169
|
+
// Enable echo cancellation
|
|
170
|
+
}
|
|
171
|
+
}).then((stream) => {
|
|
172
|
+
localStreamRef.current = stream;
|
|
173
|
+
completeRemotePeerIds.forEach((id) => {
|
|
174
|
+
const call = peer.call(id, stream);
|
|
175
|
+
call.on("stream", handleRemoteStream);
|
|
176
|
+
call.on("close", call.removeAllListeners);
|
|
177
|
+
callsRef.current[id] = call;
|
|
178
|
+
});
|
|
179
|
+
peer.on("call", (call) => {
|
|
180
|
+
call.answer(stream);
|
|
181
|
+
call.on("stream", handleRemoteStream);
|
|
182
|
+
call.on("close", call.removeAllListeners);
|
|
183
|
+
callsRef.current[call.peer] = call;
|
|
184
|
+
});
|
|
185
|
+
}).catch(handleError);
|
|
186
|
+
if (peer.open) setupAudio();
|
|
187
|
+
else peer.once("open", setupAudio);
|
|
188
|
+
return () => {
|
|
189
|
+
var _a;
|
|
190
|
+
(_a = localStreamRef.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
191
|
+
localStreamRef.current = null;
|
|
192
|
+
Object.values(callsRef.current).forEach(closeConnection);
|
|
193
|
+
callsRef.current = {};
|
|
194
|
+
};
|
|
195
|
+
}, [audio, peer]);
|
|
196
|
+
return { peerId: completePeerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
|
|
195
197
|
}
|
|
196
198
|
function useMessages() {
|
|
197
199
|
const [messages, setMessages] = useStorage("rpc-messages", []);
|
|
@@ -204,10 +206,10 @@ function useStorage(key, initialValue, local = false) {
|
|
|
204
206
|
return getStorage(key, initialValue, local);
|
|
205
207
|
});
|
|
206
208
|
const setValue = (value) => {
|
|
207
|
-
setStoredValue((
|
|
208
|
-
const
|
|
209
|
-
setStorage(key,
|
|
210
|
-
return
|
|
209
|
+
setStoredValue((prev) => {
|
|
210
|
+
const next = isSetStateFunction(value) ? value(prev) : value;
|
|
211
|
+
setStorage(key, next, local);
|
|
212
|
+
return next;
|
|
211
213
|
});
|
|
212
214
|
};
|
|
213
215
|
return [storedValue, setValue];
|
package/dist/types.d.ts
CHANGED
|
@@ -1,30 +1,43 @@
|
|
|
1
|
-
import { PeerOptions } from 'peerjs';
|
|
1
|
+
import { PeerOptions, DataConnection, MediaConnection } from 'peerjs';
|
|
2
2
|
export { PeerOptions } from 'peerjs';
|
|
3
|
-
import {
|
|
3
|
+
import { DetailedHTMLProps, HTMLAttributes, RefObject, SetStateAction, CSSProperties, ReactNode } from 'react';
|
|
4
4
|
|
|
5
|
+
type Connection = DataConnection | MediaConnection;
|
|
6
|
+
type ErrorHandler = () => void;
|
|
5
7
|
type Message = {
|
|
6
8
|
id: string;
|
|
7
9
|
text: string;
|
|
8
10
|
};
|
|
9
|
-
type
|
|
11
|
+
type MessageEventHandler = (message: Message) => void;
|
|
10
12
|
|
|
11
13
|
type RemotePeerId = string | string[];
|
|
12
|
-
type
|
|
13
|
-
name?: string;
|
|
14
|
+
type UseChatProps = {
|
|
14
15
|
peerId: string;
|
|
16
|
+
name?: string;
|
|
15
17
|
remotePeerId?: RemotePeerId;
|
|
16
18
|
text?: boolean;
|
|
17
19
|
recoverChat?: boolean;
|
|
18
20
|
audio?: boolean;
|
|
19
21
|
peerOptions?: PeerOptions;
|
|
20
|
-
onError?:
|
|
21
|
-
onMicError?:
|
|
22
|
-
onMessageSent?:
|
|
23
|
-
onMessageReceived?:
|
|
22
|
+
onError?: ErrorHandler;
|
|
23
|
+
onMicError?: ErrorHandler;
|
|
24
|
+
onMessageSent?: MessageEventHandler;
|
|
25
|
+
onMessageReceived?: MessageEventHandler;
|
|
24
26
|
};
|
|
25
|
-
type
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
type UseChatReturn = {
|
|
28
|
+
peerId: string;
|
|
29
|
+
audioStreamRef: RefObject<HTMLMediaElement | null>;
|
|
30
|
+
remotePeers: RemotePeers;
|
|
31
|
+
messages: Message[];
|
|
32
|
+
sendMessage: (message: Message) => void;
|
|
33
|
+
audio: boolean;
|
|
34
|
+
setAudio: (value: SetStateAction<boolean>) => void;
|
|
35
|
+
};
|
|
36
|
+
type IconProps = DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
|
|
37
|
+
type ChatProps = UseChatProps & {
|
|
38
|
+
dialogOptions?: DialogOptions;
|
|
39
|
+
props?: DivProps;
|
|
40
|
+
children?: Children;
|
|
28
41
|
};
|
|
29
42
|
type Children = (childrenOptions: ChildrenOptions) => ReactNode;
|
|
30
43
|
type ChildrenOptions = {
|
|
@@ -34,16 +47,14 @@ type ChildrenOptions = {
|
|
|
34
47
|
audio?: boolean;
|
|
35
48
|
setAudio?: (audio: boolean) => void;
|
|
36
49
|
};
|
|
37
|
-
type ChatProps = useChatProps & {
|
|
38
|
-
dialogOptions?: DialogOptions;
|
|
39
|
-
props?: DivProps;
|
|
40
|
-
children?: Children;
|
|
41
|
-
};
|
|
42
50
|
type DialogOptions = {
|
|
43
51
|
position?: DialogPosition;
|
|
44
52
|
style?: CSSProperties;
|
|
45
53
|
};
|
|
46
54
|
type DialogPosition = "left" | "center" | "right";
|
|
47
55
|
type DivProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
56
|
+
type RemotePeers = {
|
|
57
|
+
[id: string]: string;
|
|
58
|
+
};
|
|
48
59
|
|
|
49
|
-
export type { ChatProps, Children, ChildrenOptions, DialogOptions, DialogPosition, DivProps, IconProps, Message,
|
|
60
|
+
export type { ChatProps, Children, ChildrenOptions, Connection, DialogOptions, DialogPosition, DivProps, ErrorHandler, IconProps, Message, MessageEventHandler, RemotePeerId, RemotePeers, UseChatProps, UseChatReturn };
|
package/package.json
CHANGED