@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.
Files changed (54) hide show
  1. package/lib/cjs/index.d.ts +1 -2
  2. package/lib/cjs/index.d.ts.map +1 -1
  3. package/lib/cjs/index.js +1 -5
  4. package/lib/cjs/index.js.map +1 -1
  5. package/lib/cjs/index.native.d.ts +1 -2
  6. package/lib/cjs/index.native.d.ts.map +1 -1
  7. package/lib/cjs/index.native.js +2 -5
  8. package/lib/cjs/index.native.js.map +1 -1
  9. package/lib/cjs/video.d.ts +18 -11
  10. package/lib/cjs/video.d.ts.map +1 -1
  11. package/lib/cjs/video.js +106 -39
  12. package/lib/cjs/video.js.map +1 -1
  13. package/lib/cjs/video.native.d.ts +6 -2
  14. package/lib/cjs/video.native.d.ts.map +1 -1
  15. package/lib/cjs/video.native.js +123 -18
  16. package/lib/cjs/video.native.js.map +1 -1
  17. package/lib/cjs/video_shared.d.ts +18 -2
  18. package/lib/cjs/video_shared.d.ts.map +1 -1
  19. package/lib/cjs/video_shared.js +114 -4
  20. package/lib/cjs/video_shared.js.map +1 -1
  21. package/lib/esm/controls.d.ts +0 -1
  22. package/lib/esm/controls.d.ts.map +1 -1
  23. package/lib/esm/hooks.d.ts +1 -13
  24. package/lib/esm/hooks.d.ts.map +1 -1
  25. package/lib/esm/hooks.js +1 -137
  26. package/lib/esm/hooks.js.map +1 -1
  27. package/lib/esm/index.d.ts +1 -2
  28. package/lib/esm/index.d.ts.map +1 -1
  29. package/lib/esm/index.js +1 -2
  30. package/lib/esm/index.js.map +1 -1
  31. package/lib/esm/index.native.d.ts +1 -2
  32. package/lib/esm/index.native.d.ts.map +1 -1
  33. package/lib/esm/index.native.js +1 -2
  34. package/lib/esm/index.native.js.map +1 -1
  35. package/lib/esm/video.d.ts +18 -11
  36. package/lib/esm/video.d.ts.map +1 -1
  37. package/lib/esm/video.js +103 -39
  38. package/lib/esm/video.js.map +1 -1
  39. package/lib/esm/video.native.d.ts +6 -2
  40. package/lib/esm/video.native.d.ts.map +1 -1
  41. package/lib/esm/video.native.js +122 -19
  42. package/lib/esm/video.native.js.map +1 -1
  43. package/lib/esm/video_shared.d.ts +18 -2
  44. package/lib/esm/video_shared.d.ts.map +1 -1
  45. package/lib/esm/video_shared.js +89 -1
  46. package/lib/esm/video_shared.js.map +1 -1
  47. package/lib/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +3 -3
  49. package/src/index.native.ts +1 -5
  50. package/src/index.ts +1 -5
  51. package/src/video.native.tsx +124 -8
  52. package/src/video.tsx +106 -40
  53. package/src/video_shared.tsx +140 -2
  54. 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.26",
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.26",
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": "c4626933b58bb8f6fc07f8ba747c31377827218f",
58
+ "gitHead": "9a370854c801dd857e45a7662707bce4916a39e3",
59
59
  "publishConfig": {
60
60
  "access": "public"
61
61
  }
@@ -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
@@ -1,7 +1,3 @@
1
1
  export * from "./video"
2
2
  export * from "./controls"
3
- export * from "./hooks"
4
- export {
5
- CurrentCallContext,
6
- useCurrentCallContext,
7
- } from "./video_shared"
3
+ export * from "./video_shared"
@@ -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
- setInMeeting(false)
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: { Attendee: 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
- NativeFunction.startMeeting(meetingInfo.Meeting ?? meetingInfo, attendeeInfo)
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
- throw new Error("Unimplemented")
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
- import { Enduser, Meeting, User } from "@tellescope/types-client";
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: { Attendee: 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
- export interface CreateMeetingProps extends Omit<UserAndEnduserSelectorProps, 'onSelect'> {
275
- onSuccess?: ({ id } : { id: string }) => void,
276
- onError?: (e: APIError) => void,
277
- roomTitle?: string,
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
+ )
@@ -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: { Attendee: AttendeeInfo }) => Promise<void>,
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
- }