@tellescope/video-chat 1.3.26 → 1.3.28
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/lib/cjs/index.d.ts +1 -2
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +1 -5
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/index.native.d.ts +1 -2
- package/lib/cjs/index.native.d.ts.map +1 -1
- package/lib/cjs/index.native.js +2 -5
- package/lib/cjs/index.native.js.map +1 -1
- package/lib/cjs/video.d.ts +18 -11
- package/lib/cjs/video.d.ts.map +1 -1
- package/lib/cjs/video.js +106 -39
- package/lib/cjs/video.js.map +1 -1
- package/lib/cjs/video.native.d.ts +6 -2
- package/lib/cjs/video.native.d.ts.map +1 -1
- package/lib/cjs/video.native.js +123 -18
- package/lib/cjs/video.native.js.map +1 -1
- package/lib/cjs/video_shared.d.ts +18 -2
- package/lib/cjs/video_shared.d.ts.map +1 -1
- package/lib/cjs/video_shared.js +114 -4
- package/lib/cjs/video_shared.js.map +1 -1
- package/lib/esm/controls.d.ts +0 -1
- package/lib/esm/controls.d.ts.map +1 -1
- package/lib/esm/hooks.d.ts +1 -13
- package/lib/esm/hooks.d.ts.map +1 -1
- package/lib/esm/hooks.js +1 -137
- package/lib/esm/hooks.js.map +1 -1
- package/lib/esm/index.d.ts +1 -2
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +1 -2
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/index.native.d.ts +1 -2
- package/lib/esm/index.native.d.ts.map +1 -1
- package/lib/esm/index.native.js +1 -2
- package/lib/esm/index.native.js.map +1 -1
- package/lib/esm/video.d.ts +18 -11
- package/lib/esm/video.d.ts.map +1 -1
- package/lib/esm/video.js +103 -39
- package/lib/esm/video.js.map +1 -1
- package/lib/esm/video.native.d.ts +6 -2
- package/lib/esm/video.native.d.ts.map +1 -1
- package/lib/esm/video.native.js +122 -19
- package/lib/esm/video.native.js.map +1 -1
- package/lib/esm/video_shared.d.ts +18 -2
- package/lib/esm/video_shared.d.ts.map +1 -1
- package/lib/esm/video_shared.js +89 -1
- package/lib/esm/video_shared.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/index.native.ts +1 -5
- package/src/index.ts +1 -5
- package/src/video.native.tsx +124 -8
- package/src/video.tsx +106 -40
- package/src/video_shared.tsx +140 -2
- package/src/hooks.ts +0 -98
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/video-chat",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.28",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/cjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@mui/icons-material": "^5.0.1",
|
|
36
36
|
"@mui/material": "^5.0.2",
|
|
37
37
|
"@tellescope/constants": "^1.3.24",
|
|
38
|
-
"@tellescope/react-components": "^1.3.
|
|
38
|
+
"@tellescope/react-components": "^1.3.27",
|
|
39
39
|
"@tellescope/sdk": "^1.3.25",
|
|
40
40
|
"@tellescope/types-client": "^1.3.24",
|
|
41
41
|
"@tellescope/types-models": "^1.3.24",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
56
56
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "9a370854c801dd857e45a7662707bce4916a39e3",
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
}
|
package/src/index.native.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
export * from "./video"
|
|
2
|
-
export * from "./hooks"
|
|
3
2
|
export * from "./controls"
|
|
4
|
-
export
|
|
5
|
-
CurrentCallContext,
|
|
6
|
-
useCurrentCallContext,
|
|
7
|
-
} from "./video_shared"
|
|
3
|
+
export * from "./video_shared"
|
|
8
4
|
export {
|
|
9
5
|
RNVideoRenderView,
|
|
10
6
|
} from "./native/RNVideoRenderView"
|
package/src/index.ts
CHANGED
package/src/video.native.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
useResolvedSession,
|
|
20
20
|
useSession,
|
|
21
21
|
Flex,
|
|
22
|
+
useCalendarEvents,
|
|
22
23
|
} from "@tellescope/react-components"
|
|
23
24
|
import {
|
|
24
25
|
Button,
|
|
@@ -32,6 +33,7 @@ import {
|
|
|
32
33
|
VideoProps,
|
|
33
34
|
AttendeeDisplayInfo,
|
|
34
35
|
VideoViewProps,
|
|
36
|
+
VideoCallNativeProps,
|
|
35
37
|
} from "./video_shared"
|
|
36
38
|
import {
|
|
37
39
|
CurrentCallContext,
|
|
@@ -39,7 +41,11 @@ import {
|
|
|
39
41
|
getSDKEventEmitter,
|
|
40
42
|
MobileSDKEvent,
|
|
41
43
|
NativeFunction,
|
|
44
|
+
ControlBar,
|
|
42
45
|
} from "./index.native"
|
|
46
|
+
import { borderColor, borderRadius } from "@mui/system"
|
|
47
|
+
import { ConstructionOutlined } from "@mui/icons-material"
|
|
48
|
+
// import RNSwitchAudioOutput from 'react-native-switch-audio-output';
|
|
43
49
|
|
|
44
50
|
interface TileState {
|
|
45
51
|
isLocal: boolean,
|
|
@@ -60,6 +66,17 @@ export const WithVideo = ({ children } : VideoProps) => {
|
|
|
60
66
|
const [screenShareTile, setScreenShareTile] = useState(null as number | null)
|
|
61
67
|
const [attendees, setAttendees] = useState ([] as AttendeeDisplayInfo[])
|
|
62
68
|
|
|
69
|
+
const resetState = () => {
|
|
70
|
+
setMeeting(undefined)
|
|
71
|
+
setIsHost(false)
|
|
72
|
+
setMuted(false)
|
|
73
|
+
setVideoIsEnabled(false)
|
|
74
|
+
setVideoTiles([])
|
|
75
|
+
setLocalTileId(null)
|
|
76
|
+
setScreenShareTile(null)
|
|
77
|
+
setAttendees([])
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
const toggleVideo = async () => {
|
|
64
81
|
NativeFunction.setCameraOn(!videoIsEnabled)
|
|
65
82
|
if (videoIsEnabled) {
|
|
@@ -81,8 +98,7 @@ export const WithVideo = ({ children } : VideoProps) => {
|
|
|
81
98
|
|
|
82
99
|
// called when user clicks Leave Meeting or meeting is ended by host
|
|
83
100
|
const endSubscription = emitter.addListener(MobileSDKEvent.OnMeetingEnd, a => {
|
|
84
|
-
|
|
85
|
-
setIsLoading(false)
|
|
101
|
+
resetState()
|
|
86
102
|
});
|
|
87
103
|
|
|
88
104
|
const joinSubscription = emitter.addListener(MobileSDKEvent.OnAttendeesJoin, (added: { attendeeId: string, externalUserId: string }) => {
|
|
@@ -99,7 +115,7 @@ export const WithVideo = ({ children } : VideoProps) => {
|
|
|
99
115
|
});
|
|
100
116
|
|
|
101
117
|
const errorSubscription = emitter.addListener(MobileSDKEvent.OnError, (message) => {
|
|
102
|
-
console.error("SDK Error", message);
|
|
118
|
+
console.error("SDK Error in errorSubscription", message);
|
|
103
119
|
});
|
|
104
120
|
|
|
105
121
|
const muteSubscription = emitter.addListener(MobileSDKEvent.OnAttendeesMute, attendeeId => {
|
|
@@ -193,7 +209,7 @@ export const useStartVideoCall = (): StartVideoCallReturnType => {
|
|
|
193
209
|
setStarting(true)
|
|
194
210
|
try {
|
|
195
211
|
const { id, meeting, host } = await session.api.meetings.start_meeting({ attendees: initialAttendees })
|
|
196
|
-
NativeFunction.startMeeting(meeting.Meeting, host.info)
|
|
212
|
+
await NativeFunction.startMeeting(meeting.Meeting, host.info)
|
|
197
213
|
|
|
198
214
|
setMeeting(meeting.Meeting)
|
|
199
215
|
setIsHost(true)
|
|
@@ -236,20 +252,58 @@ export const useStartVideoCall = (): StartVideoCallReturnType => {
|
|
|
236
252
|
}
|
|
237
253
|
}
|
|
238
254
|
|
|
255
|
+
export const useStartAndJoinMeetingForCalendarEvent = (calendarEventId: string) => {
|
|
256
|
+
const session = useSession()
|
|
257
|
+
const [, { updateLocalElement: updateLocalEvent }] = useCalendarEvents()
|
|
258
|
+
|
|
259
|
+
const { setMeeting, setIsHost } = useContext(CurrentCallContext)
|
|
260
|
+
if (!(!!setMeeting && setIsHost)) {
|
|
261
|
+
throw new Error("Missing CurrentCallContext")
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const startAndJoinMeeting = useCallback(async () => {
|
|
265
|
+
const { meeting, host, id } = await session.api.meetings.start_meeting_for_event({ calendarEventId })
|
|
266
|
+
|
|
267
|
+
updateLocalEvent(calendarEventId, { meetingId: id } )
|
|
268
|
+
|
|
269
|
+
await NativeFunction.startMeeting(meeting.Meeting, host.info)
|
|
270
|
+
|
|
271
|
+
setMeeting(meeting.Meeting)
|
|
272
|
+
setIsHost(true)
|
|
273
|
+
}, [session, setMeeting, setIsHost, updateLocalEvent])
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
startAndJoinMeeting,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
239
280
|
export const useJoinVideoCall = (): JoinVideoCallReturnType => {
|
|
240
281
|
const session = useResolvedSession()
|
|
241
|
-
const { meeting, setMeeting, videoIsEnabled, toggleVideo } = React.useContext(CurrentCallContext)
|
|
282
|
+
const { meeting, setIsHost, setMeeting, videoIsEnabled, toggleVideo } = React.useContext(CurrentCallContext)
|
|
242
283
|
|
|
243
|
-
const joinMeeting = async (meetingInfo: string | { Meeting: MeetingInfo }, attendeeInfo
|
|
284
|
+
const joinMeeting = async (meetingInfo: string | { Meeting: MeetingInfo }, attendeeInfo?: { Attendee: AttendeeInfo }) => {
|
|
285
|
+
let isHost = false
|
|
244
286
|
if (typeof meetingInfo == 'string') {
|
|
245
287
|
const meetings = await session.api.meetings.my_meetings()
|
|
246
288
|
const meeting = meetings.find(m => m.id === meetingInfo)
|
|
247
289
|
meetingInfo = meeting?.meetingInfo as { Meeting: MeetingInfo }
|
|
248
290
|
attendeeInfo = { Attendee: meeting?.attendees.find?.(a => a.id === session.userInfo.id)?.info as AttendeeInfo}
|
|
291
|
+
|
|
292
|
+
if (attendeeInfo.Attendee.ExternalUserId === meeting?.creator) {
|
|
293
|
+
isHost = true
|
|
294
|
+
}
|
|
249
295
|
}
|
|
296
|
+
|
|
250
297
|
if (!meetingInfo || typeof meetingInfo === 'string' || !attendeeInfo) return
|
|
251
298
|
|
|
252
|
-
|
|
299
|
+
try {
|
|
300
|
+
await NativeFunction.startMeeting(meetingInfo.Meeting ?? meetingInfo, attendeeInfo.Attendee ?? attendeeInfo)
|
|
301
|
+
} catch(err) {
|
|
302
|
+
await NativeFunction.startMeeting(meetingInfo.Meeting ?? meetingInfo, attendeeInfo.Attendee ?? attendeeInfo)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
setMeeting(meetingInfo.Meeting ?? meetingInfo)
|
|
306
|
+
setIsHost(isHost)
|
|
253
307
|
}
|
|
254
308
|
|
|
255
309
|
const leaveMeeting = () => setMeeting(undefined)
|
|
@@ -371,5 +425,67 @@ const styles = StyleSheet.create({
|
|
|
371
425
|
});
|
|
372
426
|
|
|
373
427
|
export const LocalPreview = () => {
|
|
374
|
-
|
|
428
|
+
// console.error("LocalPreview unimplemented for Native")
|
|
429
|
+
return null
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const get_video_styles = (count: number): React.CSSProperties => {
|
|
433
|
+
return ({
|
|
434
|
+
borderWidth: '1px',
|
|
435
|
+
borderColor: '#888888',
|
|
436
|
+
borderRadius: 5,
|
|
437
|
+
padding: 5,
|
|
438
|
+
width: count === 1 ? '100%' : '50%',
|
|
439
|
+
maxHeight: (
|
|
440
|
+
count <= 4
|
|
441
|
+
? '50%'
|
|
442
|
+
: count <= 8
|
|
443
|
+
? '25%'
|
|
444
|
+
: '20%'
|
|
445
|
+
),
|
|
446
|
+
backgroundColor: '#bbbbbb'
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
export const VideoCallNative: React.JSXElementConstructor<VideoCallNativeProps> = ({
|
|
450
|
+
...props
|
|
451
|
+
}) => {
|
|
452
|
+
const remoteViews = useRemoteViews()
|
|
453
|
+
// RNSwitchAudioOutput.selectAudioOutput(RNSwitchAudioOutput.AUDIO_SPEAKER)
|
|
454
|
+
const selfView = (
|
|
455
|
+
<SelfView
|
|
456
|
+
// style={{
|
|
457
|
+
// position: 'absolute',
|
|
458
|
+
// zIndex: '100',
|
|
459
|
+
// borderRadius: 100,
|
|
460
|
+
// width: 130,
|
|
461
|
+
// height: 130,
|
|
462
|
+
// bottom: 120,
|
|
463
|
+
// right: 25
|
|
464
|
+
// }}
|
|
465
|
+
/>
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
const style = get_video_styles(remoteViews.length + 1)
|
|
469
|
+
|
|
470
|
+
return (
|
|
471
|
+
<Flex column flex={1} style={{ }}>
|
|
472
|
+
<Flex flex={1} style={{ marginBottom: 75 }}>
|
|
473
|
+
|
|
474
|
+
{remoteViews.map((view, i) => (
|
|
475
|
+
<Flex key={i} alignItems="center" justifyContent="center" style={style}>
|
|
476
|
+
{view}
|
|
477
|
+
</Flex>
|
|
478
|
+
))}
|
|
479
|
+
|
|
480
|
+
<Flex alignItems="center" justifyContent="center" style={style}>
|
|
481
|
+
{selfView}
|
|
482
|
+
</Flex>
|
|
483
|
+
|
|
484
|
+
</Flex>
|
|
485
|
+
|
|
486
|
+
<ControlBar {...props}
|
|
487
|
+
style={{ position: 'absolute', bottom: 20, width: '100%' }}
|
|
488
|
+
/>
|
|
489
|
+
</Flex>
|
|
490
|
+
)
|
|
375
491
|
}
|
package/src/video.tsx
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import React, { useCallback, useState, useEffect } from "react"
|
|
1
|
+
import React, { useCallback, useContext, useState, useEffect } from "react"
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
useResolvedSession,
|
|
5
5
|
useSession,
|
|
6
6
|
Styled,
|
|
7
|
-
UserAndEnduserSelectorProps,
|
|
8
|
-
useMeetings,
|
|
9
|
-
UserAndEnduserSelector,
|
|
10
7
|
} from "@tellescope/react-components"
|
|
11
8
|
|
|
12
9
|
import {
|
|
13
|
-
APIError,
|
|
14
10
|
UserIdentity,
|
|
15
11
|
} from "@tellescope/types-utilities"
|
|
16
12
|
import {
|
|
17
13
|
AttendeeInfo,
|
|
14
|
+
Meeting,
|
|
18
15
|
MeetingInfo,
|
|
19
16
|
} from '@tellescope/types-models'
|
|
20
17
|
|
|
@@ -43,7 +40,8 @@ import {
|
|
|
43
40
|
// useContentShareControls, // screen sharing
|
|
44
41
|
} from 'amazon-chime-sdk-component-library-react';
|
|
45
42
|
import {
|
|
46
|
-
CurrentCallContext,
|
|
43
|
+
CurrentCallContext,
|
|
44
|
+
VideoCallNativeProps,
|
|
47
45
|
} from "./index"
|
|
48
46
|
import {
|
|
49
47
|
AttendeeDisplayInfo,
|
|
@@ -54,7 +52,103 @@ import {
|
|
|
54
52
|
JoinVideoCallProps,
|
|
55
53
|
} from "./video_shared"
|
|
56
54
|
import { ConsoleLogger, DefaultDeviceController, Logger, LogLevel } from "amazon-chime-sdk-js";
|
|
57
|
-
|
|
55
|
+
|
|
56
|
+
import { useCalendarEvents } from "@tellescope/react-components"
|
|
57
|
+
import { useCurrentCallContext } from "./index"
|
|
58
|
+
import { CalendarEvent } from "@tellescope/types-client";
|
|
59
|
+
|
|
60
|
+
export const useMeetingForCalendarEvent = (event: CalendarEvent) => {
|
|
61
|
+
const { meeting } = useCurrentCallContext()
|
|
62
|
+
const { startAndJoinMeeting } = useStartAndJoinMeetingForCalendarEvent(event.id)
|
|
63
|
+
const { joinMeeting } = useJoinMeeting()
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
startAndJoinMeeting,
|
|
67
|
+
joinMeeting,
|
|
68
|
+
meeting,
|
|
69
|
+
meetingStatus: (
|
|
70
|
+
!event.enableVideoCall
|
|
71
|
+
? 'disabled'
|
|
72
|
+
: !(event.meetingId)
|
|
73
|
+
? 'waiting-room'
|
|
74
|
+
: !!meeting
|
|
75
|
+
? 'joined'
|
|
76
|
+
: 'loading'
|
|
77
|
+
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const useStartAndJoinMeetingForCalendarEvent = (calendarEventId: string) => {
|
|
83
|
+
const session = useSession()
|
|
84
|
+
const [, { updateLocalElement: updateLocalEvent }] = useCalendarEvents()
|
|
85
|
+
|
|
86
|
+
const meetingManager = useMeetingManager();
|
|
87
|
+
|
|
88
|
+
const { setMeeting, setIsHost } = useContext(CurrentCallContext)
|
|
89
|
+
if (!(!!setMeeting && setIsHost)) {
|
|
90
|
+
throw new Error("Missing CurrentCallContext")
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const startAndJoinMeeting = useCallback(async () => {
|
|
94
|
+
const { meeting, host, id } = await session.api.meetings.start_meeting_for_event({ calendarEventId })
|
|
95
|
+
|
|
96
|
+
updateLocalEvent(calendarEventId, { meetingId: id } )
|
|
97
|
+
|
|
98
|
+
setMeeting(meeting as any)
|
|
99
|
+
|
|
100
|
+
await meetingManager.join({ meetingInfo: meeting, attendeeInfo: host.info }); // Use the join API to create a meeting session
|
|
101
|
+
await meetingManager.start(); // At this point you can let users setup their devices, or start the session immediately
|
|
102
|
+
|
|
103
|
+
setMeeting(meeting.Meeting)
|
|
104
|
+
setIsHost(true)
|
|
105
|
+
}, [session, calendarEventId, updateLocalEvent, meetingManager, setMeeting, setIsHost])
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
startAndJoinMeeting,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const useJoinMeeting = () => {
|
|
113
|
+
const session = useResolvedSession()
|
|
114
|
+
const meetingManager = useMeetingManager();
|
|
115
|
+
|
|
116
|
+
const { setMeeting, setIsHost } = useContext(CurrentCallContext)
|
|
117
|
+
if (!(!!setMeeting && setIsHost)) {
|
|
118
|
+
throw new Error("Missing CurrentCallContext")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const joinMeeting = useCallback(async (meeting: Meeting) => {
|
|
122
|
+
setMeeting(meeting.meetingInfo.Meeting)
|
|
123
|
+
setIsHost(meeting.creator === session.userInfo.id)
|
|
124
|
+
|
|
125
|
+
let attendeeInfo = meeting.attendees.find(a => a.id === session.userInfo.id)?.info
|
|
126
|
+
if (!attendeeInfo) {
|
|
127
|
+
const calendarEventId = meeting.calendarEventId
|
|
128
|
+
if (calendarEventId) {
|
|
129
|
+
const result = await session.api.meetings.join_meeting_for_event({ calendarEventId })
|
|
130
|
+
|
|
131
|
+
attendeeInfo = result.attendee.info
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!attendeeInfo) {
|
|
135
|
+
console.error("Could not find attendee info for joining meeting and failed to join")
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await meetingManager.join({
|
|
141
|
+
meetingInfo: meeting.meetingInfo,
|
|
142
|
+
attendeeInfo,
|
|
143
|
+
}); // Use the join API to create a meeting session
|
|
144
|
+
await meetingManager.start(); // At this point you can let users setup their devices, or start the session immediately
|
|
145
|
+
|
|
146
|
+
}, [setMeeting, meetingManager, setMeeting, setIsHost])
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
joinMeeting,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
58
152
|
|
|
59
153
|
const WithContext = ({ children } : { children: React.ReactNode }) => {
|
|
60
154
|
const [meeting, setMeeting] = useState(undefined as MeetingInfo | undefined)
|
|
@@ -155,7 +249,7 @@ export const useJoinVideoCall = (props?: JoinVideoCallProps): JoinVideoCallRetur
|
|
|
155
249
|
const status = useMeetingStatus()
|
|
156
250
|
|
|
157
251
|
// meetingInfo may be meetingId as string
|
|
158
|
-
const joinMeeting = async (meetingInfo: string | { Meeting: MeetingInfo }, attendeeInfo
|
|
252
|
+
const joinMeeting = async (meetingInfo: string | { Meeting: MeetingInfo }, attendeeInfo?: { Attendee: AttendeeInfo }) => {
|
|
159
253
|
if (typeof meetingInfo == 'string') {
|
|
160
254
|
const meetings = await session.api.meetings.my_meetings()
|
|
161
255
|
const meeting = meetings.find(m => m.id === meetingInfo)
|
|
@@ -271,35 +365,7 @@ export const LocalPreview = ({ style=defaultPreviewStyle }: Styled) => {
|
|
|
271
365
|
}
|
|
272
366
|
export { VideoTileGrid }
|
|
273
367
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
export const CreateMeeting = ({
|
|
280
|
-
roomTitle: defaultRoomTitle = "Group Chat",
|
|
281
|
-
onSuccess, onError,
|
|
282
|
-
...props
|
|
283
|
-
} : CreateMeetingProps) => {
|
|
284
|
-
const session = useSession()
|
|
285
|
-
|
|
286
|
-
const handleCreateRoom = useCallback(({ users, endusers }: { users: User[], endusers: Enduser[] }) => {
|
|
287
|
-
const userIds = users.map(u => u.id)
|
|
288
|
-
const enduserIds = endusers.map(e => e.id)
|
|
289
|
-
|
|
290
|
-
session.api.meetings.start_meeting({
|
|
291
|
-
attendees: ([
|
|
292
|
-
...userIds.map(id => ({ type: 'user', id } as UserIdentity)),
|
|
293
|
-
...enduserIds.map(id => ({ type: 'enduser', id } as UserIdentity)),
|
|
294
|
-
])
|
|
295
|
-
})
|
|
296
|
-
.then(r => {
|
|
297
|
-
onSuccess?.(r)
|
|
298
|
-
})
|
|
299
|
-
.catch(onError)
|
|
300
|
-
}, [session, onSuccess, onError])
|
|
301
|
-
|
|
302
|
-
return (
|
|
303
|
-
<UserAndEnduserSelector {...props} onSelect={handleCreateRoom} />
|
|
304
|
-
)
|
|
305
|
-
}
|
|
368
|
+
// unimplemented for web
|
|
369
|
+
export const VideoCallNative: React.JSXElementConstructor<VideoCallNativeProps> = () => (
|
|
370
|
+
null
|
|
371
|
+
)
|
package/src/video_shared.tsx
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
|
-
import React, { CSSProperties } from "react"
|
|
1
|
+
import React, { CSSProperties, useCallback, useEffect, useState } from "react"
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
AttendeeInfo,
|
|
5
5
|
MeetingInfo,
|
|
6
6
|
} from '@tellescope/types-models'
|
|
7
|
+
import {
|
|
8
|
+
CalendarEvent,
|
|
9
|
+
Enduser,
|
|
10
|
+
User,
|
|
11
|
+
} from '@tellescope/types-client'
|
|
7
12
|
|
|
8
13
|
import {
|
|
14
|
+
APIError,
|
|
9
15
|
UserIdentity,
|
|
10
16
|
} from '@tellescope/types-utilities'
|
|
11
17
|
|
|
18
|
+
import {
|
|
19
|
+
UserAndEnduserSelectorProps,
|
|
20
|
+
useMeetings,
|
|
21
|
+
UserAndEnduserSelector,
|
|
22
|
+
useCalendarEvents,
|
|
23
|
+
Flex,
|
|
24
|
+
Typography,
|
|
25
|
+
LoadingButton,
|
|
26
|
+
useSession,
|
|
27
|
+
useResolvedSession,
|
|
28
|
+
Button,
|
|
29
|
+
} from "@tellescope/react-components"
|
|
30
|
+
import { LocalPreview, useJoinVideoCall, useStartAndJoinMeetingForCalendarEvent } from "./video"
|
|
31
|
+
|
|
12
32
|
export type AttendeeDisplayInfo = { attendeeId: string, externalUserId: string }
|
|
13
33
|
export interface CallContext {
|
|
14
34
|
meeting: MeetingInfo | undefined, setMeeting: (m: MeetingInfo | undefined) => void,
|
|
@@ -43,7 +63,7 @@ export interface JoinVideoCallReturnType {
|
|
|
43
63
|
videoIsEnabled: CallContext['videoIsEnabled'],
|
|
44
64
|
toggleVideo: CallContext['toggleVideo'],
|
|
45
65
|
leaveMeeting: () => void,
|
|
46
|
-
joinMeeting: (meetingInfo: { Meeting: MeetingInfo } | string, attendeeInfo
|
|
66
|
+
joinMeeting: (meetingInfo: { Meeting: MeetingInfo } | string, attendeeInfo?: { Attendee: AttendeeInfo }) => Promise<void>,
|
|
47
67
|
}
|
|
48
68
|
|
|
49
69
|
export interface StartVideoCallReturnType {
|
|
@@ -55,4 +75,122 @@ export interface StartVideoCallReturnType {
|
|
|
55
75
|
createAndStartMeeting: (initialAttendees?: UserIdentity[]) => Promise<string>,
|
|
56
76
|
addAttendees: (attendees: UserIdentity[]) => Promise<void>,
|
|
57
77
|
endMeeting: () => Promise<void>,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface CreateCalendarEventForAttendeesProps extends Omit<UserAndEnduserSelectorProps, 'onSelect'> {
|
|
81
|
+
onSuccess?: (c: CalendarEvent) => void,
|
|
82
|
+
onError?: (e: APIError) => void,
|
|
83
|
+
eventProps?: Pick<CalendarEvent, 'startTimeInMS' | 'durationInMinutes' | 'title'>
|
|
84
|
+
}
|
|
85
|
+
export const CreateCalendarEventForAttendees = ({
|
|
86
|
+
onSuccess,
|
|
87
|
+
onError,
|
|
88
|
+
eventProps,
|
|
89
|
+
...props
|
|
90
|
+
} : CreateCalendarEventForAttendeesProps) => {
|
|
91
|
+
const [, { createElement: createEvent }] = useCalendarEvents({ dontFetch: true })
|
|
92
|
+
const [eventTitle, /*setEventTitle*/] = useState(eventProps?.title ?? 'Video Call')
|
|
93
|
+
const session = useResolvedSession()
|
|
94
|
+
|
|
95
|
+
const handleCreateRoom = useCallback(({ users, endusers }: { users: User[], endusers: Enduser[] }) => {
|
|
96
|
+
const userIds = users.map(u => u.id)
|
|
97
|
+
const enduserIds = endusers.map(e => e.id)
|
|
98
|
+
|
|
99
|
+
createEvent({
|
|
100
|
+
title: eventTitle,
|
|
101
|
+
attendees: ([
|
|
102
|
+
...userIds.map(id => ({ type: 'user', id } as UserIdentity)),
|
|
103
|
+
...enduserIds.map(id => ({ type: 'enduser', id } as UserIdentity)),
|
|
104
|
+
]),
|
|
105
|
+
startTimeInMS: eventProps?.startTimeInMS ?? Date.now(),
|
|
106
|
+
durationInMinutes: eventProps?.durationInMinutes || 30,
|
|
107
|
+
enableVideoCall: true,
|
|
108
|
+
})
|
|
109
|
+
.then(r => {
|
|
110
|
+
onSuccess?.(r)
|
|
111
|
+
})
|
|
112
|
+
.catch(onError)
|
|
113
|
+
}, [session, onSuccess, onError])
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<UserAndEnduserSelector {...props} onSelect={handleCreateRoom} />
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type WaitingRoomProps = {
|
|
121
|
+
calendarEvent: CalendarEvent,
|
|
122
|
+
onGoBack?: () => void,
|
|
123
|
+
}
|
|
124
|
+
export const WaitingRoom = ({ calendarEvent, onGoBack } : WaitingRoomProps) => {
|
|
125
|
+
const session = useSession()
|
|
126
|
+
const [, { findById: findEvent }] = useCalendarEvents()
|
|
127
|
+
const [, { findById: findMeeting }] = useMeetings()
|
|
128
|
+
const { startAndJoinMeeting } = useStartAndJoinMeetingForCalendarEvent(calendarEvent.id)
|
|
129
|
+
const { joinMeeting } = useJoinVideoCall()
|
|
130
|
+
|
|
131
|
+
const tsMeeting = findMeeting(calendarEvent?.meetingId ?? '')
|
|
132
|
+
|
|
133
|
+
// poll to check for started meeting
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
const t = setInterval(() => {
|
|
136
|
+
const event = findEvent(calendarEvent.id, { reload: true })
|
|
137
|
+
findMeeting(event?.meetingId ?? '', { reload: true }) // reloads tsMeeting
|
|
138
|
+
}, 2500)
|
|
139
|
+
|
|
140
|
+
return () => { clearInterval(t) }
|
|
141
|
+
}, [calendarEvent, findMeeting, findEvent])
|
|
142
|
+
|
|
143
|
+
const meetingIsStarted = tsMeeting?.status === 'live'
|
|
144
|
+
const isHost = session.userInfo.id === calendarEvent?.creator
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<Flex flex={1} column alignItems="center" justifyContent="center">
|
|
148
|
+
<Typography style={{ fontSize: 20, fontWeight: 'bold' }}>
|
|
149
|
+
{calendarEvent.title}
|
|
150
|
+
</Typography>
|
|
151
|
+
|
|
152
|
+
<Typography style={{ marginBottom: 15 }}>
|
|
153
|
+
Waiting Room
|
|
154
|
+
</Typography>
|
|
155
|
+
|
|
156
|
+
<LocalPreview />
|
|
157
|
+
|
|
158
|
+
<LoadingButton variant="contained"
|
|
159
|
+
disabled={
|
|
160
|
+
(!meetingIsStarted && !isHost)
|
|
161
|
+
|| (meetingIsStarted && !tsMeeting)
|
|
162
|
+
}
|
|
163
|
+
onClick={
|
|
164
|
+
meetingIsStarted
|
|
165
|
+
? () => joinMeeting(tsMeeting.id)
|
|
166
|
+
: startAndJoinMeeting
|
|
167
|
+
}
|
|
168
|
+
submitText={
|
|
169
|
+
meetingIsStarted ? "Join Meeting" : "Start Meeting"
|
|
170
|
+
}
|
|
171
|
+
submittingText={
|
|
172
|
+
meetingIsStarted ? "Joining" : "Starting"
|
|
173
|
+
}
|
|
174
|
+
style={{
|
|
175
|
+
marginTop: 15,
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
{onGoBack &&
|
|
180
|
+
<Button onClick={onGoBack} style={{ width: '100%' }}>
|
|
181
|
+
Back
|
|
182
|
+
</Button>
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
{!meetingIsStarted && !isHost &&
|
|
186
|
+
<Typography style={{ marginTop: 5 }}>
|
|
187
|
+
Waiting for the host to start the meeting
|
|
188
|
+
</Typography>
|
|
189
|
+
}
|
|
190
|
+
</Flex>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface VideoCallNativeProps {
|
|
195
|
+
onLeave?: () => void,
|
|
58
196
|
}
|
package/src/hooks.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { useCallback, useContext } from "react"
|
|
2
|
-
import { useCalendarEvents, useResolvedSession, useSession } from "@tellescope/react-components"
|
|
3
|
-
import { useMeetingManager } from "amazon-chime-sdk-component-library-react"
|
|
4
|
-
import { CurrentCallContext, useCurrentCallContext } from "./index"
|
|
5
|
-
import { CalendarEvent, Meeting } from "@tellescope/types-client"
|
|
6
|
-
|
|
7
|
-
export const useStartAndJoinMeetingForCalendarEvent = (calendarEventId: string) => {
|
|
8
|
-
const session = useSession()
|
|
9
|
-
const [, { updateLocalElement: updateLocalEvent }] = useCalendarEvents()
|
|
10
|
-
|
|
11
|
-
const meetingManager = useMeetingManager();
|
|
12
|
-
|
|
13
|
-
const { setMeeting, setIsHost } = useContext(CurrentCallContext)
|
|
14
|
-
if (!(!!setMeeting && setIsHost)) {
|
|
15
|
-
throw new Error("Missing CurrentCallContext")
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const startAndJoinMeeting = useCallback(async () => {
|
|
19
|
-
const { meeting, host, id } = await session.api.meetings.start_meeting_for_event({ calendarEventId })
|
|
20
|
-
|
|
21
|
-
updateLocalEvent(calendarEventId, { meetingId: id } )
|
|
22
|
-
|
|
23
|
-
setMeeting(meeting as any)
|
|
24
|
-
|
|
25
|
-
await meetingManager.join({ meetingInfo: meeting, attendeeInfo: host.info }); // Use the join API to create a meeting session
|
|
26
|
-
await meetingManager.start(); // At this point you can let users setup their devices, or start the session immediately
|
|
27
|
-
|
|
28
|
-
setMeeting(meeting.Meeting)
|
|
29
|
-
setIsHost(true)
|
|
30
|
-
}, [session, calendarEventId, updateLocalEvent, meetingManager, setMeeting, setIsHost])
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
startAndJoinMeeting,
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const useJoinMeeting = () => {
|
|
38
|
-
const session = useResolvedSession()
|
|
39
|
-
const meetingManager = useMeetingManager();
|
|
40
|
-
|
|
41
|
-
const { setMeeting, setIsHost } = useContext(CurrentCallContext)
|
|
42
|
-
if (!(!!setMeeting && setIsHost)) {
|
|
43
|
-
throw new Error("Missing CurrentCallContext")
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const joinMeeting = useCallback(async (meeting: Meeting) => {
|
|
47
|
-
setMeeting(meeting.meetingInfo.Meeting)
|
|
48
|
-
setIsHost(meeting.creator === session.userInfo.id)
|
|
49
|
-
|
|
50
|
-
let attendeeInfo = meeting.attendees.find(a => a.id === session.userInfo.id)?.info
|
|
51
|
-
if (!attendeeInfo) {
|
|
52
|
-
const calendarEventId = meeting.calendarEventId
|
|
53
|
-
if (calendarEventId) {
|
|
54
|
-
const result = await session.api.meetings.join_meeting_for_event({ calendarEventId })
|
|
55
|
-
|
|
56
|
-
attendeeInfo = result.attendee.info
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!attendeeInfo) {
|
|
60
|
-
console.error("Could not find attendee info for joining meeting and failed to join")
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
await meetingManager.join({
|
|
66
|
-
meetingInfo: meeting.meetingInfo,
|
|
67
|
-
attendeeInfo,
|
|
68
|
-
}); // Use the join API to create a meeting session
|
|
69
|
-
await meetingManager.start(); // At this point you can let users setup their devices, or start the session immediately
|
|
70
|
-
|
|
71
|
-
}, [setMeeting, meetingManager, setMeeting, setIsHost])
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
joinMeeting,
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export const useMeetingForCalendarEvent = (event: CalendarEvent) => {
|
|
79
|
-
const { meeting } = useCurrentCallContext()
|
|
80
|
-
const { startAndJoinMeeting } = useStartAndJoinMeetingForCalendarEvent(event.id)
|
|
81
|
-
const { joinMeeting } = useJoinMeeting()
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
startAndJoinMeeting,
|
|
85
|
-
joinMeeting,
|
|
86
|
-
meeting,
|
|
87
|
-
meetingStatus: (
|
|
88
|
-
!event.enableVideoCall
|
|
89
|
-
? 'disabled'
|
|
90
|
-
: !(event.meetingId)
|
|
91
|
-
? 'waiting-room'
|
|
92
|
-
: !!meeting
|
|
93
|
-
? 'joined'
|
|
94
|
-
: 'loading'
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
}
|