react-peer-chat 0.8.5 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -11
- package/dist/chunks/chunk-GT3RG6VA.js +203 -0
- package/dist/chunks/{chunk-NXKKI6WY.js → chunk-JJPIWKLG.js} +2 -32
- package/dist/chunks/chunk-LNEKYYG7.js +33 -0
- package/dist/chunks/chunk-YDZ27MG7.js +81 -0
- package/dist/chunks/chunk-ZYFPSCFE.js +22 -0
- package/dist/components.d.ts +7 -0
- package/dist/components.js +5 -0
- package/dist/hooks.d.ts +11 -0
- package/dist/hooks.js +3 -0
- package/dist/icons.js +2 -1
- package/dist/index.d.ts +5 -10
- package/dist/index.js +5 -298
- package/dist/lib/storage.d.ts +6 -0
- package/dist/lib/storage.js +2 -0
- package/dist/types.d.ts +1 -1
- package/package.json +19 -16
package/README.md
CHANGED
|
@@ -317,12 +317,13 @@ that are listed in [useChat Hook API Reference](#usechat-hook-1) along with the
|
|
|
317
317
|
| `props` | [`DivProps`](#divprops) | No | - | Props to customize the `<Chat>` component. |
|
|
318
318
|
| `children` | [`Children`](#children) | No | - | See [usage with FaC](#full-customization) |
|
|
319
319
|
|
|
320
|
-
|
|
320
|
+
## Types
|
|
321
321
|
|
|
322
|
-
|
|
322
|
+
### Children
|
|
323
323
|
|
|
324
324
|
```typescript
|
|
325
|
-
import { ReactNode } from "react";
|
|
325
|
+
import type { ReactNode } from "react";
|
|
326
|
+
|
|
326
327
|
type RemotePeers = { [id: string]: string };
|
|
327
328
|
type Message = {
|
|
328
329
|
id: string;
|
|
@@ -338,10 +339,11 @@ type ChildrenOptions = {
|
|
|
338
339
|
type Children = (childrenOptions: ChildrenOptions) => ReactNode;
|
|
339
340
|
```
|
|
340
341
|
|
|
341
|
-
|
|
342
|
+
### DialogOptions
|
|
342
343
|
|
|
343
344
|
```typescript
|
|
344
|
-
import { CSSProperties } from "react";
|
|
345
|
+
import type { CSSProperties } from "react";
|
|
346
|
+
|
|
345
347
|
type DialogPosition = "left" | "center" | "right";
|
|
346
348
|
type DialogOptions = {
|
|
347
349
|
position: DialogPosition;
|
|
@@ -349,20 +351,21 @@ type DialogOptions = {
|
|
|
349
351
|
};
|
|
350
352
|
```
|
|
351
353
|
|
|
352
|
-
|
|
354
|
+
### DivProps
|
|
353
355
|
|
|
354
356
|
```typescript
|
|
355
|
-
import { DetailedHTMLProps, HTMLAttributes } from "react";
|
|
357
|
+
import type { DetailedHTMLProps, HTMLAttributes } from "react";
|
|
358
|
+
|
|
356
359
|
type DivProps = DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
|
357
360
|
```
|
|
358
361
|
|
|
359
|
-
|
|
362
|
+
### ErrorHandler
|
|
360
363
|
|
|
361
364
|
```typescript
|
|
362
365
|
type ErrorHandler = () => void;
|
|
363
366
|
```
|
|
364
367
|
|
|
365
|
-
|
|
368
|
+
### MessageEventHandler
|
|
366
369
|
|
|
367
370
|
```typescript
|
|
368
371
|
type Message = {
|
|
@@ -372,10 +375,10 @@ type Message = {
|
|
|
372
375
|
type MessageEventHandler = (message: Message) => void;
|
|
373
376
|
```
|
|
374
377
|
|
|
375
|
-
|
|
378
|
+
### PeerOptions
|
|
376
379
|
|
|
377
380
|
```typescript
|
|
378
|
-
import { PeerOptions } from "peerjs";
|
|
381
|
+
import type { PeerOptions } from "peerjs";
|
|
379
382
|
```
|
|
380
383
|
|
|
381
384
|
## License
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { getStorage, setStorage } from './chunk-ZYFPSCFE.js';
|
|
2
|
+
import { __spreadValues, __spreadProps } from './chunk-LNEKYYG7.js';
|
|
3
|
+
import { useState, useRef, useMemo, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var turnAccounts = [
|
|
7
|
+
{ username: "70061a377b51f3a3d01c11e3", credential: "lHV4NYJ5Rfl5JNa9" },
|
|
8
|
+
{ username: "13b19eb65bbf6e9f96d64b72", credential: "7R9P/+7y7Q516Etv" },
|
|
9
|
+
{ username: "3469603f5cdc7ca4a1e891ae", credential: "/jMyLSDbbcgqpVQv" },
|
|
10
|
+
{ username: "a7926f4dcc4a688d41f89752", credential: "ZYM8jFYeb8bQkL+N" },
|
|
11
|
+
{ username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
|
|
12
|
+
{ username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" }
|
|
13
|
+
];
|
|
14
|
+
var defaults = {
|
|
15
|
+
config: {
|
|
16
|
+
iceServers: [
|
|
17
|
+
{
|
|
18
|
+
urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"]
|
|
19
|
+
}
|
|
20
|
+
].concat(
|
|
21
|
+
turnAccounts.map((account) => __spreadValues({
|
|
22
|
+
urls: [
|
|
23
|
+
"turn:standard.relay.metered.ca:80",
|
|
24
|
+
"turn:standard.relay.metered.ca:80?transport=tcp",
|
|
25
|
+
"turn:standard.relay.metered.ca:443",
|
|
26
|
+
"turns:standard.relay.metered.ca:443?transport=tcp"
|
|
27
|
+
]
|
|
28
|
+
}, account))
|
|
29
|
+
)
|
|
30
|
+
},
|
|
31
|
+
peerOptions: {},
|
|
32
|
+
remotePeerId: []
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/lib/connection.ts
|
|
36
|
+
function closeConnection(conn) {
|
|
37
|
+
conn.removeAllListeners();
|
|
38
|
+
conn.close();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/lib/utils.ts
|
|
42
|
+
var addPrefix = (str) => `rpc-${str}`;
|
|
43
|
+
|
|
44
|
+
// src/lib/react.ts
|
|
45
|
+
function isSetStateFunction(v) {
|
|
46
|
+
return typeof v === "function";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/hooks.ts
|
|
50
|
+
var { config: defaultConfig, peerOptions: defaultPeerOptions, remotePeerId: defaultRemotePeerId } = defaults;
|
|
51
|
+
function useChat({
|
|
52
|
+
peerId,
|
|
53
|
+
name = "Anonymous User",
|
|
54
|
+
remotePeerId = defaultRemotePeerId,
|
|
55
|
+
peerOptions = defaultPeerOptions,
|
|
56
|
+
text = true,
|
|
57
|
+
recoverChat = false,
|
|
58
|
+
audio: allowed = true,
|
|
59
|
+
onError = () => alert("Browser not supported! Try some other browser."),
|
|
60
|
+
onMicError = () => alert("Microphone not accessible!"),
|
|
61
|
+
onMessageSent,
|
|
62
|
+
onMessageReceived
|
|
63
|
+
}) {
|
|
64
|
+
const [peer, setPeer] = useState();
|
|
65
|
+
const [audio, setAudio] = useAudio(allowed);
|
|
66
|
+
const connRef = useRef({});
|
|
67
|
+
const localStreamRef = useRef(null);
|
|
68
|
+
const audioStreamRef = useRef(null);
|
|
69
|
+
const callsRef = useRef({});
|
|
70
|
+
const [messages, setMessages, addMessage] = useMessages();
|
|
71
|
+
const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
|
|
72
|
+
const { completePeerId, completeRemotePeerIds } = useMemo(() => {
|
|
73
|
+
const remotePeerIds = Array.isArray(remotePeerId) ? remotePeerId : [remotePeerId];
|
|
74
|
+
return { completePeerId: addPrefix(peerId), completeRemotePeerIds: remotePeerIds.map(addPrefix) };
|
|
75
|
+
}, [peerId]);
|
|
76
|
+
function handleConnection(conn) {
|
|
77
|
+
connRef.current[conn.peer] = conn;
|
|
78
|
+
conn.on("open", () => {
|
|
79
|
+
conn.on("data", ({ message, messages: messages2, remotePeerName, type }) => {
|
|
80
|
+
if (type === "message") receiveMessage(message);
|
|
81
|
+
else if (type === "init") {
|
|
82
|
+
setRemotePeers((prev) => __spreadProps(__spreadValues({}, prev), { [conn.peer]: remotePeerName }));
|
|
83
|
+
if (recoverChat) setMessages((old) => messages2.length > old.length ? messages2 : old);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
conn.send({ type: "init", remotePeerName: name, messages });
|
|
87
|
+
});
|
|
88
|
+
conn.on("close", conn.removeAllListeners);
|
|
89
|
+
}
|
|
90
|
+
function handleError() {
|
|
91
|
+
setAudio(false);
|
|
92
|
+
onMicError();
|
|
93
|
+
}
|
|
94
|
+
function handleRemoteStream(remoteStream) {
|
|
95
|
+
if (audioStreamRef.current) audioStreamRef.current.srcObject = remoteStream;
|
|
96
|
+
}
|
|
97
|
+
function receiveMessage(message) {
|
|
98
|
+
addMessage(message);
|
|
99
|
+
onMessageReceived == null ? void 0 : onMessageReceived(message);
|
|
100
|
+
}
|
|
101
|
+
function sendMessage(message) {
|
|
102
|
+
addMessage(message);
|
|
103
|
+
Object.values(connRef.current).forEach((conn) => conn.send({ type: "message", message }));
|
|
104
|
+
onMessageSent == null ? void 0 : onMessageSent(message);
|
|
105
|
+
}
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (!text && !audio) return;
|
|
108
|
+
import('peerjs').then(
|
|
109
|
+
({
|
|
110
|
+
Peer,
|
|
111
|
+
util: {
|
|
112
|
+
supports: { audioVideo, data }
|
|
113
|
+
}
|
|
114
|
+
}) => {
|
|
115
|
+
if (!data || !audioVideo) return onError();
|
|
116
|
+
const peer2 = new Peer(completePeerId, __spreadValues({ config: defaultConfig }, peerOptions));
|
|
117
|
+
peer2.on("connection", handleConnection);
|
|
118
|
+
setPeer(peer2);
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
return () => {
|
|
122
|
+
setPeer((prev) => {
|
|
123
|
+
prev == null ? void 0 : prev.removeAllListeners();
|
|
124
|
+
prev == null ? void 0 : prev.destroy();
|
|
125
|
+
return void 0;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
}, [completePeerId]);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (!text || !peer) return;
|
|
131
|
+
const handleOpen = () => completeRemotePeerIds.forEach((id) => handleConnection(peer.connect(id)));
|
|
132
|
+
if (peer.open) handleOpen();
|
|
133
|
+
else peer.once("open", handleOpen);
|
|
134
|
+
return () => {
|
|
135
|
+
Object.values(connRef.current).forEach(closeConnection);
|
|
136
|
+
connRef.current = {};
|
|
137
|
+
};
|
|
138
|
+
}, [text, peer]);
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!audio || !peer) return;
|
|
141
|
+
const setupAudio = () => navigator.mediaDevices.getUserMedia({
|
|
142
|
+
video: false,
|
|
143
|
+
audio: {
|
|
144
|
+
autoGainControl: false,
|
|
145
|
+
// Disable automatic gain control
|
|
146
|
+
noiseSuppression: true,
|
|
147
|
+
// Enable noise suppression
|
|
148
|
+
echoCancellation: true
|
|
149
|
+
// Enable echo cancellation
|
|
150
|
+
}
|
|
151
|
+
}).then((stream) => {
|
|
152
|
+
localStreamRef.current = stream;
|
|
153
|
+
completeRemotePeerIds.forEach((id) => {
|
|
154
|
+
const call = peer.call(id, stream);
|
|
155
|
+
call.on("stream", handleRemoteStream);
|
|
156
|
+
call.on("close", call.removeAllListeners);
|
|
157
|
+
callsRef.current[id] = call;
|
|
158
|
+
});
|
|
159
|
+
peer.on("call", (call) => {
|
|
160
|
+
call.answer(stream);
|
|
161
|
+
call.on("stream", handleRemoteStream);
|
|
162
|
+
call.on("close", call.removeAllListeners);
|
|
163
|
+
callsRef.current[call.peer] = call;
|
|
164
|
+
});
|
|
165
|
+
}).catch(handleError);
|
|
166
|
+
if (peer.open) setupAudio();
|
|
167
|
+
else peer.once("open", setupAudio);
|
|
168
|
+
return () => {
|
|
169
|
+
var _a;
|
|
170
|
+
(_a = localStreamRef.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
171
|
+
localStreamRef.current = null;
|
|
172
|
+
Object.values(callsRef.current).forEach(closeConnection);
|
|
173
|
+
callsRef.current = {};
|
|
174
|
+
};
|
|
175
|
+
}, [audio, peer]);
|
|
176
|
+
return { peerId: completePeerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
|
|
177
|
+
}
|
|
178
|
+
function useMessages() {
|
|
179
|
+
const [messages, setMessages] = useStorage("rpc-messages", []);
|
|
180
|
+
const addMessage = (message) => setMessages((prev) => prev.concat(message));
|
|
181
|
+
return [messages, setMessages, addMessage];
|
|
182
|
+
}
|
|
183
|
+
function useStorage(key, initialValue, local = false) {
|
|
184
|
+
const [storedValue, setStoredValue] = useState(() => {
|
|
185
|
+
if (typeof window === "undefined") return initialValue;
|
|
186
|
+
return getStorage(key, initialValue, local);
|
|
187
|
+
});
|
|
188
|
+
const setValue = (value) => {
|
|
189
|
+
setStoredValue((prev) => {
|
|
190
|
+
const next = isSetStateFunction(value) ? value(prev) : value;
|
|
191
|
+
setStorage(key, next, local);
|
|
192
|
+
return next;
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
return [storedValue, setValue];
|
|
196
|
+
}
|
|
197
|
+
function useAudio(allowed) {
|
|
198
|
+
const [audio, setAudio] = useStorage("rpc-audio", false, true);
|
|
199
|
+
const enabled = audio && allowed;
|
|
200
|
+
return [enabled, setAudio];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export { useAudio, useChat, useMessages, useStorage };
|
|
@@ -1,36 +1,6 @@
|
|
|
1
|
+
import { __spreadValues } from './chunk-LNEKYYG7.js';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __defProps = Object.defineProperties;
|
|
5
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
9
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
10
|
-
var __spreadValues = (a, b) => {
|
|
11
|
-
for (var prop in b || (b = {}))
|
|
12
|
-
if (__hasOwnProp.call(b, prop))
|
|
13
|
-
__defNormalProp(a, prop, b[prop]);
|
|
14
|
-
if (__getOwnPropSymbols)
|
|
15
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
16
|
-
if (__propIsEnum.call(b, prop))
|
|
17
|
-
__defNormalProp(a, prop, b[prop]);
|
|
18
|
-
}
|
|
19
|
-
return a;
|
|
20
|
-
};
|
|
21
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
22
|
-
var __objRest = (source, exclude) => {
|
|
23
|
-
var target = {};
|
|
24
|
-
for (var prop in source)
|
|
25
|
-
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
26
|
-
target[prop] = source[prop];
|
|
27
|
-
if (source != null && __getOwnPropSymbols)
|
|
28
|
-
for (var prop of __getOwnPropSymbols(source)) {
|
|
29
|
-
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
30
|
-
target[prop] = source[prop];
|
|
31
|
-
}
|
|
32
|
-
return target;
|
|
33
|
-
};
|
|
34
4
|
function BiSolidMessageDetail(props) {
|
|
35
5
|
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" })));
|
|
36
6
|
}
|
|
@@ -55,4 +25,4 @@ function BsFillMicMuteFill(props) {
|
|
|
55
25
|
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" })));
|
|
56
26
|
}
|
|
57
27
|
|
|
58
|
-
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend
|
|
28
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
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 __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __objRest = (source, exclude) => {
|
|
21
|
+
var target = {};
|
|
22
|
+
for (var prop in source)
|
|
23
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
+
target[prop] = source[prop];
|
|
25
|
+
if (source != null && __getOwnPropSymbols)
|
|
26
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
+
target[prop] = source[prop];
|
|
29
|
+
}
|
|
30
|
+
return target;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { __objRest, __spreadProps, __spreadValues };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useChat } from './chunk-GT3RG6VA.js';
|
|
2
|
+
import { BiSolidMessageX, BiSolidMessageDetail, GrSend, BsFillMicFill, BsFillMicMuteFill } from './chunk-JJPIWKLG.js';
|
|
3
|
+
import { __objRest, __spreadValues } from './chunk-LNEKYYG7.js';
|
|
4
|
+
import React, { useRef, useState, useEffect } from 'react';
|
|
5
|
+
|
|
6
|
+
// src/styles.css
|
|
7
|
+
function injectStyle(css) {
|
|
8
|
+
if (typeof document === "undefined") return;
|
|
9
|
+
const head = document.head || document.getElementsByTagName("head")[0];
|
|
10
|
+
const style = document.createElement("style");
|
|
11
|
+
style.type = "text/css";
|
|
12
|
+
if (head.firstChild) {
|
|
13
|
+
head.insertBefore(style, head.firstChild);
|
|
14
|
+
} else {
|
|
15
|
+
head.appendChild(style);
|
|
16
|
+
}
|
|
17
|
+
if (style.styleSheet) {
|
|
18
|
+
style.styleSheet.cssText = css;
|
|
19
|
+
} else {
|
|
20
|
+
style.appendChild(document.createTextNode(css));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
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');
|
|
24
|
+
|
|
25
|
+
// src/components.tsx
|
|
26
|
+
function Chat(_a) {
|
|
27
|
+
var _b = _a, { text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children } = _b, hookProps = __objRest(_b, ["text", "audio", "onMessageReceived", "dialogOptions", "props", "children"]);
|
|
28
|
+
const _a2 = useChat(__spreadValues({
|
|
29
|
+
text,
|
|
30
|
+
audio,
|
|
31
|
+
onMessageReceived: modifiedOnMessageReceived
|
|
32
|
+
}, hookProps)), { peerId, audioStreamRef } = _a2, childrenOptions = __objRest(_a2, ["peerId", "audioStreamRef"]);
|
|
33
|
+
const { remotePeers, messages, sendMessage, audio: audioEnabled, setAudio } = childrenOptions;
|
|
34
|
+
const containerRef = useRef(null);
|
|
35
|
+
const [dialog, setDialog] = useState(false);
|
|
36
|
+
const dialogRef = useRef(null);
|
|
37
|
+
const inputRef = useRef(null);
|
|
38
|
+
const [notification, setNotification] = useState(false);
|
|
39
|
+
function modifiedOnMessageReceived(message) {
|
|
40
|
+
var _a3;
|
|
41
|
+
if (!((_a3 = dialogRef.current) == null ? void 0 : _a3.open)) setNotification(true);
|
|
42
|
+
onMessageReceived == null ? void 0 : onMessageReceived(message);
|
|
43
|
+
}
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
var _a3, _b2;
|
|
46
|
+
if (dialog) (_a3 = dialogRef.current) == null ? void 0 : _a3.show();
|
|
47
|
+
else (_b2 = dialogRef.current) == null ? void 0 : _b2.close();
|
|
48
|
+
}, [dialog]);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const container = containerRef.current;
|
|
51
|
+
if (container) container.scrollTop = container.scrollHeight;
|
|
52
|
+
}, [dialog, remotePeers, messages]);
|
|
53
|
+
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(
|
|
54
|
+
BiSolidMessageDetail,
|
|
55
|
+
{
|
|
56
|
+
title: "Open chat",
|
|
57
|
+
onClick: () => {
|
|
58
|
+
setNotification(false);
|
|
59
|
+
setDialog(true);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
), 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(
|
|
63
|
+
"form",
|
|
64
|
+
{
|
|
65
|
+
className: "rpc-input-container",
|
|
66
|
+
onSubmit: (e) => {
|
|
67
|
+
var _a3;
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
const text2 = (_a3 = inputRef.current) == null ? void 0 : _a3.value;
|
|
70
|
+
if (text2) {
|
|
71
|
+
inputRef.current.value = "";
|
|
72
|
+
sendMessage({ id: peerId, text: text2 });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
/* @__PURE__ */ React.createElement("input", { ref: inputRef, className: "rpc-input rpc-font", placeholder: "Enter a message" }),
|
|
77
|
+
/* @__PURE__ */ React.createElement("button", { type: "submit", className: "rpc-button" }, /* @__PURE__ */ React.createElement(GrSend, { title: "Send message" }))
|
|
78
|
+
)))), 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" } }));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { Chat };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/lib/storage.ts
|
|
2
|
+
var getStorageInstance = (local = true) => local ? localStorage : sessionStorage;
|
|
3
|
+
var removeStorage = (key, local = true) => getStorageInstance(local).removeItem(key);
|
|
4
|
+
var clearChat = () => {
|
|
5
|
+
removeStorage("rpc-remote-peer");
|
|
6
|
+
removeStorage("rpc-messages");
|
|
7
|
+
};
|
|
8
|
+
var setStorage = (key, value, local = true) => getStorageInstance(local).setItem(key, JSON.stringify(value));
|
|
9
|
+
function getStorage(key, fallbackValue, local = true) {
|
|
10
|
+
const value = getStorageInstance(local).getItem(key);
|
|
11
|
+
if (value) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(value);
|
|
14
|
+
} catch (e) {
|
|
15
|
+
removeStorage(key, local);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (fallbackValue !== void 0) setStorage(key, fallbackValue, local);
|
|
19
|
+
return fallbackValue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { clearChat, getStorage, removeStorage, setStorage };
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SetStateAction } from 'react';
|
|
2
|
+
import { UseChatProps, UseChatReturn, Message } from './types.js';
|
|
3
|
+
import 'peerjs';
|
|
4
|
+
|
|
5
|
+
declare function useChat({ peerId, name, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived, }: UseChatProps): UseChatReturn;
|
|
6
|
+
declare function useMessages(): readonly [Message[], (value: SetStateAction<Message[]>) => void, (message: Message) => void];
|
|
7
|
+
declare function useStorage<T>(key: string, initialValue: T, local?: boolean): readonly [T, (value: SetStateAction<T>) => void];
|
|
8
|
+
declare function useStorage<T>(key: string, initialValue?: T, local?: boolean): readonly [T | undefined, (value: SetStateAction<T | undefined>) => void];
|
|
9
|
+
declare function useAudio(allowed: boolean): readonly [boolean, (value: SetStateAction<boolean>) => void];
|
|
10
|
+
|
|
11
|
+
export { useAudio, useChat, useMessages, useStorage };
|
package/dist/hooks.js
ADDED
package/dist/icons.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-
|
|
1
|
+
export { BiSolidMessageDetail, BiSolidMessageX, BsFillMicFill, BsFillMicMuteFill, GrSend } from './chunks/chunk-JJPIWKLG.js';
|
|
2
|
+
import './chunks/chunk-LNEKYYG7.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { default } from './components.js';
|
|
2
|
+
export { useChat } from './hooks.js';
|
|
3
|
+
export { clearChat } from './lib/storage.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './types.js';
|
|
3
6
|
import 'peerjs';
|
|
4
|
-
|
|
5
|
-
declare function useChat({ peerId, name, remotePeerId, peerOptions, text, recoverChat, audio: allowed, onError, onMicError, onMessageSent, onMessageReceived, }: UseChatProps): UseChatReturn;
|
|
6
|
-
|
|
7
|
-
declare const clearChat: () => void;
|
|
8
|
-
|
|
9
|
-
declare function Chat({ text, audio, onMessageReceived, dialogOptions, props, children, ...hookProps }: ChatProps): React.JSX.Element;
|
|
10
|
-
|
|
11
|
-
export { clearChat, Chat as default, useChat };
|
package/dist/index.js
CHANGED
|
@@ -1,298 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
{ username: "70061a377b51f3a3d01c11e3", credential: "lHV4NYJ5Rfl5JNa9" },
|
|
7
|
-
{ username: "13b19eb65bbf6e9f96d64b72", credential: "7R9P/+7y7Q516Etv" },
|
|
8
|
-
{ username: "3469603f5cdc7ca4a1e891ae", credential: "/jMyLSDbbcgqpVQv" },
|
|
9
|
-
{ username: "a7926f4dcc4a688d41f89752", credential: "ZYM8jFYeb8bQkL+N" },
|
|
10
|
-
{ username: "0be25ab7f61d9d733ba94809", credential: "hiiSwWVch+ftt3SX" },
|
|
11
|
-
{ username: "3c25ba948daeab04f9b66187", credential: "FQB3GQwd27Y0dPeK" }
|
|
12
|
-
];
|
|
13
|
-
var defaults = {
|
|
14
|
-
config: {
|
|
15
|
-
iceServers: [
|
|
16
|
-
{
|
|
17
|
-
urls: ["stun:stun.l.google.com:19302", "stun:stun.relay.metered.ca:80"]
|
|
18
|
-
}
|
|
19
|
-
].concat(
|
|
20
|
-
turnAccounts.map((account) => __spreadValues({
|
|
21
|
-
urls: [
|
|
22
|
-
"turn:standard.relay.metered.ca:80",
|
|
23
|
-
"turn:standard.relay.metered.ca:80?transport=tcp",
|
|
24
|
-
"turn:standard.relay.metered.ca:443",
|
|
25
|
-
"turns:standard.relay.metered.ca:443?transport=tcp"
|
|
26
|
-
]
|
|
27
|
-
}, account))
|
|
28
|
-
)
|
|
29
|
-
},
|
|
30
|
-
peerOptions: {},
|
|
31
|
-
remotePeerId: []
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// src/lib/connection.ts
|
|
35
|
-
function closeConnection(conn) {
|
|
36
|
-
conn.removeAllListeners();
|
|
37
|
-
conn.close();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// src/lib/storage.ts
|
|
41
|
-
var getStorageInstance = (local = true) => local ? localStorage : sessionStorage;
|
|
42
|
-
var removeStorage = (key, local = true) => getStorageInstance(local).removeItem(key);
|
|
43
|
-
var clearChat = () => {
|
|
44
|
-
removeStorage("rpc-remote-peer");
|
|
45
|
-
removeStorage("rpc-messages");
|
|
46
|
-
};
|
|
47
|
-
var setStorage = (key, value, local = true) => getStorageInstance(local).setItem(key, JSON.stringify(value));
|
|
48
|
-
function getStorage(key, fallbackValue, local = true) {
|
|
49
|
-
const value = getStorageInstance(local).getItem(key);
|
|
50
|
-
if (value) {
|
|
51
|
-
try {
|
|
52
|
-
return JSON.parse(value);
|
|
53
|
-
} catch (e) {
|
|
54
|
-
removeStorage(key, local);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (fallbackValue !== void 0) setStorage(key, fallbackValue, local);
|
|
58
|
-
return fallbackValue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// src/lib/utils.ts
|
|
62
|
-
var addPrefix = (str) => `rpc-${str}`;
|
|
63
|
-
|
|
64
|
-
// src/lib/react.ts
|
|
65
|
-
function isSetStateFunction(v) {
|
|
66
|
-
return typeof v === "function";
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// src/hooks.ts
|
|
70
|
-
var { config: defaultConfig, peerOptions: defaultPeerOptions, remotePeerId: defaultRemotePeerId } = defaults;
|
|
71
|
-
function useChat({
|
|
72
|
-
peerId,
|
|
73
|
-
name = "Anonymous User",
|
|
74
|
-
remotePeerId = defaultRemotePeerId,
|
|
75
|
-
peerOptions = defaultPeerOptions,
|
|
76
|
-
text = true,
|
|
77
|
-
recoverChat = false,
|
|
78
|
-
audio: allowed = true,
|
|
79
|
-
onError = () => alert("Browser not supported! Try some other browser."),
|
|
80
|
-
onMicError = () => alert("Microphone not accessible!"),
|
|
81
|
-
onMessageSent,
|
|
82
|
-
onMessageReceived
|
|
83
|
-
}) {
|
|
84
|
-
const [peer, setPeer] = useState();
|
|
85
|
-
const [audio, setAudio] = useAudio(allowed);
|
|
86
|
-
const connRef = useRef({});
|
|
87
|
-
const localStreamRef = useRef(null);
|
|
88
|
-
const audioStreamRef = useRef(null);
|
|
89
|
-
const callsRef = useRef({});
|
|
90
|
-
const [messages, setMessages, addMessage] = useMessages();
|
|
91
|
-
const [remotePeers, setRemotePeers] = useStorage("rpc-remote-peer", {});
|
|
92
|
-
const { completePeerId, completeRemotePeerIds } = useMemo(() => {
|
|
93
|
-
const remotePeerIds = Array.isArray(remotePeerId) ? remotePeerId : [remotePeerId];
|
|
94
|
-
return { completePeerId: addPrefix(peerId), completeRemotePeerIds: remotePeerIds.map(addPrefix) };
|
|
95
|
-
}, [peerId]);
|
|
96
|
-
function handleConnection(conn) {
|
|
97
|
-
connRef.current[conn.peer] = conn;
|
|
98
|
-
conn.on("open", () => {
|
|
99
|
-
conn.on("data", ({ message, messages: messages2, remotePeerName, type }) => {
|
|
100
|
-
if (type === "message") receiveMessage(message);
|
|
101
|
-
else if (type === "init") {
|
|
102
|
-
setRemotePeers((prev) => __spreadProps(__spreadValues({}, prev), { [conn.peer]: remotePeerName }));
|
|
103
|
-
if (recoverChat) setMessages((old) => messages2.length > old.length ? messages2 : old);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
conn.send({ type: "init", remotePeerName: name, messages });
|
|
107
|
-
});
|
|
108
|
-
conn.on("close", conn.removeAllListeners);
|
|
109
|
-
}
|
|
110
|
-
function handleError() {
|
|
111
|
-
setAudio(false);
|
|
112
|
-
onMicError();
|
|
113
|
-
}
|
|
114
|
-
function handleRemoteStream(remoteStream) {
|
|
115
|
-
if (audioStreamRef.current) audioStreamRef.current.srcObject = remoteStream;
|
|
116
|
-
}
|
|
117
|
-
function receiveMessage(message) {
|
|
118
|
-
addMessage(message);
|
|
119
|
-
onMessageReceived == null ? void 0 : onMessageReceived(message);
|
|
120
|
-
}
|
|
121
|
-
function sendMessage(message) {
|
|
122
|
-
addMessage(message);
|
|
123
|
-
Object.values(connRef.current).forEach((conn) => conn.send({ type: "message", message }));
|
|
124
|
-
onMessageSent == null ? void 0 : onMessageSent(message);
|
|
125
|
-
}
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
if (!text && !audio) return;
|
|
128
|
-
import('peerjs').then(
|
|
129
|
-
({
|
|
130
|
-
Peer,
|
|
131
|
-
util: {
|
|
132
|
-
supports: { audioVideo, data }
|
|
133
|
-
}
|
|
134
|
-
}) => {
|
|
135
|
-
if (!data || !audioVideo) return onError();
|
|
136
|
-
const peer2 = new Peer(completePeerId, __spreadValues({ config: defaultConfig }, peerOptions));
|
|
137
|
-
peer2.on("connection", handleConnection);
|
|
138
|
-
setPeer(peer2);
|
|
139
|
-
}
|
|
140
|
-
);
|
|
141
|
-
return () => {
|
|
142
|
-
setPeer((prev) => {
|
|
143
|
-
prev == null ? void 0 : prev.removeAllListeners();
|
|
144
|
-
prev == null ? void 0 : prev.destroy();
|
|
145
|
-
return void 0;
|
|
146
|
-
});
|
|
147
|
-
};
|
|
148
|
-
}, [completePeerId]);
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
if (!text || !peer) return;
|
|
151
|
-
const handleOpen = () => completeRemotePeerIds.forEach((id) => handleConnection(peer.connect(id)));
|
|
152
|
-
if (peer.open) handleOpen();
|
|
153
|
-
else peer.once("open", handleOpen);
|
|
154
|
-
return () => {
|
|
155
|
-
Object.values(connRef.current).forEach(closeConnection);
|
|
156
|
-
connRef.current = {};
|
|
157
|
-
};
|
|
158
|
-
}, [text, peer]);
|
|
159
|
-
useEffect(() => {
|
|
160
|
-
if (!audio || !peer) return;
|
|
161
|
-
const setupAudio = () => navigator.mediaDevices.getUserMedia({
|
|
162
|
-
video: false,
|
|
163
|
-
audio: {
|
|
164
|
-
autoGainControl: false,
|
|
165
|
-
// Disable automatic gain control
|
|
166
|
-
noiseSuppression: true,
|
|
167
|
-
// Enable noise suppression
|
|
168
|
-
echoCancellation: true
|
|
169
|
-
// Enable echo cancellation
|
|
170
|
-
}
|
|
171
|
-
}).then((stream) => {
|
|
172
|
-
localStreamRef.current = stream;
|
|
173
|
-
completeRemotePeerIds.forEach((id) => {
|
|
174
|
-
const call = peer.call(id, stream);
|
|
175
|
-
call.on("stream", handleRemoteStream);
|
|
176
|
-
call.on("close", call.removeAllListeners);
|
|
177
|
-
callsRef.current[id] = call;
|
|
178
|
-
});
|
|
179
|
-
peer.on("call", (call) => {
|
|
180
|
-
call.answer(stream);
|
|
181
|
-
call.on("stream", handleRemoteStream);
|
|
182
|
-
call.on("close", call.removeAllListeners);
|
|
183
|
-
callsRef.current[call.peer] = call;
|
|
184
|
-
});
|
|
185
|
-
}).catch(handleError);
|
|
186
|
-
if (peer.open) setupAudio();
|
|
187
|
-
else peer.once("open", setupAudio);
|
|
188
|
-
return () => {
|
|
189
|
-
var _a;
|
|
190
|
-
(_a = localStreamRef.current) == null ? void 0 : _a.getTracks().forEach((track) => track.stop());
|
|
191
|
-
localStreamRef.current = null;
|
|
192
|
-
Object.values(callsRef.current).forEach(closeConnection);
|
|
193
|
-
callsRef.current = {};
|
|
194
|
-
};
|
|
195
|
-
}, [audio, peer]);
|
|
196
|
-
return { peerId: completePeerId, audioStreamRef, remotePeers, messages, sendMessage, audio, setAudio };
|
|
197
|
-
}
|
|
198
|
-
function useMessages() {
|
|
199
|
-
const [messages, setMessages] = useStorage("rpc-messages", []);
|
|
200
|
-
const addMessage = (message) => setMessages((prev) => prev.concat(message));
|
|
201
|
-
return [messages, setMessages, addMessage];
|
|
202
|
-
}
|
|
203
|
-
function useStorage(key, initialValue, local = false) {
|
|
204
|
-
const [storedValue, setStoredValue] = useState(() => {
|
|
205
|
-
if (typeof window === "undefined") return initialValue;
|
|
206
|
-
return getStorage(key, initialValue, local);
|
|
207
|
-
});
|
|
208
|
-
const setValue = (value) => {
|
|
209
|
-
setStoredValue((prev) => {
|
|
210
|
-
const next = isSetStateFunction(value) ? value(prev) : value;
|
|
211
|
-
setStorage(key, next, local);
|
|
212
|
-
return next;
|
|
213
|
-
});
|
|
214
|
-
};
|
|
215
|
-
return [storedValue, setValue];
|
|
216
|
-
}
|
|
217
|
-
function useAudio(allowed) {
|
|
218
|
-
const [audio, setAudio] = useStorage("rpc-audio", false, true);
|
|
219
|
-
const enabled = audio && allowed;
|
|
220
|
-
return [enabled, setAudio];
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// src/styles.css
|
|
224
|
-
function injectStyle(css) {
|
|
225
|
-
if (typeof document === "undefined") return;
|
|
226
|
-
const head = document.head || document.getElementsByTagName("head")[0];
|
|
227
|
-
const style = document.createElement("style");
|
|
228
|
-
style.type = "text/css";
|
|
229
|
-
if (head.firstChild) {
|
|
230
|
-
head.insertBefore(style, head.firstChild);
|
|
231
|
-
} else {
|
|
232
|
-
head.appendChild(style);
|
|
233
|
-
}
|
|
234
|
-
if (style.styleSheet) {
|
|
235
|
-
style.styleSheet.cssText = css;
|
|
236
|
-
} else {
|
|
237
|
-
style.appendChild(document.createTextNode(css));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
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');
|
|
241
|
-
|
|
242
|
-
// src/index.tsx
|
|
243
|
-
function Chat(_a) {
|
|
244
|
-
var _b = _a, { text = true, audio = true, onMessageReceived, dialogOptions, props = {}, children } = _b, hookProps = __objRest(_b, ["text", "audio", "onMessageReceived", "dialogOptions", "props", "children"]);
|
|
245
|
-
const _a2 = useChat(__spreadValues({
|
|
246
|
-
text,
|
|
247
|
-
audio,
|
|
248
|
-
onMessageReceived: modifiedOnMessageReceived
|
|
249
|
-
}, hookProps)), { peerId, audioStreamRef } = _a2, childrenOptions = __objRest(_a2, ["peerId", "audioStreamRef"]);
|
|
250
|
-
const { remotePeers, messages, sendMessage, audio: audioEnabled, setAudio } = childrenOptions;
|
|
251
|
-
const containerRef = useRef(null);
|
|
252
|
-
const [dialog, setDialog] = useState(false);
|
|
253
|
-
const dialogRef = useRef(null);
|
|
254
|
-
const inputRef = useRef(null);
|
|
255
|
-
const [notification, setNotification] = useState(false);
|
|
256
|
-
function modifiedOnMessageReceived(message) {
|
|
257
|
-
var _a3;
|
|
258
|
-
if (!((_a3 = dialogRef.current) == null ? void 0 : _a3.open)) setNotification(true);
|
|
259
|
-
onMessageReceived == null ? void 0 : onMessageReceived(message);
|
|
260
|
-
}
|
|
261
|
-
useEffect(() => {
|
|
262
|
-
var _a3, _b2;
|
|
263
|
-
if (dialog) (_a3 = dialogRef.current) == null ? void 0 : _a3.show();
|
|
264
|
-
else (_b2 = dialogRef.current) == null ? void 0 : _b2.close();
|
|
265
|
-
}, [dialog]);
|
|
266
|
-
useEffect(() => {
|
|
267
|
-
const container = containerRef.current;
|
|
268
|
-
if (container) container.scrollTop = container.scrollHeight;
|
|
269
|
-
}, [dialog, remotePeers, messages]);
|
|
270
|
-
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(
|
|
271
|
-
BiSolidMessageDetail,
|
|
272
|
-
{
|
|
273
|
-
title: "Open chat",
|
|
274
|
-
onClick: () => {
|
|
275
|
-
setNotification(false);
|
|
276
|
-
setDialog(true);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
), 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(
|
|
280
|
-
"form",
|
|
281
|
-
{
|
|
282
|
-
className: "rpc-input-container",
|
|
283
|
-
onSubmit: (e) => {
|
|
284
|
-
var _a3;
|
|
285
|
-
e.preventDefault();
|
|
286
|
-
const text2 = (_a3 = inputRef.current) == null ? void 0 : _a3.value;
|
|
287
|
-
if (text2) {
|
|
288
|
-
inputRef.current.value = "";
|
|
289
|
-
sendMessage({ id: peerId, text: text2 });
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
/* @__PURE__ */ React.createElement("input", { ref: inputRef, className: "rpc-input rpc-font", placeholder: "Enter a message" }),
|
|
294
|
-
/* @__PURE__ */ React.createElement("button", { type: "submit", className: "rpc-button" }, /* @__PURE__ */ React.createElement(GrSend, { title: "Send message" }))
|
|
295
|
-
)))), 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" } }));
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
export { clearChat, Chat as default, useChat };
|
|
1
|
+
export { Chat as default } from './chunks/chunk-YDZ27MG7.js';
|
|
2
|
+
export { useChat } from './chunks/chunk-GT3RG6VA.js';
|
|
3
|
+
import './chunks/chunk-JJPIWKLG.js';
|
|
4
|
+
export { clearChat } from './chunks/chunk-ZYFPSCFE.js';
|
|
5
|
+
import './chunks/chunk-LNEKYYG7.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare const removeStorage: (key: string, local?: boolean) => void;
|
|
2
|
+
declare const clearChat: () => void;
|
|
3
|
+
declare const setStorage: (key: string, value: unknown, local?: boolean) => void;
|
|
4
|
+
declare function getStorage<T>(key: string, fallbackValue?: T, local?: boolean): T | undefined;
|
|
5
|
+
|
|
6
|
+
export { clearChat, getStorage, removeStorage, setStorage };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PeerOptions, DataConnection, MediaConnection } from 'peerjs';
|
|
2
2
|
export { PeerOptions } from 'peerjs';
|
|
3
|
-
import { DetailedHTMLProps, HTMLAttributes, RefObject, SetStateAction
|
|
3
|
+
import { CSSProperties, DetailedHTMLProps, HTMLAttributes, ReactNode, RefObject, SetStateAction } from 'react';
|
|
4
4
|
|
|
5
5
|
type Connection = DataConnection | MediaConnection;
|
|
6
6
|
type ErrorHandler = () => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-peer-chat",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6",
|
|
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>",
|
|
@@ -14,54 +14,57 @@
|
|
|
14
14
|
"url": "https://github.com/SahilAggarwal2004/react-peer-chat/issues"
|
|
15
15
|
},
|
|
16
16
|
"type": "module",
|
|
17
|
-
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
17
|
"exports": {
|
|
21
18
|
".": "./dist/index.js",
|
|
22
19
|
"./icons": "./dist/icons.js",
|
|
23
20
|
"./types": "./dist/types.js"
|
|
24
21
|
},
|
|
25
22
|
"main": "dist/index.js",
|
|
26
|
-
"
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
27
26
|
"sideEffects": [
|
|
28
27
|
"**/*.css"
|
|
29
28
|
],
|
|
29
|
+
"types": "dist/index.d.ts",
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"peerjs": "^1.5.5"
|
|
32
32
|
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
35
|
+
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
36
|
+
},
|
|
33
37
|
"devDependencies": {
|
|
34
38
|
"@release-it/conventional-changelog": "^10.0.4",
|
|
35
39
|
"@types/react": "^19.2.7",
|
|
40
|
+
"prettier-package-json": "^2.8.0",
|
|
36
41
|
"release-it": "^19.2.2",
|
|
37
42
|
"tsup": "^8.5.1",
|
|
38
43
|
"typescript": "^5.9.3"
|
|
39
44
|
},
|
|
40
|
-
"peerDependencies": {
|
|
41
|
-
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
42
|
-
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
43
|
-
},
|
|
44
45
|
"keywords": [
|
|
45
|
-
"
|
|
46
|
+
"audio-chat",
|
|
46
47
|
"chat",
|
|
47
48
|
"component",
|
|
48
|
-
"peer",
|
|
49
|
-
"peerjs",
|
|
50
49
|
"p2p",
|
|
50
|
+
"p2p-chat",
|
|
51
|
+
"peer",
|
|
51
52
|
"peer-to-peer",
|
|
52
|
-
"
|
|
53
|
+
"peerjs",
|
|
54
|
+
"react",
|
|
53
55
|
"react-peer-chat",
|
|
54
|
-
"typescript",
|
|
55
|
-
"p2p-chat",
|
|
56
56
|
"text-chat",
|
|
57
|
+
"typescript",
|
|
57
58
|
"voice-chat",
|
|
58
|
-
"
|
|
59
|
+
"webrtc"
|
|
59
60
|
],
|
|
60
61
|
"scripts": {
|
|
61
62
|
"build": "pnpm i && pnpm run compile",
|
|
62
63
|
"compile": "tsup",
|
|
63
64
|
"dev": "tsup --watch",
|
|
64
65
|
"dry-release": "release-it --ci --dry-run",
|
|
66
|
+
"prettier": "prettier-package-json --write package.json",
|
|
67
|
+
"pub": "pnpm login && pnpm publish",
|
|
65
68
|
"release": "release-it --ci"
|
|
66
69
|
}
|
|
67
70
|
}
|