@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.
- package/lib/cjs/controls.d.ts +18 -0
- package/lib/cjs/controls.d.ts.map +1 -0
- package/lib/cjs/controls.js +57 -0
- package/lib/cjs/controls.js.map +1 -0
- package/lib/cjs/index.d.ts +1 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/index.native.d.ts +1 -0
- package/lib/cjs/index.native.d.ts.map +1 -1
- package/lib/cjs/index.native.js +1 -0
- package/lib/cjs/index.native.js.map +1 -1
- package/lib/cjs/native/RNVideoRenderView.d.ts +2 -2
- package/lib/cjs/native/RNVideoRenderView.d.ts.map +1 -1
- package/lib/cjs/native/RNVideoRenderView.js.map +1 -1
- package/lib/cjs/video.d.ts +13 -3
- package/lib/cjs/video.d.ts.map +1 -1
- package/lib/cjs/video.js +38 -7
- package/lib/cjs/video.js.map +1 -1
- package/lib/cjs/video.native.d.ts +7 -1
- package/lib/cjs/video.native.d.ts.map +1 -1
- package/lib/cjs/video.native.js +89 -36
- package/lib/cjs/video.native.js.map +1 -1
- package/lib/esm/controls.d.ts +18 -0
- package/lib/esm/controls.d.ts.map +1 -0
- package/lib/esm/controls.js +46 -0
- package/lib/esm/controls.js.map +1 -0
- package/lib/esm/index.d.ts +1 -0
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +1 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/index.native.d.ts +1 -0
- package/lib/esm/index.native.d.ts.map +1 -1
- package/lib/esm/index.native.js +1 -0
- package/lib/esm/index.native.js.map +1 -1
- package/lib/esm/native/RNVideoRenderView.d.ts +2 -2
- package/lib/esm/native/RNVideoRenderView.d.ts.map +1 -1
- package/lib/esm/native/RNVideoRenderView.js.map +1 -1
- package/lib/esm/shared.d.ts +1 -0
- package/lib/esm/shared.d.ts.map +1 -0
- package/lib/esm/shared.js +2 -0
- package/lib/esm/shared.js.map +1 -0
- package/lib/esm/video.d.ts +13 -3
- package/lib/esm/video.d.ts.map +1 -1
- package/lib/esm/video.js +36 -8
- package/lib/esm/video.js.map +1 -1
- package/lib/esm/video.native.d.ts +7 -1
- package/lib/esm/video.native.d.ts.map +1 -1
- package/lib/esm/video.native.js +88 -37
- package/lib/esm/video.native.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/controls.tsx +86 -0
- package/src/index.native.ts +2 -1
- package/src/index.ts +2 -1
- package/src/native/RNVideoRenderView.tsx +1 -1
- package/src/video.native.tsx +122 -53
- 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.
|
|
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.
|
|
37
|
-
"@tellescope/react-components": "^0.0.
|
|
38
|
-
"@tellescope/sdk": "^0.0.
|
|
39
|
-
"@tellescope/types-client": "^0.0.
|
|
40
|
-
"@tellescope/types-models": "^0.0.
|
|
41
|
-
"@tellescope/types-utilities": "^0.0.
|
|
42
|
-
"@tellescope/utilities": "^0.0.
|
|
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.
|
|
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": "
|
|
57
|
+
"gitHead": "8019bedb3e6f90e2d26ee71ee989bd05fc1b7ee6",
|
|
58
58
|
"publishConfig": {
|
|
59
59
|
"access": "public"
|
|
60
60
|
}
|
package/src/controls.tsx
ADDED
|
@@ -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
|
+
}
|
package/src/index.native.ts
CHANGED
|
@@ -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:
|
|
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"
|
package/src/video.native.tsx
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import React, { useCallback, useContext, useEffect, useState } from "react"
|
|
10
|
-
import { View,
|
|
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 {
|
|
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
|
|
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
|
|
43
|
-
const [
|
|
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 = () =>
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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={{
|
|
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 {
|
|
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
|
-
|
|
259
|
+
toggleVideo,
|
|
184
260
|
} = useContext(CurrentCallContext)
|
|
185
261
|
|
|
186
262
|
return (
|
|
187
|
-
<
|
|
188
|
-
<
|
|
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
|
-
) : <
|
|
268
|
+
) : <Typography style={styles.subtitle}>No one is sharing video at this moment</Typography>
|
|
204
269
|
}
|
|
205
|
-
</
|
|
270
|
+
</Flex>
|
|
271
|
+
|
|
272
|
+
{/*
|
|
206
273
|
{
|
|
207
274
|
!!shareScreenId &&
|
|
208
275
|
<React.Fragment>
|
|
209
|
-
<
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
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
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
|
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={{
|
|
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 }
|