@tellescope/video-chat 0.0.14 → 0.0.18

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 (58) hide show
  1. package/lib/cjs/controls.d.ts +18 -0
  2. package/lib/cjs/controls.d.ts.map +1 -0
  3. package/lib/cjs/controls.js +57 -0
  4. package/lib/cjs/controls.js.map +1 -0
  5. package/lib/cjs/index.d.ts +1 -0
  6. package/lib/cjs/index.d.ts.map +1 -1
  7. package/lib/cjs/index.js +1 -0
  8. package/lib/cjs/index.js.map +1 -1
  9. package/lib/cjs/index.native.d.ts +1 -0
  10. package/lib/cjs/index.native.d.ts.map +1 -1
  11. package/lib/cjs/index.native.js +1 -0
  12. package/lib/cjs/index.native.js.map +1 -1
  13. package/lib/cjs/native/RNVideoRenderView.d.ts +2 -2
  14. package/lib/cjs/native/RNVideoRenderView.d.ts.map +1 -1
  15. package/lib/cjs/native/RNVideoRenderView.js.map +1 -1
  16. package/lib/cjs/video.d.ts +13 -3
  17. package/lib/cjs/video.d.ts.map +1 -1
  18. package/lib/cjs/video.js +38 -7
  19. package/lib/cjs/video.js.map +1 -1
  20. package/lib/cjs/video.native.d.ts +7 -1
  21. package/lib/cjs/video.native.d.ts.map +1 -1
  22. package/lib/cjs/video.native.js +89 -36
  23. package/lib/cjs/video.native.js.map +1 -1
  24. package/lib/esm/controls.d.ts +18 -0
  25. package/lib/esm/controls.d.ts.map +1 -0
  26. package/lib/esm/controls.js +46 -0
  27. package/lib/esm/controls.js.map +1 -0
  28. package/lib/esm/index.d.ts +1 -0
  29. package/lib/esm/index.d.ts.map +1 -1
  30. package/lib/esm/index.js +1 -0
  31. package/lib/esm/index.js.map +1 -1
  32. package/lib/esm/index.native.d.ts +1 -0
  33. package/lib/esm/index.native.d.ts.map +1 -1
  34. package/lib/esm/index.native.js +1 -0
  35. package/lib/esm/index.native.js.map +1 -1
  36. package/lib/esm/native/RNVideoRenderView.d.ts +2 -2
  37. package/lib/esm/native/RNVideoRenderView.d.ts.map +1 -1
  38. package/lib/esm/native/RNVideoRenderView.js.map +1 -1
  39. package/lib/esm/shared.d.ts +1 -0
  40. package/lib/esm/shared.d.ts.map +1 -0
  41. package/lib/esm/shared.js +2 -0
  42. package/lib/esm/shared.js.map +1 -0
  43. package/lib/esm/video.d.ts +13 -3
  44. package/lib/esm/video.d.ts.map +1 -1
  45. package/lib/esm/video.js +36 -8
  46. package/lib/esm/video.js.map +1 -1
  47. package/lib/esm/video.native.d.ts +7 -1
  48. package/lib/esm/video.native.d.ts.map +1 -1
  49. package/lib/esm/video.native.js +88 -37
  50. package/lib/esm/video.native.js.map +1 -1
  51. package/lib/tsconfig.tsbuildinfo +1 -1
  52. package/package.json +10 -10
  53. package/src/controls.tsx +86 -0
  54. package/src/index.native.ts +2 -1
  55. package/src/index.ts +2 -1
  56. package/src/native/RNVideoRenderView.tsx +1 -1
  57. package/src/video.native.tsx +122 -53
  58. package/src/video.tsx +45 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/video-chat",
3
- "version": "0.0.14",
3
+ "version": "0.0.18",
4
4
  "description": "",
5
5
  "main": "./lib/cjs/index.js",
6
6
  "module": "./lib/esm/index.js",
@@ -33,17 +33,17 @@
33
33
  "@fontsource/roboto": "^4.5.1",
34
34
  "@mui/icons-material": "^5.0.1",
35
35
  "@mui/material": "^5.0.2",
36
- "@tellescope/constants": "^0.0.14",
37
- "@tellescope/react-components": "^0.0.14",
38
- "@tellescope/sdk": "^0.0.14",
39
- "@tellescope/types-client": "^0.0.14",
40
- "@tellescope/types-models": "^0.0.14",
41
- "@tellescope/types-utilities": "^0.0.14",
42
- "@tellescope/utilities": "^0.0.14",
36
+ "@tellescope/constants": "^0.0.18",
37
+ "@tellescope/react-components": "^0.0.18",
38
+ "@tellescope/sdk": "^0.0.18",
39
+ "@tellescope/types-client": "^0.0.18",
40
+ "@tellescope/types-models": "^0.0.18",
41
+ "@tellescope/types-utilities": "^0.0.18",
42
+ "@tellescope/utilities": "^0.0.18",
43
43
  "@typescript-eslint/eslint-plugin": "^4.33.0",
44
44
  "@typescript-eslint/parser": "^4.33.0",
45
45
  "amazon-chime-sdk-component-library-react": "^2.12.0",
46
- "amazon-chime-sdk-js": "^2.23.0",
46
+ "amazon-chime-sdk-js": "^2.24.0",
47
47
  "eslint": "^7.32.0",
48
48
  "eslint-plugin-react": "^7.26.1",
49
49
  "nodemon": "^2.0.13",
@@ -54,7 +54,7 @@
54
54
  "react": "^17.0.2",
55
55
  "react-dom": "^17.0.2"
56
56
  },
57
- "gitHead": "b57352502a35237c417fa4d5f571f0bb0aa07be2",
57
+ "gitHead": "8019bedb3e6f90e2d26ee71ee989bd05fc1b7ee6",
58
58
  "publishConfig": {
59
59
  "access": "public"
60
60
  }
@@ -0,0 +1,86 @@
1
+ // components that work with web or native
2
+ import React from "react"
3
+
4
+ import {
5
+ VideoIcon,
6
+ VideoOffIcon,
7
+ MicrophoneIcon,
8
+ MicrophoneOffIcon,
9
+ CallEndIcon,
10
+
11
+ Paper,
12
+
13
+ Styled,
14
+ } from "@tellescope/react-components/lib/esm/mui"
15
+ import { LabeledIconButton } from "@tellescope/react-components/lib/esm/controls"
16
+ import { Flex } from "@tellescope/react-components/lib/esm/layout"
17
+ import {
18
+ CurrentCallContext,
19
+ useStartVideoCall,
20
+ } from "./video"
21
+
22
+ const DEFAULT_BUTTON_SIZE = 30
23
+ interface ButtonProps {
24
+ size?: number,
25
+ }
26
+ export const VideoToggle = ({ size=DEFAULT_BUTTON_SIZE } : ButtonProps) => {
27
+ const { toggleVideo, videoIsEnabled } = React.useContext(CurrentCallContext)
28
+
29
+ return (
30
+ <LabeledIconButton size={size} Icon={videoIsEnabled ? VideoIcon : VideoOffIcon} onClick={toggleVideo}
31
+ label={videoIsEnabled ? "Turn Camera Off" : "Turn Camera On"}
32
+ />
33
+ )
34
+ }
35
+
36
+ export const MicrophoneToggle = ({ size=DEFAULT_BUTTON_SIZE }: ButtonProps) => {
37
+ const { microphoneIsEnabled, toggleMicrophone } = React.useContext(CurrentCallContext)
38
+
39
+ return (
40
+ <LabeledIconButton size={size} Icon={microphoneIsEnabled ? MicrophoneIcon : MicrophoneOffIcon} onClick={toggleMicrophone}
41
+ label={microphoneIsEnabled ? "Turn Microphone Off" : "Turn Microphone On"}
42
+ />
43
+ )
44
+ }
45
+
46
+ // ends meeting if host, otherwise leaves meeting
47
+ export const EndMeeting = ({ size=DEFAULT_BUTTON_SIZE }: ButtonProps) => {
48
+ const { endMeeting } = useStartVideoCall()
49
+
50
+ return (
51
+ <LabeledIconButton size={size} Icon={CallEndIcon} onClick={endMeeting} label="End Meeting"/>
52
+ )
53
+ }
54
+
55
+ interface LeaveMeetingProps {
56
+ onLeave?: () => void,
57
+ }
58
+ export const LeaveMeeting = ({ onLeave, size=DEFAULT_BUTTON_SIZE } : LeaveMeetingProps & ButtonProps) => (
59
+ <LabeledIconButton size={size} Icon={CallEndIcon} onClick={onLeave} label="Leave Meeting"/>
60
+ )
61
+
62
+ interface ControlbarProps {
63
+ spacing?: number,
64
+ size?: number,
65
+
66
+ }
67
+ export const ControlBar = ({ onLeave, style, spacing=15, size } : ControlbarProps & LeaveMeetingProps & Styled) => {
68
+ const { isHost } = React.useContext(CurrentCallContext)
69
+ const itemStyle = { marginLeft: spacing, marginRight: spacing }
70
+
71
+ return (
72
+ <Flex flex={1} alignItems="center" justifyContent="center" style={style}>
73
+ <Paper elevation={5} style={{ display: 'flex', flexDirection: 'row', padding: spacing }}>
74
+ <Flex style={itemStyle}>
75
+ <VideoToggle size={size}/>
76
+ </Flex>
77
+ <Flex style={itemStyle}>
78
+ <MicrophoneToggle size={size}/>
79
+ </Flex>
80
+ <Flex style={itemStyle}>
81
+ {isHost ? <EndMeeting size={size}/> : <LeaveMeeting size={size} onLeave={onLeave}/>}
82
+ </Flex>
83
+ </Paper>
84
+ </Flex>
85
+ )
86
+ }
@@ -1 +1,2 @@
1
- export * from "./video"
1
+ export * from "./video"
2
+ export * from "./controls"
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
- export * from "./video";
1
+ export * from "./video";
2
+ export * from "./controls"
@@ -8,7 +8,7 @@ import React from 'react';
8
8
  import { requireNativeComponent, findNodeHandle, ViewStyle } from 'react-native';
9
9
  import { NativeFunction } from './bridge';
10
10
 
11
- export class RNVideoView extends React.Component<{ tileId: string | number, style: ViewStyle }> {
11
+ export class RNVideoView extends React.Component<{ tileId: number, style?: ViewStyle }> {
12
12
  componentDidMount() {
13
13
  // we need to delay the bind video
14
14
  // Because "componentDidMount" will be called "immediately after the initial rendering occurs"
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import React, { useCallback, useContext, useEffect, useState } from "react"
10
- import { View, Text, StyleSheet } from "react-native"
10
+ import { View, StyleSheet } from "react-native"
11
11
  import {
12
12
  AttendeeInfo,
13
13
  MeetingInfo,
@@ -16,14 +16,25 @@ import {
16
16
  UserIdentity,
17
17
  } from '@tellescope/types-utilities'
18
18
  import { useSession } from "@tellescope/react-components/lib/esm/authentication"
19
- import { Button } from "@tellescope/react-components/lib/esm/mui"
19
+ import { Flex } from "@tellescope/react-components/lib/esm/layout"
20
+ import {
21
+ Button,
22
+ Typography,
23
+ convert_CSS_to_RNStyles,
24
+
25
+ VideoIcon,
26
+ VideoOffIcon,
27
+ MicrophoneIcon,
28
+ MicrophoneOffIcon,
29
+ } from "@tellescope/react-components/lib/esm/mui.native"
20
30
 
21
31
  import {
22
32
  CurrentCallContext,
23
33
  JoinVideoCallReturnType,
24
34
  StartVideoCallReturnType,
25
35
  VideoProps,
26
- AttendeeDisplayInfo
36
+ AttendeeDisplayInfo,
37
+ VideoViewProps,
27
38
  } from "./video.js"
28
39
 
29
40
  import {
@@ -33,17 +44,38 @@ import {
33
44
  } from "./native/bridge"
34
45
  import { RNVideoView } from "./native/RNVideoRenderView"
35
46
 
36
- export const WithVideo = ({ children } : VideoProps ) => {
47
+ export { CurrentCallContext }
48
+
49
+ interface TileState {
50
+ isLocal: boolean,
51
+ isScreenShare: boolean,
52
+ tileId: number,
53
+ }
54
+
55
+ export const WithVideo = ({ children } : VideoProps) => {
37
56
  const [meeting, setMeeting] = useState(undefined as MeetingInfo | undefined)
57
+ const [isHost, setIsHost] = useState(false)
38
58
 
39
59
  const [inMeeting, setInMeeting] = useState(false)
40
60
  const [isLoading, setIsLoading] = useState(false)
61
+ const [muted, setMuted] = useState(false)
41
62
  const [videoIsEnabled, setVideoIsEnabled] = useState(false)
42
- const [videoTiles, setVideoTiles] = useState([] as string[])
43
- const [screenShareTile, setScreenShareTile] = useState('')
63
+ const [videoTiles, setVideoTiles] = useState([] as number[])
64
+ const [localTileId, setLocalTileId] = useState(null as number | null)
65
+ const [screenShareTile, setScreenShareTile] = useState(null as number | null)
44
66
  const [attendees, setAttendees] = useState ([] as AttendeeDisplayInfo[])
45
67
 
46
- const toggleVideo = () => NativeFunction.setCameraOn(!videoIsEnabled)
68
+ const toggleVideo = async () => {
69
+ NativeFunction.setCameraOn(!videoIsEnabled)
70
+ if (videoIsEnabled) {
71
+ setLocalTileId(null)
72
+ }
73
+ setVideoIsEnabled(v => !v)
74
+ }
75
+ const toggleMic = async () => {
76
+ NativeFunction.setMute(!muted)
77
+ setMuted(m => !m)
78
+ }
47
79
  const emitter = getSDKEventEmitter()
48
80
 
49
81
  useEffect(() => {
@@ -52,19 +84,22 @@ export const WithVideo = ({ children } : VideoProps ) => {
52
84
  setIsLoading(false)
53
85
  });
54
86
 
55
- const endSubscription = emitter.addListener(MobileSDKEvent.OnMeetingEnd, () => {
56
- setInMeeting(true)
87
+ // called when user clicks Leave Meeting or meeting is ended by host
88
+ const endSubscription = emitter.addListener(MobileSDKEvent.OnMeetingEnd, a => {
89
+ setInMeeting(false)
57
90
  setIsLoading(false)
58
91
  });
59
92
 
60
- const joinSubscription = emitter.addListener(MobileSDKEvent.OnAttendeesJoin, ({ attendeeId, externalUserId }) => {
93
+ const joinSubscription = emitter.addListener(MobileSDKEvent.OnAttendeesJoin, (added: { attendeeId: string, externalUserId: string }) => {
94
+ const { attendeeId, externalUserId } = added
61
95
  setAttendees(as => !as.find(a => a.attendeeId === attendeeId)
62
96
  ? [{ attendeeId, externalUserId, muted: false }, ...as]
63
97
  : as
64
98
  )
65
99
  });
66
100
 
67
- const leaveSubscription = emitter.addListener(MobileSDKEvent.OnAttendeesLeave, ({ attendeeId }) => {
101
+ const leaveSubscription = emitter.addListener(MobileSDKEvent.OnAttendeesLeave, (leaving: { attendeeId: string }) => {
102
+ const { attendeeId } = leaving
68
103
  setAttendees(as => as.filter(a => a.attendeeId !== attendeeId))
69
104
  });
70
105
 
@@ -80,25 +115,27 @@ export const WithVideo = ({ children } : VideoProps ) => {
80
115
  setAttendees(as => as.map(a => a.attendeeId === attendeeId ? { ...a, muted: false } : a))
81
116
  });
82
117
 
83
- const addVideoSubscription = emitter.addListener(MobileSDKEvent.OnAddVideoTile, (tileState) => {
118
+ const addVideoSubscription = emitter.addListener(MobileSDKEvent.OnAddVideoTile, (tileState: TileState) => {
84
119
  if (tileState.isScreenShare) {
85
120
  setScreenShareTile(tileState.tileId)
86
121
  return
87
122
  }
88
- setVideoTiles(v => [...v, tileState.titleId])
123
+ if (tileState.isLocal) {
124
+ setLocalTileId(tileState.tileId)
125
+ }
126
+ setVideoTiles(v => [...v, tileState.tileId])
89
127
  setVideoIsEnabled(v => tileState.isLocal ? true : v)
90
128
  });
91
129
 
92
- const removeVideoSubscription = emitter.addListener(MobileSDKEvent.OnRemoveVideoTile, (tileState) => {
130
+ const removeVideoSubscription = emitter.addListener(MobileSDKEvent.OnRemoveVideoTile, (tileState: TileState) => {
93
131
  if (tileState.isScreenShare) {
94
- setScreenShareTile('')
132
+ setScreenShareTile(null)
95
133
  return
96
134
  }
97
135
  setVideoTiles(vs => vs.filter(v => v !== tileState.tileId))
98
136
  setVideoIsEnabled(v => tileState.isLocal ? false : v)
99
137
  });
100
138
 
101
-
102
139
  return () => {
103
140
  startSubscription.remove();
104
141
  endSubscription.remove();
@@ -113,15 +150,46 @@ export const WithVideo = ({ children } : VideoProps ) => {
113
150
  }, [emitter])
114
151
 
115
152
  return (
116
- <CurrentCallContext.Provider value={{ attendees, videoTiles, meeting, shareScreenId: screenShareTile, setMeeting, videoIsEnabled, toggleVideo }}>
153
+ <CurrentCallContext.Provider value={{
154
+ isHost, setIsHost,
155
+ attendees,
156
+ localTileId,
157
+ videoTiles,
158
+ meeting,
159
+ shareScreenId: screenShareTile,
160
+ setMeeting,
161
+ videoIsEnabled,
162
+ toggleVideo,
163
+ microphoneIsEnabled: !muted,
164
+ toggleMicrophone: toggleMic,
165
+ }}>
117
166
  {children}
118
167
  </CurrentCallContext.Provider>
119
168
  )
120
169
  }
121
170
 
171
+
172
+ export const useRemoteViews = (props={} as { style: React.CSSProperties }) => {
173
+ const { localTileId, videoTiles } = React.useContext(CurrentCallContext)
174
+ const nonLocal = videoTiles.filter(v => v !== localTileId)
175
+
176
+ return nonLocal.map(tileId =>
177
+ <RNVideoView key={tileId} style={convert_CSS_to_RNStyles(props.style) ?? styles.video} tileId={tileId} />
178
+ )
179
+ }
180
+
181
+ export const SelfView = ({ style } : VideoViewProps) => {
182
+ const { localTileId } = React.useContext(CurrentCallContext)
183
+ if (localTileId === null) return null // localTileId may be zero, don't return null on simple falsey check
184
+
185
+ return (
186
+ <RNVideoView style={convert_CSS_to_RNStyles(style) ?? styles.video} tileId={localTileId}/>
187
+ )
188
+ }
189
+
122
190
  export const useStartVideoCall = (): StartVideoCallReturnType => {
123
191
  const session = useSession()
124
- const { meeting, setMeeting, videoIsEnabled, toggleVideo } = React.useContext(CurrentCallContext)
192
+ const { meeting, setMeeting, setIsHost, videoIsEnabled, toggleVideo } = React.useContext(CurrentCallContext)
125
193
 
126
194
  const [starting, setStarting] = useState(false)
127
195
  const [ending, setEnding] = useState(false)
@@ -137,6 +205,7 @@ export const useStartVideoCall = (): StartVideoCallReturnType => {
137
205
  NativeFunction.startMeeting(meeting.Meeting, host.Attendee)
138
206
 
139
207
  setMeeting(meeting.Meeting)
208
+ setIsHost(true)
140
209
  } catch(err) {
141
210
  console.error(err)
142
211
  }
@@ -162,7 +231,16 @@ export const useStartVideoCall = (): StartVideoCallReturnType => {
162
231
  setMeeting(undefined)
163
232
  }
164
233
 
165
- return { meeting, videoIsEnabled, starting, ending, toggleVideo, createAndStartMeeting, addAttendees, endMeeting }
234
+ return {
235
+ meeting,
236
+ videoIsEnabled,
237
+ starting,
238
+ ending,
239
+ toggleVideo,
240
+ createAndStartMeeting,
241
+ addAttendees,
242
+ endMeeting,
243
+ }
166
244
  }
167
245
 
168
246
  export const useJoinVideoCall = (): JoinVideoCallReturnType => {
@@ -174,66 +252,57 @@ export const useJoinVideoCall = (): JoinVideoCallReturnType => {
174
252
 
175
253
  return { meeting, videoIsEnabled, toggleVideo, joinMeeting }
176
254
  }
177
-
178
255
  export const VideoTileGrid = () => {
179
256
  const {
180
257
  // attendees,
181
- videoIsEnabled,
182
258
  videoTiles,
183
- shareScreenId
259
+ toggleVideo,
184
260
  } = useContext(CurrentCallContext)
185
261
 
186
262
  return (
187
- <View style={[styles.container, { justifyContent: 'flex-start' }]}>
188
- <Text style={styles.title}>TITLE</Text>
189
- <View style={styles.buttonContainer}>
190
- {/* <MuteButton muted={currentMuted} onPress={() => NativeFunction.setMute(!currentMuted) }/> */}
191
- {/* <CameraButton disabled={selfVideoEnabled} onPress={() => NativeFunction.setCameraOn(!videoIsEnabled)}/> */}
192
- <Button disabled={videoIsEnabled} onPress={() => NativeFunction.setCameraOn(!videoIsEnabled)}>
193
- Toggle Video
194
- </Button>
195
- {/* <HangOffButton onPress={() => NativeFunction.stopMeeting()} /> */}
196
- <Button onPress={() => NativeFunction.stopMeeting()}> Leave Meeting </Button>
197
- </View>
198
- <Text style={styles.title}>Video</Text>
199
- <View style={styles.videoContainer}>
263
+ <Flex column justifyContent="space-between" alignItems="center">
264
+ <Flex style={styles.videoContainer}>
200
265
  {
201
266
  videoTiles.length > 0 ? videoTiles.map(tileId =>
202
267
  <RNVideoView style={styles.video} key={tileId} tileId={tileId} />
203
- ) : <Text style={styles.subtitle}>No one is sharing video at this moment</Text>
268
+ ) : <Typography style={styles.subtitle}>No one is sharing video at this moment</Typography>
204
269
  }
205
- </View>
270
+ </Flex>
271
+
272
+ {/*
206
273
  {
207
274
  !!shareScreenId &&
208
275
  <React.Fragment>
209
- <Text style={styles.title}>Screen Share</Text>
276
+ <Typography style={styles.title}>Screen Share</Typography>
210
277
  <View style={styles.videoContainer}>
211
278
  <RNVideoView style={styles.screenShare} key={shareScreenId} tileId={shareScreenId} />
212
279
  </View>
213
280
  </React.Fragment>
214
- }
215
- <Text style={styles.title}>Attendee</Text>
281
+ }
282
+ */}
283
+
284
+ <Flex justifyContent="space-between" style={{ height: '5%' }}>
285
+ {/* <MuteButton muted={currentMuted} onPress={() => NativeFunction.setMute(!currentMuted) }/> */}
286
+ {/* <CameraButton disabled={selfVideoEnabled} onPress={() => NativeFunction.setCameraOn(!videoIsEnabled)}/> */}
287
+ <Button onPress={toggleVideo}>
288
+ Toggle Video
289
+ </Button>
290
+ {/* <HangOffButton onPress={() => NativeFunction.stopMeeting()} /> */}
291
+ <Button onPress={() => NativeFunction.stopMeeting()}> Leave Meeting </Button>
292
+ </Flex>
293
+
216
294
  {/* <FlatList
217
295
  style={styles.attendeeList}
218
296
  data={attendees}
219
297
  renderItem={({ item }) => <AttendeeItem attendeeName={attendeeNameMap[item] ? attendeeNameMap[item] : item} muted={this.state.mutedAttendee.includes(item)}/>}
220
298
  keyExtractor={(item) => item}
221
299
  /> */}
222
- </View>
300
+
301
+ </Flex>
223
302
  );
224
303
  }
225
304
 
226
305
  const styles = StyleSheet.create({
227
- container: {
228
- justifyContent: 'center',
229
- alignItems: 'center',
230
- height: '95%',
231
- backgroundColor: 'white'
232
- },
233
- buttonContainer: {
234
- flexDirection: 'row',
235
- justifyContent: 'space-between',
236
- },
237
306
  title: {
238
307
  fontSize: 30,
239
308
  fontWeight: '700'
@@ -247,6 +316,7 @@ const styles = StyleSheet.create({
247
316
  flexDirection: 'row',
248
317
  justifyContent: 'space-around',
249
318
  width: '100%',
319
+ height: '95%',
250
320
  // This is an existing React Native issue:
251
321
  // When you create a native android component
252
322
  // that use GLSurfaceView (We use this internally), the entire screen will
@@ -254,8 +324,7 @@ const styles = StyleSheet.create({
254
324
  overflow: 'hidden'
255
325
  },
256
326
  video: {
257
- width: '45%',
258
- margin: '1%',
327
+ width: '100%',
259
328
  aspectRatio: 16 / 9,
260
329
  },
261
330
  screenShare: {
package/src/video.tsx CHANGED
@@ -12,14 +12,6 @@ import {
12
12
  MeetingInfo,
13
13
  } from '@tellescope/types-models'
14
14
 
15
- import {
16
- user_display_name,
17
- } from "@tellescope/utilities"
18
-
19
- import {
20
- Session,
21
- EnduserSession,
22
- } from "@tellescope/sdk"
23
15
 
24
16
  import { ThemeProvider } from 'styled-components';
25
17
  import {
@@ -31,11 +23,15 @@ import {
31
23
  // VideoGrid,
32
24
  // VideoTile,
33
25
  // PreviewVideo,
34
- // RemoteVideo,
26
+ RemoteVideo,
27
+ useAttendeeAudioStatus,
28
+ LocalVideo,
35
29
  useLocalVideo,
36
30
  useMeetingManager,
37
31
  useRosterState,
38
- useRemoteVideoTileState
32
+ useRemoteVideoTileState,
33
+ VideoTile,
34
+ useToggleLocalMute,
39
35
  // useRemoteVideoTileState,
40
36
  // useContentShareControls, // screen sharing
41
37
  } from 'amazon-chime-sdk-component-library-react';
@@ -46,8 +42,11 @@ export type AttendeeDisplayInfo = { attendeeId: string, externalUserId: string
46
42
  export const CurrentCallContext = React.createContext({} as {
47
43
  meeting: MeetingInfo | undefined, setMeeting: (m: MeetingInfo | undefined) => void,
48
44
  videoIsEnabled: boolean, toggleVideo: () => Promise<void>,
49
- attendees: AttendeeDisplayInfo[], shareScreenId: string,
50
- videoTiles: (number | string)[],
45
+ microphoneIsEnabled: boolean, toggleMicrophone: () => Promise<void>,
46
+ attendees: AttendeeDisplayInfo[], shareScreenId: number | null,
47
+ localTileId: number | null,
48
+ isHost: boolean, setIsHost: (b: boolean) => void;
49
+ videoTiles: (number)[],
51
50
  })
52
51
  export interface VideoProps {
53
52
  children?: React.ReactNode,
@@ -55,10 +54,12 @@ export interface VideoProps {
55
54
  }
56
55
  const WithContext = ({ children } : { children: React.ReactNode }) => {
57
56
  const [meeting, setMeeting] = useState(undefined as MeetingInfo | undefined)
58
- const { toggleVideo, isVideoEnabled: videoIsEnabled } = useLocalVideo();
57
+ const [isHost, setIsHost] = useState(false)
58
+ const { toggleVideo, isVideoEnabled: videoIsEnabled, tileId: localTileId } = useLocalVideo();
59
59
  const { roster } = useRosterState()
60
60
  const { tileId } = useContentShareState()
61
61
  const { tiles } = useRemoteVideoTileState()
62
+ const { muted, toggleMute } = useToggleLocalMute()
62
63
 
63
64
  const attendees = [] as AttendeeDisplayInfo[]
64
65
  for (const attendeeId in roster) {
@@ -67,7 +68,19 @@ const WithContext = ({ children } : { children: React.ReactNode }) => {
67
68
  }
68
69
 
69
70
  return (
70
- <CurrentCallContext.Provider value={{ attendees, videoTiles: tiles, shareScreenId: tileId?.toString() ?? '', meeting, setMeeting, videoIsEnabled, toggleVideo }}>
71
+ <CurrentCallContext.Provider value={{
72
+ isHost, setIsHost,
73
+ attendees,
74
+ localTileId,
75
+ videoTiles: tiles,
76
+ shareScreenId: tileId,
77
+ meeting,
78
+ setMeeting,
79
+ videoIsEnabled,
80
+ toggleVideo,
81
+ microphoneIsEnabled: !muted,
82
+ toggleMicrophone: async () => toggleMute(),
83
+ }}>
71
84
  {children}
72
85
  </CurrentCallContext.Provider>
73
86
  )
@@ -85,9 +98,9 @@ export const WithVideo = ({ children, theme=darkTheme }: VideoProps) => (
85
98
  export const useStartVideoCall = () => {
86
99
  const [starting, setStarting] = useState(false)
87
100
  const [ending, setEnding] = useState(false)
88
- const { meeting, setMeeting, toggleVideo, videoIsEnabled } = React.useContext(CurrentCallContext)
101
+ const { meeting, setMeeting, toggleVideo, videoIsEnabled, setIsHost } = React.useContext(CurrentCallContext)
89
102
 
90
- const session = useSession()
103
+ const session = useSession() // meetings can only be started by users, not endusers (for now)
91
104
  const meetingManager = useMeetingManager();
92
105
 
93
106
  const createAndStartMeeting = async (initialAttendees?: UserIdentity[]) => {
@@ -104,6 +117,7 @@ export const useStartVideoCall = () => {
104
117
  }
105
118
 
106
119
  setMeeting(meeting.Meeting)
120
+ setIsHost(true)
107
121
  } catch(err) {
108
122
  console.error(err)
109
123
  }
@@ -147,4 +161,19 @@ export const useJoinVideoCall = () => {
147
161
  }
148
162
  export type JoinVideoCallReturnType = ReturnType<typeof useJoinVideoCall>
149
163
 
164
+ export interface VideoViewProps {
165
+ style?: CSSProperties,
166
+ }
167
+ export const SelfView = ({ style }: VideoViewProps) => <div style={style}><LocalVideo/></div>
168
+
169
+ export const useRemoteViews = (props={} as VideoViewProps) => {
170
+ const { localTileId, videoTiles } = React.useContext(CurrentCallContext)
171
+ const nonLocal = videoTiles.filter(v => v !== localTileId)
172
+
173
+ return nonLocal.map(tileId =>
174
+ <RemoteVideo key={tileId} style={props.style} tileId={tileId} />
175
+ )
176
+ }
177
+
178
+
150
179
  export { VideoTileGrid }