@cloudflare/realtimekit-react 1.2.0-staging.9 → 1.2.1-staging.1
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 +201 -0
- package/build-types.sh +12 -0
- package/dist/index.cjs.js +1 -23
- package/dist/index.d.ts +20 -12
- package/dist/index.es.js +41 -30640
- package/example/App.tsx +34 -0
- package/example/components/Chat.tsx +51 -0
- package/example/components/Meeting.tsx +83 -0
- package/example/components/Participants.tsx +122 -0
- package/example/index.css +24 -0
- package/example/main.tsx +11 -0
- package/example/vite-env.d.ts +1 -0
- package/package.json +3 -6
- package/tsconfig.node.json +8 -0
- package/types/context.d.ts +36 -0
- package/types/index.d.ts +14 -0
package/example/App.tsx
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import { RealtimeKitProvider, useRealtimeKitClient } from '../src';
|
|
4
|
+
import Meeting from './components/Meeting';
|
|
5
|
+
import Participants from './components/Participants';
|
|
6
|
+
import Chat from './components/Chat';
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
const [client, loadClient] = useRealtimeKitClient();
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
fetch('https://api.cluster.dyte.in/auth/anonymous')
|
|
13
|
+
.then((res) => res.json())
|
|
14
|
+
.then((data) => {
|
|
15
|
+
const { authToken } = data;
|
|
16
|
+
loadClient({
|
|
17
|
+
authToken,
|
|
18
|
+
defaults: { video: false, audio: false },
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}, []);
|
|
22
|
+
|
|
23
|
+
Object.assign(window, { client });
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<RealtimeKitProvider value={client}>
|
|
27
|
+
<Meeting />
|
|
28
|
+
<Participants />
|
|
29
|
+
<Chat />
|
|
30
|
+
</RealtimeKitProvider>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default App;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useRealtimeKitSelector } from '../../src';
|
|
3
|
+
|
|
4
|
+
function Chat() {
|
|
5
|
+
const roomJoined = useRealtimeKitSelector(
|
|
6
|
+
(meeting) => meeting.self.roomJoined,
|
|
7
|
+
);
|
|
8
|
+
const chat = useRealtimeKitSelector((meeting) => meeting.chat);
|
|
9
|
+
if (!roomJoined) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<fieldset className="section">
|
|
15
|
+
<legend>Chat</legend>
|
|
16
|
+
<div>
|
|
17
|
+
<ul>
|
|
18
|
+
{chat?.messages.map(
|
|
19
|
+
(message: any) => (
|
|
20
|
+
<li key={message.id}>
|
|
21
|
+
{message.type}
|
|
22
|
+
-
|
|
23
|
+
{message.type === 'text' && message.message}
|
|
24
|
+
</li>
|
|
25
|
+
),
|
|
26
|
+
)}
|
|
27
|
+
</ul>
|
|
28
|
+
<form
|
|
29
|
+
onSubmit={(e) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
const messageInput = (e.target as any).elements
|
|
32
|
+
.message as HTMLInputElement;
|
|
33
|
+
const message = messageInput.value.trim();
|
|
34
|
+
|
|
35
|
+
if (message !== '') {
|
|
36
|
+
if (chat) {
|
|
37
|
+
chat.sendTextMessage(message);
|
|
38
|
+
}
|
|
39
|
+
messageInput.value = '';
|
|
40
|
+
}
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<input type="text" name="message" />
|
|
44
|
+
<button type="submit">Send</button>
|
|
45
|
+
</form>
|
|
46
|
+
</div>
|
|
47
|
+
</fieldset>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default Chat;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useRealtimeKitMeeting, useRealtimeKitSelector } from '../../src';
|
|
3
|
+
|
|
4
|
+
function Meeting() {
|
|
5
|
+
const { meeting } = useRealtimeKitMeeting();
|
|
6
|
+
const roomJoined = useRealtimeKitSelector((m) => m.self.roomJoined);
|
|
7
|
+
const participantCount = useRealtimeKitSelector((m) => m.participants.count);
|
|
8
|
+
const self = useRealtimeKitSelector((m) => m.self);
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<fieldset>
|
|
12
|
+
<legend>Meeting details</legend>
|
|
13
|
+
<div>
|
|
14
|
+
{!roomJoined && (
|
|
15
|
+
<div>
|
|
16
|
+
<div>
|
|
17
|
+
Status:
|
|
18
|
+
{' '}
|
|
19
|
+
{roomJoined === undefined ? 'Connecting...' : 'Connected'}
|
|
20
|
+
</div>
|
|
21
|
+
{meeting && (
|
|
22
|
+
<div>
|
|
23
|
+
<button type="button" onClick={() => meeting.joinRoom()}>
|
|
24
|
+
Join
|
|
25
|
+
</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,122 @@
|
|
|
1
|
+
import React, { FC, useEffect, useRef } from 'react';
|
|
2
|
+
import { useRealtimeKitSelector } from '../../src';
|
|
3
|
+
|
|
4
|
+
const PeerView: FC<{
|
|
5
|
+
peerId: string;
|
|
6
|
+
}> = ({ peerId }) => {
|
|
7
|
+
const {
|
|
8
|
+
audioEnabled, audioTrack, videoEnabled, videoTrack,
|
|
9
|
+
} = useRealtimeKitSelector((meeting) => meeting.participants.active.get(peerId))!;
|
|
10
|
+
const videoRef = useRef<HTMLVideoElement>(null);
|
|
11
|
+
const audioRef = useRef<HTMLAudioElement>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!audioRef.current) return;
|
|
15
|
+
|
|
16
|
+
if (audioEnabled && audioTrack) {
|
|
17
|
+
const stream = new MediaStream();
|
|
18
|
+
stream.addTrack(audioTrack);
|
|
19
|
+
audioRef.current.srcObject = stream;
|
|
20
|
+
} else {
|
|
21
|
+
audioRef.current.srcObject = null;
|
|
22
|
+
}
|
|
23
|
+
}, [audioEnabled, audioTrack]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!videoRef.current) return;
|
|
27
|
+
|
|
28
|
+
if (videoEnabled && videoTrack) {
|
|
29
|
+
const stream = new MediaStream();
|
|
30
|
+
stream.addTrack(videoTrack);
|
|
31
|
+
videoRef.current.srcObject = stream;
|
|
32
|
+
} else {
|
|
33
|
+
videoRef.current.srcObject = null;
|
|
34
|
+
}
|
|
35
|
+
}, [videoEnabled, videoTrack]);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div>
|
|
39
|
+
<video
|
|
40
|
+
width={100}
|
|
41
|
+
height={100}
|
|
42
|
+
ref={videoRef}
|
|
43
|
+
autoPlay
|
|
44
|
+
playsInline
|
|
45
|
+
style={{
|
|
46
|
+
display: videoEnabled ? 'block' : 'none',
|
|
47
|
+
background: 'wheat',
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
<audio ref={audioRef} autoPlay />
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function ActiveParticipants() {
|
|
56
|
+
const activeParticipants = useRealtimeKitSelector(
|
|
57
|
+
(meeting) => meeting.participants.active,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div>
|
|
62
|
+
{activeParticipants?.toArray().map((peer: { id: string }) => (
|
|
63
|
+
<PeerView key={peer.id} peerId={peer.id} />
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function Participants() {
|
|
70
|
+
const roomJoined = useRealtimeKitSelector(
|
|
71
|
+
(meeting) => meeting.self.roomJoined,
|
|
72
|
+
);
|
|
73
|
+
const joinedParticipants = useRealtimeKitSelector(
|
|
74
|
+
(meeting) => meeting.participants.joined,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!joinedParticipants || !roomJoined) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<fieldset className="section">
|
|
83
|
+
<legend>Participants</legend>
|
|
84
|
+
<div>
|
|
85
|
+
<h2>Active Participants</h2>
|
|
86
|
+
<ActiveParticipants />
|
|
87
|
+
</div>
|
|
88
|
+
<div>
|
|
89
|
+
<h2>Joined Participants</h2>
|
|
90
|
+
<ul style={{ paddingLeft: '1.25rem' }}>
|
|
91
|
+
{joinedParticipants
|
|
92
|
+
?.toArray()
|
|
93
|
+
.map(
|
|
94
|
+
(peer: {
|
|
95
|
+
id: string;
|
|
96
|
+
audioEnabled: boolean;
|
|
97
|
+
name: string;
|
|
98
|
+
videoEnabled: boolean;
|
|
99
|
+
}) => (
|
|
100
|
+
<li key={peer.id}>
|
|
101
|
+
{peer.name}
|
|
102
|
+
{' '}
|
|
103
|
+
- audio:
|
|
104
|
+
{' '}
|
|
105
|
+
<span className="token">
|
|
106
|
+
{JSON.stringify(peer.audioEnabled)}
|
|
107
|
+
</span>
|
|
108
|
+
, video:
|
|
109
|
+
{' '}
|
|
110
|
+
<span className="token">
|
|
111
|
+
{JSON.stringify(peer.videoEnabled)}
|
|
112
|
+
</span>
|
|
113
|
+
</li>
|
|
114
|
+
),
|
|
115
|
+
)}
|
|
116
|
+
</ul>
|
|
117
|
+
</div>
|
|
118
|
+
</fieldset>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
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
|
+
}
|
package/example/main.tsx
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudflare/realtimekit-react",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1-staging.1",
|
|
4
4
|
"description": "A real-time video and audio SDK for building custom, collaborative communication experiences.",
|
|
5
5
|
"main": "./dist/index.cjs.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -12,15 +12,12 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
|
-
"files": [
|
|
16
|
-
"dist"
|
|
17
|
-
],
|
|
18
15
|
"bugs": {
|
|
19
|
-
"url": "https://
|
|
16
|
+
"url": "https://community.cloudflare.com"
|
|
20
17
|
},
|
|
21
18
|
"private": false,
|
|
22
19
|
"dependencies": {
|
|
23
|
-
"@cloudflare/realtimekit": "1.2.
|
|
20
|
+
"@cloudflare/realtimekit": "1.2.1-staging.1"
|
|
24
21
|
},
|
|
25
22
|
"peerDependencies": {
|
|
26
23
|
"react": ">=16.8.6"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import RTKClient from '@cloudflare/realtimekit';
|
|
3
|
+
type Listener = () => void;
|
|
4
|
+
declare class RTKUpdates {
|
|
5
|
+
private meeting;
|
|
6
|
+
private l;
|
|
7
|
+
constructor(meeting: RTKClient);
|
|
8
|
+
clean(): void;
|
|
9
|
+
addListener(listener: Listener): void;
|
|
10
|
+
removeListener(listener: Listener): void;
|
|
11
|
+
private onUpdate;
|
|
12
|
+
}
|
|
13
|
+
export declare const RealtimeKitContext: React.Context<{
|
|
14
|
+
meeting: RTKClient | undefined;
|
|
15
|
+
updates: RTKUpdates | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Provider component which makes the RealtimeKitClient object
|
|
19
|
+
* available to nested components
|
|
20
|
+
* @component
|
|
21
|
+
* @param value The RealtimeKitClient instance from `useRealtimeKitClient()`
|
|
22
|
+
* @param fallback Any fallback UI you want to render until the instance initialises.
|
|
23
|
+
*/
|
|
24
|
+
export declare function RealtimeKitProvider({ value, children, fallback, }: {
|
|
25
|
+
value: RTKClient | undefined;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
fallback?: ReactNode;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
/**
|
|
30
|
+
* Hook which returns the reference to the RealtimeKitClient object
|
|
31
|
+
* @returns meeting instance from `useRealtimeKitClient()`
|
|
32
|
+
*/
|
|
33
|
+
export declare const useRealtimeKitClient: () => {
|
|
34
|
+
meeting: RTKClient;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import RTKClient, { RTKConfigOptions } from '@cloudflare/realtimekit';
|
|
2
|
+
interface RealtimeKitClientParams {
|
|
3
|
+
resetOnLeave?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Hook which manages a RealtimeKitClient instance
|
|
7
|
+
*/
|
|
8
|
+
export declare const useRealtimeKitClient: (clientParams?: RealtimeKitClientParams) => readonly [RTKClient | undefined, (options: RTKConfigOptions) => Promise<RTKClient | undefined>];
|
|
9
|
+
type StateSelector<T extends object, U> = (state: T) => U;
|
|
10
|
+
/**
|
|
11
|
+
* Hook which extracts data from the RealtimeKitClient object
|
|
12
|
+
*/
|
|
13
|
+
export declare const useRealtimeKitSelector: <StateSlice>(selector: StateSelector<RTKClient, StateSlice>) => StateSlice;
|
|
14
|
+
export * from './context';
|