react-peer-chat 0.8.0 → 0.8.2
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/LICENSE +2 -2
- package/README.md +10 -8
- package/dist/chunks/chunk-4CBQRGQ3.js +75 -0
- package/dist/icons.d.ts +11 -7
- package/dist/icons.js +1 -28
- package/dist/index.d.ts +20 -6
- package/dist/index.js +293 -75
- package/dist/types.d.ts +19 -15
- package/dist/types.js +1 -1
- package/package.json +7 -5
- package/dist/connection.d.ts +0 -2
- package/dist/connection.js +0 -4
- package/dist/constants.d.ts +0 -5
- package/dist/constants.js +0 -15
- package/dist/hooks.d.ts +0 -13
- package/dist/hooks.js +0 -155
- package/dist/storage.d.ts +0 -4
- package/dist/storage.js +0 -25
- package/dist/styles.css +0 -121
- package/dist/utils.d.ts +0 -1
- package/dist/utils.js +0 -1
package/LICENSE
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) Sahil Aggawal, <aggarwalsahil2004@gmail.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
6
|
|
|
7
7
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
8
|
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -46,7 +46,6 @@ The default export of `react-peer-chat` is the `<Chat>` component, which offers
|
|
|
46
46
|
```jsx
|
|
47
47
|
import React from "react";
|
|
48
48
|
import Chat, { clearChat } from "react-peer-chat";
|
|
49
|
-
import "react-peer-chat/styles.css";
|
|
50
49
|
|
|
51
50
|
export default function App() {
|
|
52
51
|
return (
|
|
@@ -64,7 +63,6 @@ export default function App() {
|
|
|
64
63
|
```jsx
|
|
65
64
|
import React from "react";
|
|
66
65
|
import Chat, { clearChat } from "react-peer-chat";
|
|
67
|
-
import "react-peer-chat/styles.css";
|
|
68
66
|
|
|
69
67
|
export default function App() {
|
|
70
68
|
return (
|
|
@@ -88,7 +86,6 @@ Use the props provided by the `<Chat>` component for customization.
|
|
|
88
86
|
```jsx
|
|
89
87
|
import React from "react";
|
|
90
88
|
import Chat from "react-peer-chat";
|
|
91
|
-
import "react-peer-chat/styles.css";
|
|
92
89
|
|
|
93
90
|
export default function App() {
|
|
94
91
|
return (
|
|
@@ -115,7 +112,6 @@ Use Function as Children (FaC) to fully customize the `<Chat>` component.
|
|
|
115
112
|
```jsx
|
|
116
113
|
import React from 'react'
|
|
117
114
|
import Chat from 'react-peer-chat'
|
|
118
|
-
// import 'react-peer-chat/styles.css' (No need to import CSS when using custom component)
|
|
119
115
|
|
|
120
116
|
export default function App() {
|
|
121
117
|
return <Chat
|
|
@@ -141,7 +137,6 @@ You can also provide custom ICE servers to avoid connectivity issues if the free
|
|
|
141
137
|
```jsx
|
|
142
138
|
import React from "react";
|
|
143
139
|
import Chat from "react-peer-chat";
|
|
144
|
-
import "react-peer-chat/styles.css";
|
|
145
140
|
|
|
146
141
|
export default function App() {
|
|
147
142
|
return (
|
|
@@ -175,7 +170,6 @@ The `useChat` hook is ideal when you want to completely redesign the Chat UI or
|
|
|
175
170
|
import React, { useEffect, useRef, useState } from "react";
|
|
176
171
|
import { clearChat, useChat } from "react-peer-chat";
|
|
177
172
|
import { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from "react-peer-chat/icons";
|
|
178
|
-
import "react-peer-chat/styles.css"; // (No need to import CSS when using custom styles)
|
|
179
173
|
|
|
180
174
|
function Chat({ text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children, ...hookProps }) {
|
|
181
175
|
const { peerId, audioStreamRef, ...childrenOptions } = useChat({
|
|
@@ -261,10 +255,18 @@ function Chat({ text = true, audio = true, onMessageReceived, dialogOptions, pro
|
|
|
261
255
|
</dialog>
|
|
262
256
|
</div>
|
|
263
257
|
)}
|
|
264
|
-
{audio &&
|
|
258
|
+
{audio && (
|
|
259
|
+
<button>
|
|
260
|
+
{audioEnabled ? <BsFillMicFill title="Turn mic off" onClick={() => setAudio(false)} /> : <BsFillMicMuteFill title="Turn mic on" onClick={() => setAudio(true)} />}
|
|
261
|
+
</button>
|
|
262
|
+
)}
|
|
265
263
|
</>
|
|
266
264
|
)}
|
|
267
|
-
{audio &&
|
|
265
|
+
{audio && (
|
|
266
|
+
<button className="rpc-button" onClick={() => setAudio(!audioEnabled)}>
|
|
267
|
+
{audioEnabled ? <BsFillMicFill title="Turn mic off" /> : <BsFillMicMuteFill title="Turn mic on" />}
|
|
268
|
+
</button>
|
|
269
|
+
)}
|
|
268
270
|
</div>
|
|
269
271
|
);
|
|
270
272
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __objRest = (source, exclude) => {
|
|
20
|
+
var target = {};
|
|
21
|
+
for (var prop in source)
|
|
22
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
23
|
+
target[prop] = source[prop];
|
|
24
|
+
if (source != null && __getOwnPropSymbols)
|
|
25
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
26
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
27
|
+
target[prop] = source[prop];
|
|
28
|
+
}
|
|
29
|
+
return target;
|
|
30
|
+
};
|
|
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
|
+
function BiSolidMessageDetail(props) {
|
|
52
|
+
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
|
+
}
|
|
54
|
+
function BiSolidMessageX(props) {
|
|
55
|
+
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-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" })));
|
|
56
|
+
}
|
|
57
|
+
function GrSend(props) {
|
|
58
|
+
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", className: "rpc-invert" }, /* @__PURE__ */ React.createElement(
|
|
59
|
+
"path",
|
|
60
|
+
{
|
|
61
|
+
fill: "none",
|
|
62
|
+
stroke: "#000",
|
|
63
|
+
strokeWidth: 2,
|
|
64
|
+
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"
|
|
65
|
+
}
|
|
66
|
+
)));
|
|
67
|
+
}
|
|
68
|
+
function BsFillMicFill(props) {
|
|
69
|
+
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: "M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0V3z" }), /* @__PURE__ */ 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" })));
|
|
70
|
+
}
|
|
71
|
+
function BsFillMicMuteFill(props) {
|
|
72
|
+
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
|
+
}
|
|
74
|
+
|
|
75
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend, __async, __objRest, __spreadValues };
|
package/dist/icons.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { IconProps } from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import React__default from 'react';
|
|
2
|
+
import { IconProps } from './types.js';
|
|
3
|
+
import 'peerjs';
|
|
4
|
+
|
|
5
|
+
declare function BiSolidMessageDetail(props: IconProps): React__default.JSX.Element;
|
|
6
|
+
declare function BiSolidMessageX(props: IconProps): React__default.JSX.Element;
|
|
7
|
+
declare function GrSend(props: IconProps): React__default.JSX.Element;
|
|
8
|
+
declare function BsFillMicFill(props: IconProps): React__default.JSX.Element;
|
|
9
|
+
declare function BsFillMicMuteFill(props: IconProps): React__default.JSX.Element;
|
|
10
|
+
|
|
11
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend };
|
package/dist/icons.js
CHANGED
|
@@ -1,28 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export function BiSolidMessageDetail(props) {
|
|
3
|
-
return (React.createElement("span", Object.assign({ className: "rpc-icon-container" }, props),
|
|
4
|
-
React.createElement("svg", { viewBox: "0 0 24 24", width: "1.25rem", height: "1.25rem" },
|
|
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
|
-
}
|
|
7
|
-
export function BiSolidMessageX(props) {
|
|
8
|
-
return (React.createElement("span", Object.assign({ className: "rpc-icon-container" }, props),
|
|
9
|
-
React.createElement("svg", { viewBox: "0 0 24 24", width: "1.25rem", height: "1.25rem" },
|
|
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
|
-
}
|
|
12
|
-
export function GrSend(props) {
|
|
13
|
-
return (React.createElement("span", Object.assign({ className: "rpc-icon-container" }, props),
|
|
14
|
-
React.createElement("svg", { viewBox: "0 0 24 24", width: "1.25rem", height: "1.25rem", className: "rpc-invert" },
|
|
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
|
-
}
|
|
17
|
-
export function BsFillMicFill(props) {
|
|
18
|
-
return (React.createElement("span", Object.assign({ className: "rpc-icon-container" }, props),
|
|
19
|
-
React.createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "1.25rem", height: "1.25rem" },
|
|
20
|
-
React.createElement("path", { d: "M5 3a3 3 0 0 1 6 0v5a3 3 0 0 1-6 0V3z" }),
|
|
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
|
-
}
|
|
23
|
-
export function BsFillMicMuteFill(props) {
|
|
24
|
-
return (React.createElement("span", Object.assign({ className: "rpc-icon-container" }, props),
|
|
25
|
-
React.createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", width: "1.25rem", height: "1.25rem" },
|
|
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
|
-
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" }))));
|
|
28
|
-
}
|
|
1
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-4CBQRGQ3.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import React__default from 'react';
|
|
3
|
+
import { useChatProps, RemotePeers, Message, ChatProps } from './types.js';
|
|
4
|
+
import 'peerjs';
|
|
5
|
+
|
|
6
|
+
declare function useChat({ name, peerId, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived, }: useChatProps): {
|
|
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
|
+
};
|
|
15
|
+
|
|
16
|
+
declare const clearChat: () => void;
|
|
17
|
+
|
|
18
|
+
declare function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps): React__default.JSX.Element;
|
|
19
|
+
|
|
20
|
+
export { clearChat, Chat as default, useChat };
|
package/dist/index.js
CHANGED
|
@@ -1,78 +1,296 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { __spreadValues, __async, __objRest, BiSolidMessageX, BiSolidMessageDetail, GrSend, BsFillMicFill, BsFillMicMuteFill } from './chunks/chunk-4CBQRGQ3.js';
|
|
2
|
+
import React, { useRef, useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
// src/connection.tsx
|
|
5
|
+
function closeConnection(conn) {
|
|
6
|
+
conn.removeAllListeners();
|
|
7
|
+
conn.close();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/constants.ts
|
|
11
|
+
var turnAccounts = [
|
|
12
|
+
{ username: "70061a377b51f3a3d01c11e3", credential: "lHV4NYJ5Rfl5JNa9" },
|
|
13
|
+
{ username: "13b19eb65bbf6e9f96d64b72", credential: "7R9P/+7y7Q516Etv" },
|
|
14
|
+
{ username: "3469603f5cdc7ca4a1e891ae", credential: "/jMyLSDbbcgqpVQv" },
|
|
15
|
+
{ username: "a7926f4dcc4a688d41f89752", credential: "ZYM8jFYeb8bQkL+N" },
|
|
16
|
+
{ username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
|
|
17
|
+
{ username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" }
|
|
18
|
+
];
|
|
19
|
+
var defaultConfig = {
|
|
20
|
+
iceServers: [
|
|
21
|
+
{
|
|
22
|
+
urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"]
|
|
23
|
+
}
|
|
24
|
+
].concat(
|
|
25
|
+
turnAccounts.map((account) => __spreadValues({
|
|
26
|
+
urls: [
|
|
27
|
+
"turn:standard.relay.metered.ca:80",
|
|
28
|
+
"turn:standard.relay.metered.ca:80?transport=tcp",
|
|
29
|
+
"turn:standard.relay.metered.ca:443",
|
|
30
|
+
"turns:standard.relay.metered.ca:443?transport=tcp"
|
|
31
|
+
]
|
|
32
|
+
}, account))
|
|
33
|
+
)
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/storage.ts
|
|
37
|
+
var removeStorage = (key, local = false) => (local ? localStorage : sessionStorage).removeItem(key);
|
|
38
|
+
var setStorage = (key, value, local = false) => (local ? localStorage : sessionStorage).setItem(key, JSON.stringify(value));
|
|
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
|
+
};
|
|
55
|
+
var clearChat = () => {
|
|
56
|
+
removeStorage("rpc-remote-peer");
|
|
57
|
+
removeStorage("rpc-messages");
|
|
11
58
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
59
|
+
|
|
60
|
+
// src/utils.ts
|
|
61
|
+
var addPrefix = (str) => `rpc-${str}`;
|
|
62
|
+
|
|
63
|
+
// src/hooks.tsx
|
|
64
|
+
function useChat({
|
|
65
|
+
name,
|
|
66
|
+
peerId,
|
|
67
|
+
remotePeerId = [],
|
|
68
|
+
peerOptions,
|
|
69
|
+
text = true,
|
|
70
|
+
recoverChat = false,
|
|
71
|
+
audio: allowed = true,
|
|
72
|
+
onError = () => alert("Browser not supported! Try some other browser."),
|
|
73
|
+
onMicError = () => alert("Microphone not accessible!"),
|
|
74
|
+
onMessageSent,
|
|
75
|
+
onMessageReceived
|
|
76
|
+
}) {
|
|
77
|
+
const [audio, setAudio] = useAudio(allowed);
|
|
78
|
+
const audioStreamRef = useRef(null);
|
|
79
|
+
const connRef = useRef({});
|
|
80
|
+
const localStream = useRef(null);
|
|
81
|
+
const [messages, setMessages, addMessage] = useMessages();
|
|
82
|
+
const [peer, setPeer] = useState();
|
|
83
|
+
const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
|
|
84
|
+
peerId = addPrefix(peerId);
|
|
85
|
+
if (typeof remotePeerId === "string") remotePeerId = [remotePeerId];
|
|
86
|
+
const remotePeerIds = remotePeerId.map(addPrefix);
|
|
87
|
+
function handleConnection(conn) {
|
|
88
|
+
connRef.current[conn.peer] = conn;
|
|
89
|
+
conn.on("open", () => {
|
|
90
|
+
conn.on("data", ({ message, messages: messages2, remotePeerName, type }) => {
|
|
91
|
+
if (type === "message") receiveMessage(message);
|
|
92
|
+
else if (type === "init") {
|
|
93
|
+
setRemotePeers((prev) => {
|
|
94
|
+
prev[conn.peer] = remotePeerName || "Anonymous User";
|
|
95
|
+
return prev;
|
|
96
|
+
});
|
|
97
|
+
if (recoverChat) setMessages((old) => messages2.length > old.length ? messages2 : old);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
conn.send({ type: "init", remotePeerName: name, messages });
|
|
101
|
+
});
|
|
102
|
+
conn.on("close", conn.removeAllListeners);
|
|
103
|
+
}
|
|
104
|
+
function handleError() {
|
|
105
|
+
setAudio(false);
|
|
106
|
+
onMicError();
|
|
107
|
+
}
|
|
108
|
+
function handleRemoteStream(remoteStream) {
|
|
109
|
+
if (audioStreamRef.current) audioStreamRef.current.srcObject = remoteStream;
|
|
110
|
+
}
|
|
111
|
+
function receiveMessage(message) {
|
|
112
|
+
addMessage(message);
|
|
113
|
+
onMessageReceived == null ? void 0 : onMessageReceived(message);
|
|
114
|
+
}
|
|
115
|
+
function sendMessage(message) {
|
|
116
|
+
addMessage(message);
|
|
117
|
+
Object.values(connRef.current).forEach((conn) => conn.send({ type: "message", message }));
|
|
118
|
+
onMessageSent == null ? void 0 : onMessageSent(message);
|
|
119
|
+
}
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (!text && !audio) {
|
|
122
|
+
setPeer(void 0);
|
|
123
|
+
return;
|
|
31
124
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
125
|
+
(function() {
|
|
126
|
+
return __async(this, null, function* () {
|
|
127
|
+
const {
|
|
128
|
+
Peer,
|
|
129
|
+
util: {
|
|
130
|
+
supports: { audioVideo, data }
|
|
131
|
+
}
|
|
132
|
+
} = yield import('peerjs');
|
|
133
|
+
if (!data || !audioVideo) return onError();
|
|
134
|
+
const peer2 = new Peer(peerId, __spreadValues({ config: defaultConfig }, peerOptions));
|
|
135
|
+
setPeer(peer2);
|
|
136
|
+
});
|
|
137
|
+
})();
|
|
138
|
+
}, [audio]);
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!peer) return;
|
|
141
|
+
let calls = {};
|
|
142
|
+
peer.on("open", () => {
|
|
143
|
+
remotePeerIds.forEach((id) => {
|
|
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);
|
|
184
|
+
return () => {
|
|
185
|
+
var _a;
|
|
186
|
+
(_a = localStream.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
187
|
+
Object.values(connRef.current).forEach(closeConnection);
|
|
188
|
+
connRef.current = {};
|
|
189
|
+
Object.values(calls).forEach(closeConnection);
|
|
190
|
+
peer.removeAllListeners();
|
|
191
|
+
peer.destroy();
|
|
192
|
+
};
|
|
193
|
+
}, [peer]);
|
|
194
|
+
return { peerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
|
|
195
|
+
}
|
|
196
|
+
function useMessages() {
|
|
197
|
+
const [messages, setMessages] = useStorage("rpc-messages", []);
|
|
198
|
+
const addMessage = (message) => setMessages((prev) => prev.concat(message));
|
|
199
|
+
return [messages, setMessages, addMessage];
|
|
200
|
+
}
|
|
201
|
+
function useStorage(key, initialValue, local = false) {
|
|
202
|
+
const [storedValue, setStoredValue] = useState(() => {
|
|
203
|
+
if (typeof window === "undefined") return initialValue;
|
|
204
|
+
return getStorage(key, initialValue, local);
|
|
205
|
+
});
|
|
206
|
+
const setValue = (value) => {
|
|
207
|
+
setStoredValue((old) => {
|
|
208
|
+
const updatedValue = typeof value === "function" ? value(old) : value;
|
|
209
|
+
setStorage(key, updatedValue, local);
|
|
210
|
+
return updatedValue;
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
return [storedValue, setValue];
|
|
214
|
+
}
|
|
215
|
+
function useAudio(allowed) {
|
|
216
|
+
const [audio, setAudio] = useStorage("rpc-audio", false, true);
|
|
217
|
+
const enabled = audio && allowed;
|
|
218
|
+
return [enabled, setAudio];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/styles.css
|
|
222
|
+
function injectStyle(css) {
|
|
223
|
+
if (typeof document === "undefined") return;
|
|
224
|
+
const head = document.head || document.getElementsByTagName("head")[0];
|
|
225
|
+
const style = document.createElement("style");
|
|
226
|
+
style.type = "text/css";
|
|
227
|
+
if (head.firstChild) {
|
|
228
|
+
head.insertBefore(style, head.firstChild);
|
|
229
|
+
} else {
|
|
230
|
+
head.appendChild(style);
|
|
231
|
+
}
|
|
232
|
+
if (style.styleSheet) {
|
|
233
|
+
style.styleSheet.cssText = css;
|
|
234
|
+
} else {
|
|
235
|
+
style.appendChild(document.createTextNode(css));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
injectStyle('.rpc-font {\n font-family:\n "Trebuchet MS",\n "Lucida Sans Unicode",\n "Lucida Grande",\n "Lucida Sans",\n Arial,\n sans-serif;\n}\n.rpc-main {\n display: flex;\n align-items: center;\n column-gap: 0.5rem;\n}\n.rpc-dialog-container {\n position: relative;\n}\n.rpc-notification {\n position: relative;\n}\n.rpc-notification .rpc-badge {\n background-color: red;\n position: absolute;\n top: 0;\n right: 0;\n width: 6px;\n aspect-ratio: 1;\n border-radius: 100%;\n}\n.rpc-dialog {\n position: absolute;\n background-color: black;\n color: white;\n padding: 0.4rem 0 0.25rem 0;\n border-radius: 0.4rem;\n font-size: small;\n}\n.rpc-position-left {\n left: 0.6rem;\n translate: -100%;\n}\n.rpc-position-center {\n left: 0.5rem;\n translate: -50%;\n}\n.rpc-position-right {\n left: 0.3rem;\n}\n.rpc-hr {\n margin: 0.25rem 0;\n border-color: rgba(255, 255, 255, 0.7);\n}\n.rpc-message-container {\n height: 7rem;\n overflow-y: scroll;\n padding-inline: 0.5rem;\n margin-bottom: 0.25rem;\n}\n.rpc-message-container::-webkit-scrollbar {\n width: 2.5px;\n}\n.rpc-message-container::-webkit-scrollbar-track {\n background: gray;\n}\n.rpc-message-container::-webkit-scrollbar-thumb {\n background-color: white;\n}\n.rpc-heading {\n text-align: center;\n font-size: medium;\n font-weight: bold;\n padding-inline: 0.5rem;\n}\n.rpc-input-container {\n display: flex;\n width: 15rem;\n max-width: 90vw;\n column-gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n}\n.rpc-input {\n color: white;\n width: 100%;\n background-color: black;\n border: none;\n outline: 1px solid rgba(255, 255, 255, 0.8);\n border-radius: 0.25rem;\n padding: 0.3rem 0.25rem;\n}\n.rpc-input::placeholder {\n color: rgba(255, 255, 255, 0.7);\n}\n.rpc-input:focus {\n outline: 2px solid white;\n}\n.rpc-button {\n all: unset;\n display: flex;\n}\n.rpc-icon-container {\n display: flex;\n align-items: center;\n}\n.rpc-invert {\n filter: invert(100%);\n}\n');
|
|
239
|
+
|
|
240
|
+
// src/index.tsx
|
|
241
|
+
function Chat(_a) {
|
|
242
|
+
var _b = _a, { text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children } = _b, hookProps = __objRest(_b, ["text", "audio", "onMessageReceived", "dialogOptions", "props", "children"]);
|
|
243
|
+
const _a2 = useChat(__spreadValues({
|
|
244
|
+
text,
|
|
245
|
+
audio,
|
|
246
|
+
onMessageReceived: modifiedOnMessageReceived
|
|
247
|
+
}, hookProps)), { peerId, audioStreamRef } = _a2, childrenOptions = __objRest(_a2, ["peerId", "audioStreamRef"]);
|
|
248
|
+
const { remotePeers, messages, sendMessage, audio: audioEnabled, setAudio } = childrenOptions;
|
|
249
|
+
const containerRef = useRef(null);
|
|
250
|
+
const [dialog, setDialog] = useState(false);
|
|
251
|
+
const dialogRef = useRef(null);
|
|
252
|
+
const inputRef = useRef(null);
|
|
253
|
+
const [notification, setNotification] = useState(false);
|
|
254
|
+
function modifiedOnMessageReceived(message) {
|
|
255
|
+
var _a3;
|
|
256
|
+
if (!((_a3 = dialogRef.current) == null ? void 0 : _a3.open)) setNotification(true);
|
|
257
|
+
onMessageReceived == null ? void 0 : onMessageReceived(message);
|
|
258
|
+
}
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
var _a3, _b2;
|
|
261
|
+
if (dialog) (_a3 = dialogRef.current) == null ? void 0 : _a3.show();
|
|
262
|
+
else (_b2 = dialogRef.current) == null ? void 0 : _b2.close();
|
|
263
|
+
}, [dialog]);
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
const container = containerRef.current;
|
|
266
|
+
if (container) container.scrollTop = container.scrollHeight;
|
|
267
|
+
}, [dialog, remotePeers, messages]);
|
|
268
|
+
return /* @__PURE__ */ React.createElement("div", __spreadValues({ className: "rpc-main rpc-font" }, props), typeof children === "function" ? children(childrenOptions) : /* @__PURE__ */ React.createElement(React.Fragment, null, text && /* @__PURE__ */ React.createElement("div", { className: "rpc-dialog-container" }, dialog ? /* @__PURE__ */ React.createElement(BiSolidMessageX, { title: "Close chat", onClick: () => setDialog(false) }) : /* @__PURE__ */ React.createElement("div", { className: "rpc-notification" }, /* @__PURE__ */ React.createElement(
|
|
269
|
+
BiSolidMessageDetail,
|
|
270
|
+
{
|
|
271
|
+
title: "Open chat",
|
|
272
|
+
onClick: () => {
|
|
273
|
+
setNotification(false);
|
|
274
|
+
setDialog(true);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
), notification && /* @__PURE__ */ React.createElement("span", { className: "rpc-badge" })), /* @__PURE__ */ React.createElement("dialog", { ref: dialogRef, className: `${dialog ? "rpc-dialog" : ""} rpc-position-${(dialogOptions == null ? void 0 : dialogOptions.position) || "center"}`, style: dialogOptions == null ? void 0 : dialogOptions.style }, /* @__PURE__ */ React.createElement("div", { className: "rpc-heading" }, "Chat"), /* @__PURE__ */ React.createElement("hr", { className: "rpc-hr" }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { ref: containerRef, className: "rpc-message-container" }, messages.map(({ id, text: text2 }, i) => /* @__PURE__ */ React.createElement("div", { key: i }, /* @__PURE__ */ React.createElement("strong", null, id === peerId ? "You" : remotePeers[id], ": "), /* @__PURE__ */ React.createElement("span", null, text2)))), /* @__PURE__ */ React.createElement("hr", { className: "rpc-hr" }), /* @__PURE__ */ React.createElement(
|
|
278
|
+
"form",
|
|
279
|
+
{
|
|
280
|
+
className: "rpc-input-container",
|
|
281
|
+
onSubmit: (e) => {
|
|
282
|
+
var _a3;
|
|
283
|
+
e.preventDefault();
|
|
284
|
+
const text2 = (_a3 = inputRef.current) == null ? void 0 : _a3.value;
|
|
285
|
+
if (text2) {
|
|
286
|
+
inputRef.current.value = "";
|
|
287
|
+
sendMessage({ id: peerId, text: text2 });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
/* @__PURE__ */ React.createElement("input", { ref: inputRef, className: "rpc-input rpc-font", placeholder: "Enter a message" }),
|
|
292
|
+
/* @__PURE__ */ React.createElement("button", { type: "submit", className: "rpc-button" }, /* @__PURE__ */ React.createElement(GrSend, { title: "Send message" }))
|
|
293
|
+
)))), audio && /* @__PURE__ */ React.createElement("button", { className: "rpc-button", onClick: () => setAudio(!audioEnabled) }, audioEnabled ? /* @__PURE__ */ React.createElement(BsFillMicFill, { title: "Turn mic off" }) : /* @__PURE__ */ React.createElement(BsFillMicMuteFill, { title: "Turn mic on" }))), audio && audioEnabled && /* @__PURE__ */ React.createElement("audio", { ref: audioStreamRef, autoPlay: true, style: { display: "none" } }));
|
|
77
294
|
}
|
|
78
|
-
|
|
295
|
+
|
|
296
|
+
export { clearChat, Chat as default, useChat };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { PeerOptions } from
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { PeerOptions } from 'peerjs';
|
|
2
|
+
export { PeerOptions } from 'peerjs';
|
|
3
|
+
import { CSSProperties, DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
type Message = {
|
|
4
6
|
id: string;
|
|
5
7
|
text: string;
|
|
6
8
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
type MessageEvent = (message: Message) => void;
|
|
10
|
+
|
|
11
|
+
type RemotePeerId = string | string[];
|
|
12
|
+
type useChatProps = {
|
|
11
13
|
name?: string;
|
|
12
14
|
peerId: string;
|
|
13
15
|
remotePeerId?: RemotePeerId;
|
|
@@ -20,26 +22,28 @@ export type useChatProps = {
|
|
|
20
22
|
onMessageSent?: MessageEvent;
|
|
21
23
|
onMessageReceived?: MessageEvent;
|
|
22
24
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
type IconProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
|
|
26
|
+
type RemotePeers = {
|
|
25
27
|
[id: string]: string;
|
|
26
28
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
type Children = (childrenOptions: ChildrenOptions) => ReactNode;
|
|
30
|
+
type ChildrenOptions = {
|
|
29
31
|
remotePeers?: RemotePeers;
|
|
30
32
|
messages?: Message[];
|
|
31
33
|
sendMessage?: (message: Message) => void;
|
|
32
34
|
audio?: boolean;
|
|
33
35
|
setAudio?: (audio: boolean) => void;
|
|
34
36
|
};
|
|
35
|
-
|
|
37
|
+
type ChatProps = useChatProps & {
|
|
36
38
|
dialogOptions?: DialogOptions;
|
|
37
39
|
props?: DivProps;
|
|
38
40
|
children?: Children;
|
|
39
41
|
};
|
|
40
|
-
|
|
42
|
+
type DialogOptions = {
|
|
41
43
|
position?: DialogPosition;
|
|
42
44
|
style?: CSSProperties;
|
|
43
45
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
type DialogPosition = "left" | "center" | "right";
|
|
47
|
+
type DivProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
48
|
+
|
|
49
|
+
export type { ChatProps, Children, ChildrenOptions, DialogOptions, DialogPosition, DivProps, IconProps, Message, MessageEvent, RemotePeerId, RemotePeers, useChatProps };
|
package/dist/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-peer-chat",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "An easy to use react component for impleting peer-to-peer chatting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sahil Aggarwal <aggarwalsahil2004@gmail.com>",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./dist/index.js",
|
|
19
19
|
"./icons": "./dist/icons.js",
|
|
20
|
-
"./styles.css": "./dist/styles.css",
|
|
21
20
|
"./types": "./dist/types.js"
|
|
22
21
|
},
|
|
23
22
|
"main": "dist/index.js",
|
|
@@ -26,7 +25,9 @@
|
|
|
26
25
|
"peerjs": "^1.5.4"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
|
-
"@types/react": "^19.0.
|
|
28
|
+
"@types/react": "^19.0.10",
|
|
29
|
+
"tsup": "^8.4.0",
|
|
30
|
+
"typescript": "^5.8.2"
|
|
30
31
|
},
|
|
31
32
|
"peerDependencies": {
|
|
32
33
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
@@ -49,7 +50,8 @@
|
|
|
49
50
|
"audio-chat"
|
|
50
51
|
],
|
|
51
52
|
"scripts": {
|
|
52
|
-
"
|
|
53
|
-
"
|
|
53
|
+
"build": "pnpm i && pnpm run compile",
|
|
54
|
+
"compile": "tsup",
|
|
55
|
+
"dev": "tsup --watch"
|
|
54
56
|
}
|
|
55
57
|
}
|
package/dist/connection.d.ts
DELETED
package/dist/connection.js
DELETED
package/dist/constants.d.ts
DELETED
package/dist/constants.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const turnAccounts = [
|
|
2
|
-
{ username: "70061a377b51f3a3d01c11e3", credential: "lHV4NYJ5Rfl5JNa9" },
|
|
3
|
-
{ username: "13b19eb65bbf6e9f96d64b72", credential: "7R9P/+7y7Q516Etv" },
|
|
4
|
-
{ username: "3469603f5cdc7ca4a1e891ae", credential: "/jMyLSDbbcgqpVQv" },
|
|
5
|
-
{ username: "a7926f4dcc4a688d41f89752", credential: "ZYM8jFYeb8bQkL+N" },
|
|
6
|
-
{ username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
|
|
7
|
-
{ username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" },
|
|
8
|
-
];
|
|
9
|
-
export const defaultConfig = {
|
|
10
|
-
iceServers: [
|
|
11
|
-
{
|
|
12
|
-
urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"],
|
|
13
|
-
},
|
|
14
|
-
].concat(turnAccounts.map((account) => (Object.assign({ urls: ["turn:standard.relay.metered.ca:80", "turn:standard.relay.metered.ca:80?transport=tcp", "turn:standard.relay.metered.ca:443", "turns:standard.relay.metered.ca:443?transport=tcp"] }, account)))),
|
|
15
|
-
};
|
package/dist/hooks.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Message, RemotePeers, useChatProps } from "./types.js";
|
|
2
|
-
export declare function useChat({ name, peerId, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived }: useChatProps): {
|
|
3
|
-
peerId: string;
|
|
4
|
-
audioStreamRef: import("react").RefObject<HTMLMediaElement>;
|
|
5
|
-
remotePeers: RemotePeers;
|
|
6
|
-
messages: Message[];
|
|
7
|
-
sendMessage: (message: Message) => void;
|
|
8
|
-
audio: boolean;
|
|
9
|
-
setAudio: (value: boolean | ((old: boolean) => boolean)) => void;
|
|
10
|
-
};
|
|
11
|
-
export declare function useMessages(): readonly [Message[], (value: Message[] | ((old: Message[]) => Message[])) => void, (message: Message) => void];
|
|
12
|
-
export declare function useStorage<Value>(key: string, initialValue: Value, local?: boolean): [Value, (value: Value | ((old: Value) => Value)) => void];
|
|
13
|
-
export declare function useAudio(allowed: boolean): readonly [boolean, (value: boolean | ((old: boolean) => boolean)) => void];
|
package/dist/hooks.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { useEffect, useRef, useState } from "react";
|
|
11
|
-
import { closeConnection } from "./connection.js";
|
|
12
|
-
import { defaultConfig } from "./constants.js";
|
|
13
|
-
import { getStorage, setStorage } from "./storage.js";
|
|
14
|
-
import { addPrefix } from "./utils.js";
|
|
15
|
-
export function useChat({ name, peerId, remotePeerId = [], peerOptions, text = true, recoverChat = false, audio: allowed = true, onError = () => alert("Browser not supported! Try some other browser."), onMicError = () => alert("Microphone not accessible!"), onMessageSent, onMessageReceived }) {
|
|
16
|
-
const [audio, setAudio] = useAudio(allowed);
|
|
17
|
-
const audioStreamRef = useRef(null);
|
|
18
|
-
const connRef = useRef({});
|
|
19
|
-
const localStream = useRef();
|
|
20
|
-
const [messages, setMessages, addMessage] = useMessages();
|
|
21
|
-
const [peer, setPeer] = useState();
|
|
22
|
-
const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
|
|
23
|
-
peerId = addPrefix(peerId);
|
|
24
|
-
if (typeof remotePeerId === "string")
|
|
25
|
-
remotePeerId = [remotePeerId];
|
|
26
|
-
const remotePeerIds = remotePeerId.map(addPrefix);
|
|
27
|
-
function handleConnection(conn) {
|
|
28
|
-
connRef.current[conn.peer] = conn;
|
|
29
|
-
conn.on("open", () => {
|
|
30
|
-
conn.on("data", ({ message, messages, remotePeerName, type }) => {
|
|
31
|
-
if (type === "message")
|
|
32
|
-
receiveMessage(message);
|
|
33
|
-
else if (type === "init") {
|
|
34
|
-
setRemotePeers((prev) => {
|
|
35
|
-
prev[conn.peer] = remotePeerName || "Anonymous User";
|
|
36
|
-
return prev;
|
|
37
|
-
});
|
|
38
|
-
if (recoverChat)
|
|
39
|
-
setMessages((old) => (messages.length > old.length ? messages : old));
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
conn.send({ type: "init", remotePeerName: name, messages });
|
|
43
|
-
});
|
|
44
|
-
conn.on("close", conn.removeAllListeners);
|
|
45
|
-
}
|
|
46
|
-
function handleError() {
|
|
47
|
-
setAudio(false);
|
|
48
|
-
onMicError();
|
|
49
|
-
}
|
|
50
|
-
function handleRemoteStream(remoteStream) {
|
|
51
|
-
if (audioStreamRef.current)
|
|
52
|
-
audioStreamRef.current.srcObject = remoteStream;
|
|
53
|
-
}
|
|
54
|
-
function receiveMessage(message) {
|
|
55
|
-
addMessage(message);
|
|
56
|
-
onMessageReceived === null || onMessageReceived === void 0 ? void 0 : onMessageReceived(message);
|
|
57
|
-
}
|
|
58
|
-
function sendMessage(message) {
|
|
59
|
-
addMessage(message);
|
|
60
|
-
Object.values(connRef.current).forEach((conn) => conn.send({ type: "message", message }));
|
|
61
|
-
onMessageSent === null || onMessageSent === void 0 ? void 0 : onMessageSent(message);
|
|
62
|
-
}
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
if (!text && !audio) {
|
|
65
|
-
setPeer(undefined);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
(function () {
|
|
69
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
-
const { Peer, util: { supports: { audioVideo, data }, }, } = yield import("peerjs");
|
|
71
|
-
if (!data || !audioVideo)
|
|
72
|
-
return onError();
|
|
73
|
-
const peer = new Peer(peerId, Object.assign({ config: defaultConfig }, peerOptions));
|
|
74
|
-
setPeer(peer);
|
|
75
|
-
});
|
|
76
|
-
})();
|
|
77
|
-
}, [audio]);
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
if (!peer)
|
|
80
|
-
return;
|
|
81
|
-
let calls = {};
|
|
82
|
-
peer.on("open", () => {
|
|
83
|
-
remotePeerIds.forEach((id) => {
|
|
84
|
-
if (text)
|
|
85
|
-
handleConnection(peer.connect(id));
|
|
86
|
-
});
|
|
87
|
-
if (audio) {
|
|
88
|
-
const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
|
89
|
-
try {
|
|
90
|
-
getUserMedia({
|
|
91
|
-
video: false,
|
|
92
|
-
audio: {
|
|
93
|
-
autoGainControl: false,
|
|
94
|
-
noiseSuppression: true,
|
|
95
|
-
echoCancellation: true,
|
|
96
|
-
},
|
|
97
|
-
}, (stream) => {
|
|
98
|
-
localStream.current = stream;
|
|
99
|
-
remotePeerIds.forEach((id) => {
|
|
100
|
-
const call = peer.call(id, stream);
|
|
101
|
-
call.on("stream", handleRemoteStream);
|
|
102
|
-
call.on("close", call.removeAllListeners);
|
|
103
|
-
calls[id] = call;
|
|
104
|
-
});
|
|
105
|
-
peer.on("call", (call) => {
|
|
106
|
-
call.answer(stream);
|
|
107
|
-
call.on("stream", handleRemoteStream);
|
|
108
|
-
call.on("close", call.removeAllListeners);
|
|
109
|
-
calls[call.peer] = call;
|
|
110
|
-
});
|
|
111
|
-
}, handleError);
|
|
112
|
-
}
|
|
113
|
-
catch (_a) {
|
|
114
|
-
handleError();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
peer.on("connection", handleConnection);
|
|
119
|
-
return () => {
|
|
120
|
-
var _a;
|
|
121
|
-
(_a = localStream.current) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
122
|
-
Object.values(connRef.current).forEach(closeConnection);
|
|
123
|
-
connRef.current = {};
|
|
124
|
-
Object.values(calls).forEach(closeConnection);
|
|
125
|
-
peer.removeAllListeners();
|
|
126
|
-
peer.destroy();
|
|
127
|
-
};
|
|
128
|
-
}, [peer]);
|
|
129
|
-
return { peerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
|
|
130
|
-
}
|
|
131
|
-
export function useMessages() {
|
|
132
|
-
const [messages, setMessages] = useStorage("rpc-messages", []);
|
|
133
|
-
const addMessage = (message) => setMessages((prev) => prev.concat(message));
|
|
134
|
-
return [messages, setMessages, addMessage];
|
|
135
|
-
}
|
|
136
|
-
export function useStorage(key, initialValue, local = false) {
|
|
137
|
-
const [storedValue, setStoredValue] = useState(() => {
|
|
138
|
-
if (typeof window === "undefined")
|
|
139
|
-
return initialValue;
|
|
140
|
-
return getStorage(key, initialValue, local);
|
|
141
|
-
});
|
|
142
|
-
const setValue = (value) => {
|
|
143
|
-
setStoredValue((old) => {
|
|
144
|
-
const updatedValue = typeof value === "function" ? value(old) : value;
|
|
145
|
-
setStorage(key, updatedValue, local);
|
|
146
|
-
return updatedValue;
|
|
147
|
-
});
|
|
148
|
-
};
|
|
149
|
-
return [storedValue, setValue];
|
|
150
|
-
}
|
|
151
|
-
export function useAudio(allowed) {
|
|
152
|
-
const [audio, setAudio] = useStorage("rpc-audio", false, true);
|
|
153
|
-
const enabled = audio || allowed;
|
|
154
|
-
return [enabled, setAudio];
|
|
155
|
-
}
|
package/dist/storage.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const removeStorage: (key: string, local?: boolean) => void;
|
|
2
|
-
export declare const setStorage: (key: string, value: any, local?: boolean) => void;
|
|
3
|
-
export declare const getStorage: (key: string, fallbackValue?: any, local?: boolean) => any;
|
|
4
|
-
export declare const clearChat: () => void;
|
package/dist/storage.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export const removeStorage = (key, local = false) => (local ? localStorage : sessionStorage).removeItem(key);
|
|
2
|
-
export const setStorage = (key, value, local = false) => (local ? localStorage : sessionStorage).setItem(key, JSON.stringify(value));
|
|
3
|
-
export const getStorage = (key, fallbackValue, local = false) => {
|
|
4
|
-
let value = (local ? localStorage : sessionStorage).getItem(key);
|
|
5
|
-
try {
|
|
6
|
-
if (!value)
|
|
7
|
-
throw new Error("Value doesn't exist");
|
|
8
|
-
value = JSON.parse(value);
|
|
9
|
-
}
|
|
10
|
-
catch (_a) {
|
|
11
|
-
if (fallbackValue !== undefined) {
|
|
12
|
-
value = fallbackValue;
|
|
13
|
-
setStorage(key, value, local);
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
value = null;
|
|
17
|
-
removeStorage(key, local);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return value;
|
|
21
|
-
};
|
|
22
|
-
export const clearChat = () => {
|
|
23
|
-
removeStorage("rpc-remote-peer");
|
|
24
|
-
removeStorage("rpc-messages");
|
|
25
|
-
};
|
package/dist/styles.css
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
.rpc-font {
|
|
2
|
-
font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
.rpc-main {
|
|
6
|
-
display: flex;
|
|
7
|
-
align-items: center;
|
|
8
|
-
column-gap: 0.5rem;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.rpc-dialog-container {
|
|
12
|
-
position: relative;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.rpc-notification {
|
|
16
|
-
position: relative;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.rpc-notification .rpc-badge {
|
|
20
|
-
background-color: red;
|
|
21
|
-
position: absolute;
|
|
22
|
-
top: 0;
|
|
23
|
-
right: 0;
|
|
24
|
-
width: 6px;
|
|
25
|
-
aspect-ratio: 1;
|
|
26
|
-
border-radius: 100%;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.rpc-dialog {
|
|
30
|
-
position: absolute;
|
|
31
|
-
background-color: black;
|
|
32
|
-
color: white;
|
|
33
|
-
padding: 0.4rem 0 0.25rem 0;
|
|
34
|
-
border-radius: 0.4rem;
|
|
35
|
-
font-size: small;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.rpc-position-left {
|
|
39
|
-
left: 0.6rem;
|
|
40
|
-
translate: -100%;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.rpc-position-center {
|
|
44
|
-
left: 0.5rem;
|
|
45
|
-
translate: -50%;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.rpc-position-right {
|
|
49
|
-
left: 0.3rem;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.rpc-hr {
|
|
53
|
-
margin: 0.25rem 0;
|
|
54
|
-
border-color: rgba(255, 255, 255, 0.7);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.rpc-message-container {
|
|
58
|
-
height: 7rem;
|
|
59
|
-
overflow-y: scroll;
|
|
60
|
-
padding-inline: 0.5rem;
|
|
61
|
-
margin-bottom: 0.25rem;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.rpc-message-container::-webkit-scrollbar {
|
|
65
|
-
width: 2.5px;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.rpc-message-container::-webkit-scrollbar-track {
|
|
69
|
-
background: gray;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.rpc-message-container::-webkit-scrollbar-thumb {
|
|
73
|
-
background-color: white;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.rpc-heading {
|
|
77
|
-
text-align: center;
|
|
78
|
-
font-size: medium;
|
|
79
|
-
font-weight: bold;
|
|
80
|
-
padding-inline: 0.5rem;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.rpc-input-container {
|
|
84
|
-
display: flex;
|
|
85
|
-
width: 15rem;
|
|
86
|
-
max-width: 90vw;
|
|
87
|
-
column-gap: 0.25rem;
|
|
88
|
-
padding: 0.25rem 0.5rem;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.rpc-input {
|
|
92
|
-
color: white;
|
|
93
|
-
width: 100%;
|
|
94
|
-
background-color: black;
|
|
95
|
-
border: none;
|
|
96
|
-
outline: 1px solid rgba(255, 255, 255, 0.8);
|
|
97
|
-
border-radius: 0.25rem;
|
|
98
|
-
padding: 0.3rem 0.25rem;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.rpc-input::placeholder {
|
|
102
|
-
color: rgba(255, 255, 255, 0.7);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.rpc-input:focus {
|
|
106
|
-
outline: 2px solid white;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.rpc-button {
|
|
110
|
-
all: unset;
|
|
111
|
-
display: flex;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.rpc-icon-container {
|
|
115
|
-
display: flex;
|
|
116
|
-
align-items: center;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.rpc-invert {
|
|
120
|
-
filter: invert(100%);
|
|
121
|
-
}
|
package/dist/utils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const addPrefix: (str: string) => string;
|
package/dist/utils.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const addPrefix = (str) => `rpc-${str}`;
|