@cloudflare/realtimekit-react 0.0.0 → 2.5.0-staging.88

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.
@@ -0,0 +1,37 @@
1
+ import React, { useEffect } from 'react';
2
+
3
+ import { DyteProvider, useDyteClient } from '../src';
4
+ import Meeting from './components/Meeting';
5
+ import Participants from './components/Participants';
6
+ import Chat from './components/Chat';
7
+
8
+ const roomName = 'avbigx-hemnqy';
9
+
10
+ function App() {
11
+ const [client, loadClient] = useDyteClient();
12
+
13
+ useEffect(() => {
14
+ fetch('https://api.cluster.dyte.in/auth/anonymous')
15
+ .then((res) => res.json())
16
+ .then((data) => {
17
+ const { authToken } = data;
18
+ loadClient({
19
+ roomName,
20
+ authToken,
21
+ defaults: { video: false, audio: false },
22
+ });
23
+ });
24
+ }, []);
25
+
26
+ Object.assign(window, { client });
27
+
28
+ return (
29
+ <DyteProvider value={client}>
30
+ <Meeting />
31
+ <Participants />
32
+ <Chat />
33
+ </DyteProvider>
34
+ );
35
+ }
36
+
37
+ export default App;
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { useDyteSelector } from '../../src';
3
+
4
+ function Chat() {
5
+ const roomJoined = useDyteSelector((meeting) => meeting.self.roomJoined);
6
+ const chat = useDyteSelector((meeting) => meeting.chat);
7
+ if (!roomJoined) {
8
+ return null;
9
+ }
10
+
11
+ return (
12
+ <fieldset className="section">
13
+ <legend>Chat</legend>
14
+ <div>
15
+ <ul>
16
+ {chat?.messages.map((message) => (
17
+ <li key={message.id}>
18
+ {message.type}
19
+ {' '}
20
+ -
21
+ {message.type === 'text' && message.message}
22
+ </li>
23
+ ))}
24
+ </ul>
25
+ <form
26
+ onSubmit={(e) => {
27
+ e.preventDefault();
28
+ const messageInput = (e.target as any).elements
29
+ .message as HTMLInputElement;
30
+ const message = messageInput.value.trim();
31
+
32
+ if (message !== '') {
33
+ if (chat) {
34
+ chat.sendTextMessage(message);
35
+ }
36
+ messageInput.value = '';
37
+ }
38
+ }}
39
+ >
40
+ <input type="text" name="message" />
41
+ <button type="submit">Send</button>
42
+ </form>
43
+ </div>
44
+ </fieldset>
45
+ );
46
+ }
47
+
48
+ export default Chat;
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import { useDyteMeeting, useDyteSelector } from '../../src';
3
+
4
+ function Meeting() {
5
+ const { meeting } = useDyteMeeting();
6
+ const roomJoined = useDyteSelector((m) => m.self.roomJoined);
7
+ const participantCount = useDyteSelector(
8
+ (m) => m.participants.count,
9
+ );
10
+ const self = useDyteSelector((m) => m.self);
11
+
12
+ return (
13
+ <fieldset>
14
+ <legend>Meeting details</legend>
15
+ <div>
16
+ {!roomJoined && (
17
+ <div>
18
+ <div>
19
+ Status:
20
+ {' '}
21
+ {roomJoined === undefined ? 'Connecting...' : 'Connected'}
22
+ </div>
23
+ {meeting && (
24
+ <div>
25
+ <button type="button" onClick={() => meeting.joinRoom()}>Join</button>
26
+ </div>
27
+ )}
28
+ </div>
29
+ )}
30
+ {roomJoined && (
31
+ <div>
32
+ <div>
33
+ Name:
34
+ {self?.name}
35
+ </div>
36
+ <div>
37
+ audio:
38
+ {' '}
39
+ <span className="token">
40
+ {JSON.stringify(self?.audioEnabled)}
41
+ </span>
42
+ , video:
43
+ {' '}
44
+ <span className="token">
45
+ {JSON.stringify(self?.videoEnabled)}
46
+ </span>
47
+ </div>
48
+ <div>
49
+ Participant Count:
50
+ {participantCount}
51
+ </div>
52
+ <button
53
+ type="button"
54
+ onClick={() => {
55
+ if (self?.audioEnabled) {
56
+ self?.disableAudio();
57
+ } else {
58
+ self?.enableAudio();
59
+ }
60
+ }}
61
+ >
62
+ {self?.audioEnabled ? 'Disable Audio' : 'Enable Audio'}
63
+ </button>
64
+ <button
65
+ type="button"
66
+ onClick={() => {
67
+ if (self?.videoEnabled) {
68
+ self?.disableVideo();
69
+ } else {
70
+ self?.enableVideo();
71
+ }
72
+ }}
73
+ >
74
+ {self?.videoEnabled ? 'Disable Video' : 'Enable Video'}
75
+ </button>
76
+ </div>
77
+ )}
78
+ </div>
79
+ </fieldset>
80
+ );
81
+ }
82
+
83
+ export default Meeting;
@@ -0,0 +1,112 @@
1
+ /* eslint-disable jsx-a11y/media-has-caption */
2
+ import React, { FC, useEffect, useRef } from 'react';
3
+ import { useDyteSelector } from '../../src';
4
+
5
+ const PeerView: FC<{
6
+ peerId: string,
7
+ }> = ({
8
+ peerId,
9
+ }) => {
10
+ const {
11
+ audioEnabled, audioTrack, videoEnabled, videoTrack,
12
+ } = useDyteSelector(
13
+ (meeting) => meeting.participants.active.get(peerId),
14
+ )!;
15
+ const videoRef = useRef<HTMLVideoElement>(null);
16
+ const audioRef = useRef<HTMLAudioElement>(null);
17
+
18
+ useEffect(() => {
19
+ if (!audioRef.current) return;
20
+
21
+ if (audioEnabled && audioTrack) {
22
+ const stream = new MediaStream();
23
+ stream.addTrack(audioTrack);
24
+ audioRef.current.srcObject = stream;
25
+ } else {
26
+ audioRef.current.srcObject = null;
27
+ }
28
+ }, [audioEnabled, audioTrack]);
29
+
30
+ useEffect(() => {
31
+ if (!videoRef.current) return;
32
+
33
+ if (videoEnabled && videoTrack) {
34
+ const stream = new MediaStream();
35
+ stream.addTrack(videoTrack);
36
+ videoRef.current.srcObject = stream;
37
+ } else {
38
+ videoRef.current.srcObject = null;
39
+ }
40
+ }, [videoEnabled, videoTrack]);
41
+
42
+ return (
43
+ <div>
44
+ <video
45
+ width={100}
46
+ height={100}
47
+ ref={videoRef}
48
+ autoPlay
49
+ playsInline
50
+ style={{ display: videoEnabled ? 'block' : 'none', background: 'wheat' }}
51
+ />
52
+ <audio ref={audioRef} autoPlay />
53
+ </div>
54
+ );
55
+ };
56
+
57
+ function ActiveParticipants() {
58
+ const activeParticipants = useDyteSelector(
59
+ (meeting) => meeting.participants.active,
60
+ );
61
+
62
+ return (
63
+ <div>
64
+ {activeParticipants?.toArray().map((peer) => (
65
+ <PeerView
66
+ key={peer.id}
67
+ peerId={peer.id}
68
+ />
69
+ ))}
70
+ </div>
71
+ );
72
+ }
73
+
74
+ function Participants() {
75
+ const roomJoined = useDyteSelector((meeting) => meeting.self.roomJoined);
76
+ const joinedParticipants = useDyteSelector(
77
+ (meeting) => meeting.participants.joined,
78
+ );
79
+
80
+ if (!joinedParticipants || !roomJoined) {
81
+ return null;
82
+ }
83
+
84
+ return (
85
+ <fieldset className="section">
86
+ <legend>Participants</legend>
87
+ <div>
88
+ <h2>Active Participants</h2>
89
+ <ActiveParticipants />
90
+ </div>
91
+ <div>
92
+ <h2>Joined Participants</h2>
93
+ <ul style={{ paddingLeft: '1.25rem' }}>
94
+ {joinedParticipants?.toArray().map((peer) => (
95
+ <li key={peer.id}>
96
+ {peer.name}
97
+ {' '}
98
+ - audio:
99
+ {' '}
100
+ <span className="token">{JSON.stringify(peer.audioEnabled)}</span>
101
+ , video:
102
+ {' '}
103
+ <span className="token">{JSON.stringify(peer.videoEnabled)}</span>
104
+ </li>
105
+ ))}
106
+ </ul>
107
+ </div>
108
+ </fieldset>
109
+ );
110
+ }
111
+
112
+ export default Participants;
@@ -0,0 +1,24 @@
1
+ body {
2
+ font-family: sans-serif;
3
+ margin: 1rem;
4
+ }
5
+
6
+ span.token {
7
+ color: #2160fd;
8
+ }
9
+
10
+ .section {
11
+ margin-top: 1rem;
12
+ }
13
+
14
+ fieldset {
15
+ border: 2px solid #c7c7c7;
16
+ border-radius: 6px;
17
+ }
18
+
19
+ #root {
20
+ display: block;
21
+ width: 100%;
22
+ max-width: 768px;
23
+ margin: 0 auto;
24
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import './index.css';
4
+ import App from './App';
5
+
6
+ ReactDOM.render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ document.getElementById('root'),
11
+ );
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
package/package.json CHANGED
@@ -1,5 +1,31 @@
1
1
  {
2
- "name": "@cloudflare/realtimekit-react",
3
- "version": "0.0.0",
4
- "scripts": {}
5
- }
2
+ "name": "@cloudflare/realtimekit-react",
3
+ "version": "2.5.0-staging.88",
4
+ "description": "A real-time video and audio SDK for building custom, collaborative communication experiences.",
5
+ "main": "./dist/index.cjs.js",
6
+ "module": "./dist/index.es.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.es.js",
10
+ "require": "./dist/index.cjs.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "types": "./dist/index.d.ts",
15
+ "bugs": {
16
+ "url": "https://realtime.cloudflare.com/issues"
17
+ },
18
+ "private": false,
19
+ "dependencies": {
20
+ "@cloudflare/realtimekit": "2.5.0-staging.88"
21
+ },
22
+ "peerDependencies": {
23
+ "react": ">=16.8.6"
24
+ },
25
+ "publishConfig": {
26
+ "tag": "staging"
27
+ },
28
+ "scripts": {
29
+ "postpublish": "mv package.json.bak package.json"
30
+ }
31
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "module": "esnext",
5
+ "moduleResolution": "node"
6
+ },
7
+ "include": ["vite.config.ts"]
8
+ }
@@ -0,0 +1,36 @@
1
+ import React, { ReactNode } from 'react';
2
+ import DyteClient from '@dytesdk/web-core';
3
+ type Listener = () => void;
4
+ declare class DyteUpdates {
5
+ private meeting;
6
+ private l;
7
+ constructor(meeting: DyteClient);
8
+ clean(): void;
9
+ addListener(listener: Listener): void;
10
+ removeListener(listener: Listener): void;
11
+ private onUpdate;
12
+ }
13
+ export declare const DyteContext: React.Context<{
14
+ meeting: DyteClient | undefined;
15
+ updates: DyteUpdates | undefined;
16
+ }>;
17
+ /**
18
+ * Provider component which makes the DyteClient object
19
+ * available to nested components
20
+ * @component
21
+ * @param value The DyteClient instance from `useDyteClient()`
22
+ * @param fallback Any fallback UI you want to render until the instance initialises.
23
+ */
24
+ export declare function DyteProvider({ value, children, fallback, }: {
25
+ value: DyteClient | undefined;
26
+ children: ReactNode;
27
+ fallback?: ReactNode;
28
+ }): import("react/jsx-runtime").JSX.Element;
29
+ /**
30
+ * Hook which returns the reference to the DyteClient object
31
+ * @returns meeting instance from `useDyteClient()`
32
+ */
33
+ export declare const useDyteMeeting: () => {
34
+ meeting: DyteClient;
35
+ };
36
+ export {};
@@ -0,0 +1,14 @@
1
+ import DyteClient, { DyteConfigOptions } from '@dytesdk/web-core';
2
+ interface DyteClientParams {
3
+ resetOnLeave?: boolean;
4
+ }
5
+ /**
6
+ * Hook which manages a DyteClient instance
7
+ */
8
+ export declare const useDyteClient: (dyteClientParams?: DyteClientParams) => readonly [DyteClient | undefined, (options: DyteConfigOptions) => Promise<DyteClient | undefined>];
9
+ type StateSelector<T extends object, U> = (state: T) => U;
10
+ /**
11
+ * Hook which extracts data from the DyteClient object
12
+ */
13
+ export declare const useDyteSelector: <StateSlice>(selector: StateSelector<DyteClient, StateSlice>) => StateSlice;
14
+ export * from './context';