@tellescope/video-chat 0.0.15 → 0.0.19

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 +1 -1
  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 +12 -2
  17. package/lib/cjs/video.d.ts.map +1 -1
  18. package/lib/cjs/video.js +37 -5
  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 +61 -13
  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 +1 -1
  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 +12 -2
  44. package/lib/esm/video.d.ts.map +1 -1
  45. package/lib/esm/video.js +39 -5
  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 +58 -12
  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 +65 -7
  58. package/src/video.tsx +43 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/video-chat",
3
- "version": "0.0.15",
3
+ "version": "0.0.19",
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.15",
37
- "@tellescope/react-components": "^0.0.15",
38
- "@tellescope/sdk": "^0.0.15",
39
- "@tellescope/types-client": "^0.0.15",
40
- "@tellescope/types-models": "^0.0.15",
41
- "@tellescope/types-utilities": "^0.0.15",
42
- "@tellescope/utilities": "^0.0.15",
36
+ "@tellescope/constants": "^0.0.19",
37
+ "@tellescope/react-components": "^0.0.19",
38
+ "@tellescope/sdk": "^0.0.19",
39
+ "@tellescope/types-client": "^0.0.19",
40
+ "@tellescope/types-models": "^0.0.19",
41
+ "@tellescope/types-utilities": "^0.0.19",
42
+ "@tellescope/utilities": "^0.0.19",
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": "bafbe70fd6546fdbc894fd408ae071709d3c5b80",
57
+ "gitHead": "ddee8ca945d3daf7355a9eada1305666e56d7434",
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"
@@ -17,14 +17,24 @@ import {
17
17
  } from '@tellescope/types-utilities'
18
18
  import { useSession } from "@tellescope/react-components/lib/esm/authentication"
19
19
  import { Flex } from "@tellescope/react-components/lib/esm/layout"
20
- import { Button, Typography } from "@tellescope/react-components/lib/esm/mui"
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"
21
30
 
22
31
  import {
23
32
  CurrentCallContext,
24
33
  JoinVideoCallReturnType,
25
34
  StartVideoCallReturnType,
26
35
  VideoProps,
27
- AttendeeDisplayInfo
36
+ AttendeeDisplayInfo,
37
+ VideoViewProps,
28
38
  } from "./video.js"
29
39
 
30
40
  import {
@@ -34,26 +44,38 @@ import {
34
44
  } from "./native/bridge"
35
45
  import { RNVideoView } from "./native/RNVideoRenderView"
36
46
 
47
+ export { CurrentCallContext }
48
+
37
49
  interface TileState {
38
50
  isLocal: boolean,
39
51
  isScreenShare: boolean,
40
52
  tileId: number,
41
53
  }
42
54
 
43
- export const WithVideo = ({ children } : VideoProps ) => {
55
+ export const WithVideo = ({ children } : VideoProps) => {
44
56
  const [meeting, setMeeting] = useState(undefined as MeetingInfo | undefined)
57
+ const [isHost, setIsHost] = useState(false)
45
58
 
46
59
  const [inMeeting, setInMeeting] = useState(false)
47
60
  const [isLoading, setIsLoading] = useState(false)
61
+ const [muted, setMuted] = useState(false)
48
62
  const [videoIsEnabled, setVideoIsEnabled] = useState(false)
49
63
  const [videoTiles, setVideoTiles] = useState([] as number[])
64
+ const [localTileId, setLocalTileId] = useState(null as number | null)
50
65
  const [screenShareTile, setScreenShareTile] = useState(null as number | null)
51
66
  const [attendees, setAttendees] = useState ([] as AttendeeDisplayInfo[])
52
67
 
53
68
  const toggleVideo = async () => {
54
- console.log('toggling', NativeFunction.setCameraOn(!videoIsEnabled))
69
+ NativeFunction.setCameraOn(!videoIsEnabled)
70
+ if (videoIsEnabled) {
71
+ setLocalTileId(null)
72
+ }
55
73
  setVideoIsEnabled(v => !v)
56
74
  }
75
+ const toggleMic = async () => {
76
+ NativeFunction.setMute(!muted)
77
+ setMuted(m => !m)
78
+ }
57
79
  const emitter = getSDKEventEmitter()
58
80
 
59
81
  useEffect(() => {
@@ -98,6 +120,9 @@ export const WithVideo = ({ children } : VideoProps ) => {
98
120
  setScreenShareTile(tileState.tileId)
99
121
  return
100
122
  }
123
+ if (tileState.isLocal) {
124
+ setLocalTileId(tileState.tileId)
125
+ }
101
126
  setVideoTiles(v => [...v, tileState.tileId])
102
127
  setVideoIsEnabled(v => tileState.isLocal ? true : v)
103
128
  });
@@ -126,22 +151,45 @@ export const WithVideo = ({ children } : VideoProps ) => {
126
151
 
127
152
  return (
128
153
  <CurrentCallContext.Provider value={{
154
+ isHost, setIsHost,
129
155
  attendees,
156
+ localTileId,
130
157
  videoTiles,
131
158
  meeting,
132
159
  shareScreenId: screenShareTile,
133
160
  setMeeting,
134
161
  videoIsEnabled,
135
- toggleVideo
162
+ toggleVideo,
163
+ microphoneIsEnabled: !muted,
164
+ toggleMicrophone: toggleMic,
136
165
  }}>
137
166
  {children}
138
167
  </CurrentCallContext.Provider>
139
168
  )
140
169
  }
141
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
+
142
190
  export const useStartVideoCall = (): StartVideoCallReturnType => {
143
191
  const session = useSession()
144
- const { meeting, setMeeting, videoIsEnabled, toggleVideo } = React.useContext(CurrentCallContext)
192
+ const { meeting, setMeeting, setIsHost, videoIsEnabled, toggleVideo } = React.useContext(CurrentCallContext)
145
193
 
146
194
  const [starting, setStarting] = useState(false)
147
195
  const [ending, setEnding] = useState(false)
@@ -157,6 +205,7 @@ export const useStartVideoCall = (): StartVideoCallReturnType => {
157
205
  NativeFunction.startMeeting(meeting.Meeting, host.Attendee)
158
206
 
159
207
  setMeeting(meeting.Meeting)
208
+ setIsHost(true)
160
209
  } catch(err) {
161
210
  console.error(err)
162
211
  }
@@ -182,7 +231,16 @@ export const useStartVideoCall = (): StartVideoCallReturnType => {
182
231
  setMeeting(undefined)
183
232
  }
184
233
 
185
- 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
+ }
186
244
  }
187
245
 
188
246
  export const useJoinVideoCall = (): JoinVideoCallReturnType => {
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,12 +23,15 @@ import {
31
23
  // VideoGrid,
32
24
  // VideoTile,
33
25
  // PreviewVideo,
34
- // RemoteVideo,
26
+ RemoteVideo,
35
27
  useAttendeeAudioStatus,
28
+ LocalVideo,
36
29
  useLocalVideo,
37
30
  useMeetingManager,
38
31
  useRosterState,
39
- useRemoteVideoTileState
32
+ useRemoteVideoTileState,
33
+ VideoTile,
34
+ useToggleLocalMute,
40
35
  // useRemoteVideoTileState,
41
36
  // useContentShareControls, // screen sharing
42
37
  } from 'amazon-chime-sdk-component-library-react';
@@ -47,8 +42,11 @@ export type AttendeeDisplayInfo = { attendeeId: string, externalUserId: string
47
42
  export const CurrentCallContext = React.createContext({} as {
48
43
  meeting: MeetingInfo | undefined, setMeeting: (m: MeetingInfo | undefined) => void,
49
44
  videoIsEnabled: boolean, toggleVideo: () => Promise<void>,
45
+ microphoneIsEnabled: boolean, toggleMicrophone: () => Promise<void>,
50
46
  attendees: AttendeeDisplayInfo[], shareScreenId: number | null,
51
- videoTiles: (number | string)[],
47
+ localTileId: number | null,
48
+ isHost: boolean, setIsHost: (b: boolean) => void;
49
+ videoTiles: (number)[],
52
50
  })
53
51
  export interface VideoProps {
54
52
  children?: React.ReactNode,
@@ -56,10 +54,12 @@ export interface VideoProps {
56
54
  }
57
55
  const WithContext = ({ children } : { children: React.ReactNode }) => {
58
56
  const [meeting, setMeeting] = useState(undefined as MeetingInfo | undefined)
59
- const { toggleVideo, isVideoEnabled: videoIsEnabled } = useLocalVideo();
57
+ const [isHost, setIsHost] = useState(false)
58
+ const { toggleVideo, isVideoEnabled: videoIsEnabled, tileId: localTileId } = useLocalVideo();
60
59
  const { roster } = useRosterState()
61
60
  const { tileId } = useContentShareState()
62
61
  const { tiles } = useRemoteVideoTileState()
62
+ const { muted, toggleMute } = useToggleLocalMute()
63
63
 
64
64
  const attendees = [] as AttendeeDisplayInfo[]
65
65
  for (const attendeeId in roster) {
@@ -68,7 +68,19 @@ const WithContext = ({ children } : { children: React.ReactNode }) => {
68
68
  }
69
69
 
70
70
  return (
71
- <CurrentCallContext.Provider value={{ attendees, videoTiles: tiles, shareScreenId: tileId, 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
+ }}>
72
84
  {children}
73
85
  </CurrentCallContext.Provider>
74
86
  )
@@ -86,9 +98,9 @@ export const WithVideo = ({ children, theme=darkTheme }: VideoProps) => (
86
98
  export const useStartVideoCall = () => {
87
99
  const [starting, setStarting] = useState(false)
88
100
  const [ending, setEnding] = useState(false)
89
- const { meeting, setMeeting, toggleVideo, videoIsEnabled } = React.useContext(CurrentCallContext)
101
+ const { meeting, setMeeting, toggleVideo, videoIsEnabled, setIsHost } = React.useContext(CurrentCallContext)
90
102
 
91
- const session = useSession()
103
+ const session = useSession() // meetings can only be started by users, not endusers (for now)
92
104
  const meetingManager = useMeetingManager();
93
105
 
94
106
  const createAndStartMeeting = async (initialAttendees?: UserIdentity[]) => {
@@ -105,6 +117,7 @@ export const useStartVideoCall = () => {
105
117
  }
106
118
 
107
119
  setMeeting(meeting.Meeting)
120
+ setIsHost(true)
108
121
  } catch(err) {
109
122
  console.error(err)
110
123
  }
@@ -148,4 +161,19 @@ export const useJoinVideoCall = () => {
148
161
  }
149
162
  export type JoinVideoCallReturnType = ReturnType<typeof useJoinVideoCall>
150
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
+
151
179
  export { VideoTileGrid }