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 CHANGED
@@ -1,9 +1,9 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Sahil Aggawal, <aggarwalsahil2004@gmail.com>
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 && <button>{audioEnabled ? <BsFillMicFill title="Turn mic off" onClick={() => setAudio(false)} /> : <BsFillMicMuteFill title="Turn mic on" onClick={() => setAudio(true)} />}</button>}
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 && audioEnabled && <audio ref={audioStreamRef} autoPlay style={{ display: "none" }} />}
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 React from "react";
2
- import { IconProps } from "./types.js";
3
- export declare function BiSolidMessageDetail(props: IconProps): React.JSX.Element;
4
- export declare function BiSolidMessageX(props: IconProps): React.JSX.Element;
5
- export declare function GrSend(props: IconProps): React.JSX.Element;
6
- export declare function BsFillMicFill(props: IconProps): React.JSX.Element;
7
- export declare function BsFillMicMuteFill(props: IconProps): React.JSX.Element;
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
- import React from "react";
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 "react";
2
- import { useChat } from "./hooks.js";
3
- import { clearChat } from "./storage.js";
4
- import { ChatProps } from "./types.js";
5
- export default function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps): React.JSX.Element;
6
- export { clearChat, useChat };
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
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
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
- import React, { useEffect, useRef, useState } from "react";
13
- import { useChat } from "./hooks.js";
14
- import { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from "./icons.js";
15
- import { clearChat } from "./storage.js";
16
- export default function Chat(_a) {
17
- var { text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children } = _a, hookProps = __rest(_a, ["text", "audio", "onMessageReceived", "dialogOptions", "props", "children"]);
18
- const _b = useChat(Object.assign({ text,
19
- audio, onMessageReceived: modifiedOnMessageReceived }, hookProps)), { peerId, audioStreamRef } = _b, childrenOptions = __rest(_b, ["peerId", "audioStreamRef"]);
20
- const { remotePeers, messages, sendMessage, audio: audioEnabled, setAudio } = childrenOptions;
21
- const containerRef = useRef(null);
22
- const [dialog, setDialog] = useState(false);
23
- const dialogRef = useRef(null);
24
- const inputRef = useRef(null);
25
- const [notification, setNotification] = useState(false);
26
- function modifiedOnMessageReceived(message) {
27
- var _a;
28
- if (!((_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.open))
29
- setNotification(true);
30
- onMessageReceived === null || onMessageReceived === void 0 ? void 0 : onMessageReceived(message);
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
- useEffect(() => {
33
- var _a, _b;
34
- if (dialog)
35
- (_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.show();
36
- else
37
- (_b = dialogRef.current) === null || _b === void 0 ? void 0 : _b.close();
38
- }, [dialog]);
39
- useEffect(() => {
40
- const container = containerRef.current;
41
- if (container)
42
- container.scrollTop = container.scrollHeight;
43
- }, [dialog, remotePeers, messages]);
44
- return (React.createElement("div", Object.assign({ className: "rpc-main rpc-font" }, props),
45
- typeof children === "function" ? (children(childrenOptions)) : (React.createElement(React.Fragment, null,
46
- text && (React.createElement("div", { className: "rpc-dialog-container" },
47
- dialog ? (React.createElement(BiSolidMessageX, { title: "Close chat", onClick: () => setDialog(false) })) : (React.createElement("div", { className: "rpc-notification" },
48
- React.createElement(BiSolidMessageDetail, { title: "Open chat", onClick: () => {
49
- setNotification(false);
50
- setDialog(true);
51
- } }),
52
- notification && React.createElement("span", { className: "rpc-badge" }))),
53
- React.createElement("dialog", { ref: dialogRef, className: `${dialog ? "rpc-dialog" : ""} rpc-position-${(dialogOptions === null || dialogOptions === void 0 ? void 0 : dialogOptions.position) || "center"}`, style: dialogOptions === null || dialogOptions === void 0 ? void 0 : dialogOptions.style },
54
- React.createElement("div", { className: "rpc-heading" }, "Chat"),
55
- React.createElement("hr", { className: "rpc-hr" }),
56
- React.createElement("div", null,
57
- React.createElement("div", { ref: containerRef, className: "rpc-message-container" }, messages.map(({ id, text }, i) => (React.createElement("div", { key: i },
58
- React.createElement("strong", null,
59
- id === peerId ? "You" : remotePeers[id],
60
- ": "),
61
- React.createElement("span", null, text))))),
62
- React.createElement("hr", { className: "rpc-hr" }),
63
- React.createElement("form", { className: "rpc-input-container", onSubmit: (e) => {
64
- var _a;
65
- e.preventDefault();
66
- const text = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value;
67
- if (text) {
68
- inputRef.current.value = "";
69
- sendMessage({ id: peerId, text });
70
- }
71
- } },
72
- React.createElement("input", { ref: inputRef, className: "rpc-input rpc-font", placeholder: "Enter a message" }),
73
- React.createElement("button", { type: "submit", className: "rpc-button" },
74
- React.createElement(GrSend, { title: "Send message" }))))))),
75
- audio && React.createElement("button", null, audioEnabled ? React.createElement(BsFillMicFill, { title: "Turn mic off", onClick: () => setAudio(false) }) : React.createElement(BsFillMicMuteFill, { title: "Turn mic on", onClick: () => setAudio(true) })))),
76
- audio && audioEnabled && React.createElement("audio", { ref: audioStreamRef, autoPlay: true, style: { display: "none" } })));
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
- export { clearChat, useChat };
295
+
296
+ export { clearChat, Chat as default, useChat };
package/dist/types.d.ts CHANGED
@@ -1,13 +1,15 @@
1
- import { PeerOptions } from "peerjs";
2
- import { CSSProperties, DetailedHTMLProps, HTMLAttributes, ReactNode } from "react";
3
- export type Message = {
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
- export type MessageEvent = (message: Message) => void;
8
- export type { PeerOptions };
9
- export type RemotePeerId = string | string[];
10
- export type useChatProps = {
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
- export type IconProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
24
- export type RemotePeers = {
25
+ type IconProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>;
26
+ type RemotePeers = {
25
27
  [id: string]: string;
26
28
  };
27
- export type Children = (childrenOptions: ChildrenOptions) => ReactNode;
28
- export type ChildrenOptions = {
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
- export type ChatProps = useChatProps & {
37
+ type ChatProps = useChatProps & {
36
38
  dialogOptions?: DialogOptions;
37
39
  props?: DivProps;
38
40
  children?: Children;
39
41
  };
40
- export type DialogOptions = {
42
+ type DialogOptions = {
41
43
  position?: DialogPosition;
42
44
  style?: CSSProperties;
43
45
  };
44
- export type DialogPosition = "left" | "center" | "right";
45
- export type DivProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
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
- export {};
1
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-peer-chat",
3
- "version": "0.8.0",
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.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
- "copy": "copy .\\src\\*.css dist",
53
- "build": "pnpm i && tsc && pnpm copy"
53
+ "build": "pnpm i && pnpm run compile",
54
+ "compile": "tsup",
55
+ "dev": "tsup --watch"
54
56
  }
55
57
  }
@@ -1,2 +0,0 @@
1
- import { DataConnection, MediaConnection } from "peerjs";
2
- export declare function closeConnection(conn: DataConnection | MediaConnection): void;
@@ -1,4 +0,0 @@
1
- export function closeConnection(conn) {
2
- conn.removeAllListeners();
3
- conn.close();
4
- }
@@ -1,5 +0,0 @@
1
- export declare const defaultConfig: {
2
- iceServers: {
3
- urls: string[];
4
- }[];
5
- };
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}`;