agora-appbuilder-core 2.0.1 → 2.0.3-rc2
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/Readme.md +2 -1
- package/package.json +1 -1
- package/template/_package-lock.json +8853 -8806
- package/template/agora-rn-uikit/.git/index +0 -0
- package/template/agora-rn-uikit/.git/logs/HEAD +2 -2
- package/template/agora-rn-uikit/.git/logs/refs/heads/ab-dev-auto +1 -1
- package/template/agora-rn-uikit/.git/logs/refs/heads/master +1 -1
- package/template/agora-rn-uikit/.git/logs/refs/remotes/origin/HEAD +1 -1
- package/template/agora-rn-uikit/.git/objects/pack/pack-2a0122bf5a3199f941e5a52535f43881623f752c.idx +0 -0
- package/template/agora-rn-uikit/.git/objects/pack/{pack-f379286d0537eb68377220b4929979324b8d5d1c.pack → pack-2a0122bf5a3199f941e5a52535f43881623f752c.pack} +0 -0
- package/template/agora-rn-uikit/.git/packed-refs +4 -2
- package/template/agora-rn-uikit/.git/refs/heads/ab-dev-auto +1 -1
- package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +21 -0
- package/template/agora-rn-uikit/src/Controls/BtnTemplate.tsx +22 -14
- package/template/agora-rn-uikit/src/Controls/ImageIcon.tsx +17 -13
- package/template/agora-rn-uikit/src/Controls/types.ts +4 -0
- package/template/agora-rn-uikit/src/Rtc/Create.tsx +117 -19
- package/template/agora-rn-uikit/src/RtcConfigure.tsx +24 -13
- package/template/{src/utils/isSafariBrowser.tsx → agora-rn-uikit/src/Utils/isSafariBrowser.ts} +3 -0
- package/template/{src → agora-rn-uikit/src}/hooks/useImageDelay.tsx +5 -2
- package/template/agora-rn-uikit/src/index.ts +2 -0
- package/template/bridge/rtc/webNg/RtcEngine.ts +73 -70
- package/template/bridge/rtc/webNg/Types.ts +59 -5
- package/template/bridge/rtm/web/Types.ts +13 -0
- package/template/bridge/rtm/web/index.ts +78 -1
- package/template/global.d.ts +2 -0
- package/template/package-lock.json +8853 -8806
- package/template/package.json +3 -4
- package/template/react-native-toast-message/src/components/base/styles.js +4 -2
- package/template/src/assets/icons.ts +41 -28
- package/template/src/components/Chat.tsx +70 -184
- package/template/src/components/ChatContext.ts +13 -2
- package/template/src/components/Controls.native.tsx +37 -76
- package/template/src/components/Controls.tsx +50 -158
- package/template/src/components/DeviceConfigure.native.tsx +5 -1
- package/template/src/components/DeviceConfigure.tsx +38 -20
- package/template/src/components/Navbar.tsx +185 -226
- package/template/src/components/ParticipantsView.tsx +176 -184
- package/template/src/components/Precall.native.tsx +83 -69
- package/template/src/components/Precall.tsx +174 -191
- package/template/src/components/RTMConfigure.tsx +264 -78
- package/template/src/components/SettingsView.tsx +9 -19
- package/template/src/components/livestream/LiveStreamContext.tsx +411 -0
- package/template/src/components/livestream/Types.ts +69 -0
- package/template/src/components/livestream/index.ts +9 -0
- package/template/src/components/livestream/views/LiveStreamControls.tsx +27 -0
- package/template/src/components/participants/AllAudienceParticipants.tsx +53 -0
- package/template/src/components/participants/AllHostParticipants.tsx +65 -0
- package/template/src/components/participants/MeParticipant.tsx +37 -0
- package/template/src/components/participants/ParticipantName.tsx +47 -0
- package/template/src/components/participants/ParticipantSectionTitle.tsx +29 -0
- package/template/src/components/participants/RemoteParticipants.tsx +69 -0
- package/template/src/components/participants/ScreenshareParticipants.tsx +28 -0
- package/template/src/components/participants/context/ParticipantContext.tsx +97 -0
- package/template/src/components/styles.ts +13 -0
- package/template/src/pages/Create.tsx +25 -14
- package/template/src/pages/VideoCall.tsx +197 -159
- package/template/src/subComponents/ChatBubble.tsx +4 -1
- package/template/src/subComponents/ChatContainer.tsx +44 -20
- package/template/src/subComponents/ChatInput.tsx +4 -12
- package/template/src/subComponents/Checkbox.native.tsx +6 -5
- package/template/src/subComponents/Checkbox.tsx +1 -0
- package/template/src/subComponents/Recording.tsx +23 -9
- package/template/src/subComponents/RemoteVideoMute.tsx +2 -3
- package/template/src/subComponents/SelectDevice.tsx +70 -35
- package/template/src/subComponents/chat/ChatParticipants.tsx +121 -0
- package/template/src/subComponents/livestream/ApprovedLiveStreamControlsView.tsx +23 -0
- package/template/src/subComponents/livestream/CurrentLiveStreamRequestsView.tsx +70 -0
- package/template/src/subComponents/livestream/controls/LocalRaiseHand.tsx +57 -0
- package/template/src/subComponents/livestream/controls/RemoteLiveStreamApprovedRequestRecall.tsx +24 -0
- package/template/src/subComponents/livestream/controls/RemoteLiveStreamRequestApprove.tsx +38 -0
- package/template/src/subComponents/livestream/controls/RemoteLiveStreamRequestReject.tsx +37 -0
- package/template/src/subComponents/livestream/index.ts +18 -0
- package/template/src/subComponents/screenshare/ScreenshareButton.tsx +80 -0
- package/template/src/subComponents/screenshare/ScreenshareConfigure.native.tsx +24 -0
- package/template/src/subComponents/screenshare/ScreenshareConfigure.tsx +200 -0
- package/template/src/subComponents/screenshare/ScreenshareContext.tsx +21 -0
- package/template/src/utils/index.tsx +48 -0
- package/template/agora-rn-uikit/.git/objects/pack/pack-f379286d0537eb68377220b4929979324b8d5d1c.idx +0 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import React, {createContext, useContext, useState, useRef} from 'react';
|
|
2
|
+
import ChatContext, {
|
|
3
|
+
controlMessageEnum,
|
|
4
|
+
messageChannelType,
|
|
5
|
+
attrRequestTypes,
|
|
6
|
+
} from '../ChatContext';
|
|
7
|
+
import Toast from '../../../react-native-toast-message';
|
|
8
|
+
import {
|
|
9
|
+
LiveStreamControlMessageEnum,
|
|
10
|
+
LSNotificationObject,
|
|
11
|
+
liveStreamContext,
|
|
12
|
+
requestStatus,
|
|
13
|
+
requestInterface,
|
|
14
|
+
attrRequestStatus,
|
|
15
|
+
attrRequestInterface,
|
|
16
|
+
liveStreamPropsInterface,
|
|
17
|
+
} from './Types';
|
|
18
|
+
import {ClientRole} from '../../../agora-rn-uikit';
|
|
19
|
+
import ScreenshareContext from '../../subComponents/screenshare/ScreenshareContext';
|
|
20
|
+
import {filterObject} from '../../utils';
|
|
21
|
+
|
|
22
|
+
const LiveStreamContext = createContext(null as unknown as liveStreamContext);
|
|
23
|
+
|
|
24
|
+
export const LiveStreamContextConsumer = LiveStreamContext.Consumer;
|
|
25
|
+
|
|
26
|
+
export const LiveStreamContextProvider = (props: liveStreamPropsInterface) => {
|
|
27
|
+
const screenshareContextInstance = useContext(ScreenshareContext);
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
userList,
|
|
31
|
+
localUid,
|
|
32
|
+
sendControlMessageToUid,
|
|
33
|
+
sendControlMessage,
|
|
34
|
+
broadcastUserAttributes,
|
|
35
|
+
addOrUpdateLocalUserAttributes,
|
|
36
|
+
events,
|
|
37
|
+
} = useContext(ChatContext);
|
|
38
|
+
|
|
39
|
+
const {isHost, setRtcProps} = props;
|
|
40
|
+
|
|
41
|
+
const [currLiveStreamRequest, setLiveStreamRequest] = useState<
|
|
42
|
+
Partial<Record<string, requestInterface>>
|
|
43
|
+
>({});
|
|
44
|
+
|
|
45
|
+
const [uidsOfInitialRequests, setUidsOfInitialRequests] = useState<
|
|
46
|
+
attrRequestInterface[]
|
|
47
|
+
>([]);
|
|
48
|
+
|
|
49
|
+
const [activeLiveStreamRequest, setActiveLiveStreamRequest] = useState<
|
|
50
|
+
Partial<Record<string, requestInterface>>
|
|
51
|
+
>({});
|
|
52
|
+
|
|
53
|
+
const [raiseHandRequestActive, setRaiseHandRequestActive] = useState(false);
|
|
54
|
+
|
|
55
|
+
const [lastCheckedRequestTimestamp, setLastCheckedRequestTimestamp] =
|
|
56
|
+
useState(0);
|
|
57
|
+
|
|
58
|
+
const [lastRequestReceivedTimestamp, setLastRequestReceivedTimestamp] =
|
|
59
|
+
useState(0);
|
|
60
|
+
|
|
61
|
+
const [isPendingRequestToReview, setPendingRequestToReview] = useState(false);
|
|
62
|
+
|
|
63
|
+
const localUserRef = useRef({uid: localUid, status: ''});
|
|
64
|
+
|
|
65
|
+
const showToast = (text: string) => {
|
|
66
|
+
Toast.show({
|
|
67
|
+
type: 'success',
|
|
68
|
+
text1: text,
|
|
69
|
+
visibilityTime: 1000,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const updateRtcProps = (newClientRole: ClientRole) => {
|
|
74
|
+
setRtcProps((prevState: any) => ({
|
|
75
|
+
...prevState,
|
|
76
|
+
role:
|
|
77
|
+
newClientRole === ClientRole.Audience
|
|
78
|
+
? ClientRole.Audience
|
|
79
|
+
: ClientRole.Broadcaster,
|
|
80
|
+
}));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const getAttendeeName = (uid: number | string) => {
|
|
84
|
+
return userList[uid] ? userList[uid]?.name : 'user';
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
setActiveLiveStreamRequest(
|
|
89
|
+
filterObject(
|
|
90
|
+
currLiveStreamRequest,
|
|
91
|
+
([k, v]) => v?.status === requestStatus.AwaitingAction,
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
}, [currLiveStreamRequest]);
|
|
95
|
+
|
|
96
|
+
React.useEffect(() => {
|
|
97
|
+
// Get the time timestamp of recent request
|
|
98
|
+
const recentRequest = Object.values(activeLiveStreamRequest).sort(
|
|
99
|
+
(a, b) => b?.ts - a?.ts || 0,
|
|
100
|
+
)[0]; // sorting in descending order and take the first request
|
|
101
|
+
|
|
102
|
+
if (recentRequest?.ts) {
|
|
103
|
+
setLastRequestReceivedTimestamp(recentRequest.ts);
|
|
104
|
+
}
|
|
105
|
+
if (Object.keys(activeLiveStreamRequest).length === 0) {
|
|
106
|
+
setPendingRequestToReview(false);
|
|
107
|
+
}
|
|
108
|
+
}, [activeLiveStreamRequest]);
|
|
109
|
+
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
if (
|
|
112
|
+
// If active requests and last seen is less than last message received
|
|
113
|
+
Object.keys(activeLiveStreamRequest).length !== 0 &&
|
|
114
|
+
lastRequestReceivedTimestamp >= lastCheckedRequestTimestamp
|
|
115
|
+
) {
|
|
116
|
+
setPendingRequestToReview(true);
|
|
117
|
+
} else {
|
|
118
|
+
setPendingRequestToReview(false);
|
|
119
|
+
}
|
|
120
|
+
}, [lastRequestReceivedTimestamp, lastCheckedRequestTimestamp]);
|
|
121
|
+
|
|
122
|
+
React.useEffect(() => {
|
|
123
|
+
// Remove requests for users who are offline
|
|
124
|
+
setLiveStreamRequest(
|
|
125
|
+
filterObject(
|
|
126
|
+
currLiveStreamRequest,
|
|
127
|
+
([k, v]) => userList[k] && !userList[k]?.offline,
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// Check attribute of user joined if it has request livestreaming attribute
|
|
132
|
+
const uidsOfUsersHavingLSRequest: attrRequestInterface[] = Object.keys(
|
|
133
|
+
filterObject(
|
|
134
|
+
userList,
|
|
135
|
+
([k, v]) =>
|
|
136
|
+
v?.requests === attrRequestStatus.RaiseHand_AwaitingAction ||
|
|
137
|
+
v?.requests === attrRequestStatus.RaiseHand_Approved,
|
|
138
|
+
),
|
|
139
|
+
).map((key) => ({
|
|
140
|
+
uid: key,
|
|
141
|
+
status:
|
|
142
|
+
userList[key]?.requests || attrRequestStatus.RaiseHand_AwaitingAction,
|
|
143
|
+
}));
|
|
144
|
+
|
|
145
|
+
// console.log('uidsOfUsersHavingLSRequest', uidsOfUsersHavingLSRequest);
|
|
146
|
+
// Set uids of user who have active live streaming request
|
|
147
|
+
setUidsOfInitialRequests([...uidsOfUsersHavingLSRequest]);
|
|
148
|
+
}, [userList]);
|
|
149
|
+
|
|
150
|
+
React.useEffect(() => {
|
|
151
|
+
// Filter new requests
|
|
152
|
+
const initialRequests = uidsOfInitialRequests
|
|
153
|
+
.filter(
|
|
154
|
+
(item: attrRequestInterface) => !currLiveStreamRequest?.[item.uid],
|
|
155
|
+
)
|
|
156
|
+
.reduce((acc, item) => {
|
|
157
|
+
return {
|
|
158
|
+
...acc,
|
|
159
|
+
[item.uid]: {
|
|
160
|
+
uid: item.uid,
|
|
161
|
+
ts: new Date().getTime(),
|
|
162
|
+
status:
|
|
163
|
+
item.status === attrRequestStatus.RaiseHand_Approved
|
|
164
|
+
? requestStatus.Approved
|
|
165
|
+
: requestStatus.AwaitingAction,
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}, {});
|
|
169
|
+
setLiveStreamRequest((oldLiveStreamRequest) => ({
|
|
170
|
+
...oldLiveStreamRequest,
|
|
171
|
+
...initialRequests,
|
|
172
|
+
}));
|
|
173
|
+
}, [uidsOfInitialRequests]);
|
|
174
|
+
|
|
175
|
+
// Events listening section
|
|
176
|
+
|
|
177
|
+
React.useEffect(() => {
|
|
178
|
+
events.on(
|
|
179
|
+
messageChannelType.Public,
|
|
180
|
+
'onLiveStreamActionsForHost',
|
|
181
|
+
(data: any, error: any) => {
|
|
182
|
+
if (!data) return;
|
|
183
|
+
if (!isHost) return;
|
|
184
|
+
switch (data.msg) {
|
|
185
|
+
// 1. All Hosts in channel add the audience request with 'Awaiting action' status
|
|
186
|
+
case LiveStreamControlMessageEnum.raiseHandRequest:
|
|
187
|
+
showToast(
|
|
188
|
+
`${getAttendeeName(data.uid)} ${
|
|
189
|
+
LSNotificationObject.RAISE_HAND_RECEIVED
|
|
190
|
+
}`,
|
|
191
|
+
);
|
|
192
|
+
addOrUpdateLiveStreamRequest({
|
|
193
|
+
uid: data.uid,
|
|
194
|
+
ts: data.ts,
|
|
195
|
+
status: requestStatus.AwaitingAction,
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
// 2. All Hosts in channel update their status when a audience recalls his request
|
|
199
|
+
case LiveStreamControlMessageEnum.raiseHandRequestRecall:
|
|
200
|
+
showToast(
|
|
201
|
+
`${getAttendeeName(data.uid)} ${
|
|
202
|
+
LSNotificationObject.RAISE_HAND_REQUEST_RECALL
|
|
203
|
+
}`,
|
|
204
|
+
);
|
|
205
|
+
addOrUpdateLiveStreamRequest({
|
|
206
|
+
uid: data.uid,
|
|
207
|
+
ts: data.ts,
|
|
208
|
+
status: requestStatus.Cancelled,
|
|
209
|
+
});
|
|
210
|
+
break;
|
|
211
|
+
// 3. All Host in channel update their status when a audience request is approved
|
|
212
|
+
case LiveStreamControlMessageEnum.notifyAllRequestApproved:
|
|
213
|
+
addOrUpdateLiveStreamRequest({
|
|
214
|
+
uid: data.uid,
|
|
215
|
+
ts: data.ts,
|
|
216
|
+
status: requestStatus.Approved,
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
// 4. All Host in channel update their status when a audience request is rejected
|
|
220
|
+
case LiveStreamControlMessageEnum.notifyAllRequestRejected:
|
|
221
|
+
addOrUpdateLiveStreamRequest({
|
|
222
|
+
uid: data.uid,
|
|
223
|
+
ts: data.ts,
|
|
224
|
+
status: requestStatus.Cancelled,
|
|
225
|
+
});
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
events.on(
|
|
233
|
+
messageChannelType.Private,
|
|
234
|
+
'onLiveStreamActionsForAudience',
|
|
235
|
+
(data: any, error: any) => {
|
|
236
|
+
if (!data) return;
|
|
237
|
+
switch (data.msg) {
|
|
238
|
+
// 1. Audience receives this when the request is accepted by host
|
|
239
|
+
case LiveStreamControlMessageEnum.raiseHandRequestAccepted:
|
|
240
|
+
if (!raiseHandRequestActive) return;
|
|
241
|
+
showToast(LSNotificationObject.RAISE_HAND_ACCEPTED);
|
|
242
|
+
// Audience notfies all host when request is approved
|
|
243
|
+
notifyAllHostsInChannel(
|
|
244
|
+
LiveStreamControlMessageEnum.notifyAllRequestApproved,
|
|
245
|
+
);
|
|
246
|
+
changeClientRoleTo(ClientRole.Broadcaster);
|
|
247
|
+
localUserRef.current.status = requestStatus.Approved;
|
|
248
|
+
updateLocalUserAttributes(attrRequestStatus.RaiseHand_Approved);
|
|
249
|
+
break;
|
|
250
|
+
// 2. Audience receives this when the request is cancelled by host
|
|
251
|
+
case LiveStreamControlMessageEnum.raiseHandRequestRejected:
|
|
252
|
+
showToast(LSNotificationObject.RAISE_HAND_REJECTED);
|
|
253
|
+
setRaiseHandRequestActive(false);
|
|
254
|
+
// Audience notfies all host when request is approved
|
|
255
|
+
notifyAllHostsInChannel(
|
|
256
|
+
LiveStreamControlMessageEnum.notifyAllRequestRejected,
|
|
257
|
+
);
|
|
258
|
+
localUserRef.current.status = requestStatus.Cancelled;
|
|
259
|
+
updateLocalUserAttributes(attrRequestTypes.none);
|
|
260
|
+
break;
|
|
261
|
+
// 3. Audience receives this when host demotes (canceled after approval)
|
|
262
|
+
case LiveStreamControlMessageEnum.raiseHandApprovedRequestRecall:
|
|
263
|
+
showToast(LSNotificationObject.RAISE_HAND_APPROVED_REQUEST_RECALL);
|
|
264
|
+
screenshareContextInstance?.stopUserScreenShare(); // This will not exist on ios
|
|
265
|
+
setRaiseHandRequestActive(false);
|
|
266
|
+
// Audience notfies all host when request is rejected
|
|
267
|
+
notifyAllHostsInChannel(
|
|
268
|
+
LiveStreamControlMessageEnum.notifyAllRequestRejected,
|
|
269
|
+
);
|
|
270
|
+
changeClientRoleTo(ClientRole.Audience);
|
|
271
|
+
localUserRef.current.status = requestStatus.Cancelled;
|
|
272
|
+
break;
|
|
273
|
+
// 4. Audience when receives kickUser notifies all host when is kicked out
|
|
274
|
+
case controlMessageEnum.kickUser:
|
|
275
|
+
notifyAllHostsInChannel(
|
|
276
|
+
LiveStreamControlMessageEnum.notifyAllRequestRejected,
|
|
277
|
+
);
|
|
278
|
+
localUserRef.current.status = requestStatus.Cancelled;
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
return () => {
|
|
287
|
+
// Cleanup the listeners
|
|
288
|
+
events.off(messageChannelType.Public, 'onLiveStreamActionsForHost');
|
|
289
|
+
events.off(messageChannelType.Private, 'onLiveStreamActionsForAudience');
|
|
290
|
+
};
|
|
291
|
+
}, [events, localUid, isHost, raiseHandRequestActive, userList]);
|
|
292
|
+
|
|
293
|
+
const addOrUpdateLiveStreamRequest = (request: requestInterface) => {
|
|
294
|
+
if (request && request?.uid && request?.ts && request?.uid) {
|
|
295
|
+
setLiveStreamRequest((oldLiveStreamRequest) => ({
|
|
296
|
+
...oldLiveStreamRequest,
|
|
297
|
+
[request?.uid as string]: {
|
|
298
|
+
status: request.status,
|
|
299
|
+
ts: request.ts,
|
|
300
|
+
uid: request.uid,
|
|
301
|
+
},
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const changeClientRoleTo = (newRole: ClientRole) => {
|
|
307
|
+
updateRtcProps(newRole);
|
|
308
|
+
broadcastUserAttributes(
|
|
309
|
+
[{key: 'role', value: newRole.toString()}],
|
|
310
|
+
controlMessageEnum.clientRoleChanged,
|
|
311
|
+
);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const notifyAllHostsInChannel = (ctrlEnum: LiveStreamControlMessageEnum) => {
|
|
315
|
+
sendControlMessage(ctrlEnum);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/****************** HOST CONTROLS ******************
|
|
319
|
+
* Host controls for Live Streaming
|
|
320
|
+
* a. Host can approve streaming request sent by audience
|
|
321
|
+
* b. Host can reject streaming request sent by audience
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
const hostApprovesRequestOfUID = (uid: number | string) => {
|
|
325
|
+
addOrUpdateLiveStreamRequest({
|
|
326
|
+
uid,
|
|
327
|
+
ts: new Date().getTime(),
|
|
328
|
+
status: requestStatus.Cancelled,
|
|
329
|
+
});
|
|
330
|
+
sendControlMessageToUid(
|
|
331
|
+
LiveStreamControlMessageEnum.raiseHandRequestAccepted,
|
|
332
|
+
uid,
|
|
333
|
+
);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const hostRejectsRequestOfUID = (uid: number | string) => {
|
|
337
|
+
addOrUpdateLiveStreamRequest({
|
|
338
|
+
uid,
|
|
339
|
+
ts: new Date().getTime(),
|
|
340
|
+
status: requestStatus.Cancelled,
|
|
341
|
+
});
|
|
342
|
+
sendControlMessageToUid(
|
|
343
|
+
LiveStreamControlMessageEnum.raiseHandRequestRejected,
|
|
344
|
+
uid,
|
|
345
|
+
);
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
/****************** AUDIENCE CONTROLS ****************
|
|
349
|
+
* Audience have below controls
|
|
350
|
+
* a. Audience can raise a request to live stream
|
|
351
|
+
* b. Audience can recalls his request to live stream
|
|
352
|
+
* i. While recalling the request could be either approved or not approved
|
|
353
|
+
*/
|
|
354
|
+
|
|
355
|
+
const audienceSendsRequest = () => {
|
|
356
|
+
showToast(LSNotificationObject.RAISE_HAND_REQUEST);
|
|
357
|
+
setRaiseHandRequestActive(true);
|
|
358
|
+
sendControlMessage(LiveStreamControlMessageEnum.raiseHandRequest);
|
|
359
|
+
updateLocalUserAttributes(attrRequestStatus.RaiseHand_AwaitingAction);
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const audienceRecallsRequest = () => {
|
|
363
|
+
/**
|
|
364
|
+
* if: Check if request is already approved
|
|
365
|
+
* else: Audience Request was not approved by host, and was in 'Awaiting Action' status
|
|
366
|
+
*/
|
|
367
|
+
if (
|
|
368
|
+
localUserRef &&
|
|
369
|
+
localUserRef.current?.status === requestStatus.Approved
|
|
370
|
+
) {
|
|
371
|
+
screenshareContextInstance?.stopUserScreenShare(); // This will not exist on ios
|
|
372
|
+
setRaiseHandRequestActive(false);
|
|
373
|
+
/// Change role and send message in channel notifying the same
|
|
374
|
+
changeClientRoleTo(ClientRole.Audience);
|
|
375
|
+
notifyAllHostsInChannel(
|
|
376
|
+
LiveStreamControlMessageEnum.notifyAllRequestRejected,
|
|
377
|
+
);
|
|
378
|
+
} else {
|
|
379
|
+
setRaiseHandRequestActive(false);
|
|
380
|
+
// Send message in channel to withdraw the request
|
|
381
|
+
sendControlMessage(LiveStreamControlMessageEnum.raiseHandRequestRecall);
|
|
382
|
+
}
|
|
383
|
+
updateLocalUserAttributes(attrRequestTypes.none);
|
|
384
|
+
showToast(LSNotificationObject.RAISE_HAND_REQUEST_RECALL_LOCAL);
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const updateLocalUserAttributes = (
|
|
388
|
+
value: attrRequestTypes | attrRequestStatus,
|
|
389
|
+
) => {
|
|
390
|
+
addOrUpdateLocalUserAttributes([{key: 'requests', value: value}]);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
return (
|
|
394
|
+
<LiveStreamContext.Provider
|
|
395
|
+
value={{
|
|
396
|
+
setLastCheckedRequestTimestamp,
|
|
397
|
+
isPendingRequestToReview,
|
|
398
|
+
currLiveStreamRequest,
|
|
399
|
+
hostApprovesRequestOfUID,
|
|
400
|
+
hostRejectsRequestOfUID,
|
|
401
|
+
audienceSendsRequest,
|
|
402
|
+
audienceRecallsRequest,
|
|
403
|
+
raiseHandRequestActive,
|
|
404
|
+
setRaiseHandRequestActive,
|
|
405
|
+
}}>
|
|
406
|
+
{props.children}
|
|
407
|
+
</LiveStreamContext.Provider>
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
export default LiveStreamContext;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// DECLARE ENUMS & CONTANTS
|
|
2
|
+
|
|
3
|
+
export enum requestStatus {
|
|
4
|
+
AwaitingAction = 'AWAITING_ACTION',
|
|
5
|
+
Approved = 'APPROVED',
|
|
6
|
+
Cancelled = 'CANCELLED',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export enum attrRequestStatus {
|
|
10
|
+
RaiseHand_AwaitingAction = 'AWAITING_ACTION',
|
|
11
|
+
RaiseHand_Approved = 'APPROVED',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export enum LiveStreamControlMessageEnum {
|
|
15
|
+
raiseHandRequest = 'RAISE_HAND_REQUEST',
|
|
16
|
+
raiseHandRequestAccepted = 'RAISE_HAND_ACCEPTED',
|
|
17
|
+
raiseHandRequestRejected = 'RAISE_HAND_REJECTED',
|
|
18
|
+
raiseHandRequestReceived = 'RAISE_HAND_RECEIVED',
|
|
19
|
+
raiseHandRequestRecall = 'RAISE_HAND_REQUEST_RECALL',
|
|
20
|
+
raiseHandRequestRecallLocal = 'RAISE_HAND_REQUEST_RECALL_LOCAL',
|
|
21
|
+
raiseHandApprovedRequestRecall = 'RAISE_HAND_APPROVED_REQUEST_RECALL',
|
|
22
|
+
notifyAllRequestApproved = 'NOTIFY_REQUEST_APPROVED',
|
|
23
|
+
notifyAllRequestRejected = 'NOTIFY_REQUEST_REJECTED',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const LSNotificationObject = {
|
|
27
|
+
[LiveStreamControlMessageEnum.raiseHandRequest]:
|
|
28
|
+
'You have raised your hand. Request sent to host for approval',
|
|
29
|
+
[LiveStreamControlMessageEnum.raiseHandRequestReceived]:
|
|
30
|
+
'has raised their hand',
|
|
31
|
+
[LiveStreamControlMessageEnum.raiseHandRequestAccepted]:
|
|
32
|
+
'Your request was approved, unmute to start talking',
|
|
33
|
+
[LiveStreamControlMessageEnum.raiseHandRequestRejected]:
|
|
34
|
+
'Your request was rejected by the host',
|
|
35
|
+
[LiveStreamControlMessageEnum.raiseHandRequestRecall]:
|
|
36
|
+
'has lowered their hand',
|
|
37
|
+
[LiveStreamControlMessageEnum.raiseHandRequestRecallLocal]:
|
|
38
|
+
'You have lowered your hand',
|
|
39
|
+
[LiveStreamControlMessageEnum.raiseHandApprovedRequestRecall]:
|
|
40
|
+
'The host has revoked streaming permissions',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface liveStreamPropsInterface {
|
|
44
|
+
isHost: boolean;
|
|
45
|
+
setRtcProps: any;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface liveStreamContext {
|
|
49
|
+
setLastCheckedRequestTimestamp: (timestamp: number) => void;
|
|
50
|
+
isPendingRequestToReview: boolean;
|
|
51
|
+
currLiveStreamRequest: Partial<Record<string, requestInterface>>;
|
|
52
|
+
hostApprovesRequestOfUID: (uid: number) => void;
|
|
53
|
+
hostRejectsRequestOfUID: (uid: number) => void;
|
|
54
|
+
audienceSendsRequest: () => void;
|
|
55
|
+
audienceRecallsRequest: () => void;
|
|
56
|
+
raiseHandRequestActive: boolean;
|
|
57
|
+
setRaiseHandRequestActive: (state: boolean) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface requestInterface {
|
|
61
|
+
ts: number;
|
|
62
|
+
status: requestStatus;
|
|
63
|
+
uid: string | number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface attrRequestInterface {
|
|
67
|
+
status: attrRequestStatus;
|
|
68
|
+
uid: string | number;
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {LiveStreamControlMessageEnum, requestStatus} from './Types';
|
|
2
|
+
|
|
3
|
+
import LiveStreamContext, {
|
|
4
|
+
LiveStreamContextProvider,
|
|
5
|
+
} from './LiveStreamContext';
|
|
6
|
+
|
|
7
|
+
export {LiveStreamContextProvider, LiveStreamControlMessageEnum, requestStatus};
|
|
8
|
+
|
|
9
|
+
export default LiveStreamContext;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
********************************************
|
|
3
|
+
Copyright © 2021 Agora Lab, Inc., all rights reserved.
|
|
4
|
+
AppBuilder and all associated components, source code, APIs, services, and documentation
|
|
5
|
+
(the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
|
|
6
|
+
accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
|
|
7
|
+
Use without a license or in violation of any license terms and conditions (including use for
|
|
8
|
+
any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
|
|
9
|
+
information visit https://appbuilder.agora.io.
|
|
10
|
+
*********************************************
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import {View} from 'react-native';
|
|
14
|
+
import {LocalRaiseHand} from '../../../subComponents/livestream';
|
|
15
|
+
|
|
16
|
+
const LiveStreamControls = (props: any) => {
|
|
17
|
+
const {showControls} = props;
|
|
18
|
+
if (!$config.RAISE_HAND) return <></>;
|
|
19
|
+
if (!showControls) return <></>;
|
|
20
|
+
return (
|
|
21
|
+
<View style={{alignSelf: 'center'}}>
|
|
22
|
+
<LocalRaiseHand />
|
|
23
|
+
</View>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default LiveStreamControls;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import {View, Text} from 'react-native';
|
|
3
|
+
import RemoteEndCall from '../../subComponents/RemoteEndCall';
|
|
4
|
+
import ParticipantName from '../../components/participants/ParticipantName';
|
|
5
|
+
import chatContext from '../ChatContext';
|
|
6
|
+
|
|
7
|
+
const AllAudienceParticipants = (props: any) => {
|
|
8
|
+
const {p_style, isHost, participantList} = props;
|
|
9
|
+
const {localUid} = useContext(chatContext);
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<View style={p_style.participantContainer}>
|
|
13
|
+
{Object.keys(participantList).length == 0 ? (
|
|
14
|
+
<Text style={p_style.infoText}>No one has joined yet</Text>
|
|
15
|
+
) : (
|
|
16
|
+
<>
|
|
17
|
+
{/* Audience should see his name first */}
|
|
18
|
+
{Object.entries(participantList)
|
|
19
|
+
.filter(([uid, _]: any) => uid === localUid)
|
|
20
|
+
.map(([uid, user]: any, index: number) => (
|
|
21
|
+
<View style={p_style.participantRow} key={index}>
|
|
22
|
+
<ParticipantName value={user.name} />
|
|
23
|
+
{isHost && (
|
|
24
|
+
<View style={p_style.participantActionContainer}>
|
|
25
|
+
<View style={[p_style.actionBtnIcon]}>
|
|
26
|
+
<RemoteEndCall uid={uid} isHost={isHost} />
|
|
27
|
+
</View>
|
|
28
|
+
</View>
|
|
29
|
+
)}
|
|
30
|
+
</View>
|
|
31
|
+
))}
|
|
32
|
+
{/* Others Audience in the call */}
|
|
33
|
+
{Object.entries(participantList)
|
|
34
|
+
.filter(([uid, _]: any) => uid !== localUid)
|
|
35
|
+
.map(([uid, user]: any, index: number) => (
|
|
36
|
+
<View style={p_style.participantRow} key={index}>
|
|
37
|
+
<ParticipantName value={user.name} />
|
|
38
|
+
{isHost && (
|
|
39
|
+
<View style={p_style.participantActionContainer}>
|
|
40
|
+
<View style={[p_style.actionBtnIcon]}>
|
|
41
|
+
<RemoteEndCall uid={uid} isHost={isHost} />
|
|
42
|
+
</View>
|
|
43
|
+
</View>
|
|
44
|
+
)}
|
|
45
|
+
</View>
|
|
46
|
+
))}
|
|
47
|
+
</>
|
|
48
|
+
)}
|
|
49
|
+
</View>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default AllAudienceParticipants;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, {useContext} from 'react';
|
|
2
|
+
import {MinUidConsumer, MaxUidConsumer} from '../../../agora-rn-uikit';
|
|
3
|
+
import chatContext from '../ChatContext';
|
|
4
|
+
|
|
5
|
+
import MeParticipant from './MeParticipant';
|
|
6
|
+
import ScreenshareParticipants from './ScreenshareParticipants';
|
|
7
|
+
import RemoteParticipants from './RemoteParticipants';
|
|
8
|
+
import {UserType} from './../RTMConfigure';
|
|
9
|
+
|
|
10
|
+
export default function AllHostParticipants(props: any) {
|
|
11
|
+
const {p_style, isHost} = props;
|
|
12
|
+
const {userList, localUid} = useContext(chatContext);
|
|
13
|
+
|
|
14
|
+
const getParticipantName = (userUID: number | string) => {
|
|
15
|
+
if (userUID === 'local')
|
|
16
|
+
return userList[localUid] ? userList[localUid].name + ' ' : 'You ';
|
|
17
|
+
else if (userUID === 1)
|
|
18
|
+
return userList[localUid]
|
|
19
|
+
? userList[localUid].name + "'s screenshare "
|
|
20
|
+
: 'Your screenshare ';
|
|
21
|
+
else
|
|
22
|
+
return userList[userUID]
|
|
23
|
+
? userList[userUID].name + ' '
|
|
24
|
+
: String(userUID)[0] === '1'
|
|
25
|
+
? 'PSTN User '
|
|
26
|
+
: 'User ';
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<MinUidConsumer>
|
|
31
|
+
{(minUsers) => (
|
|
32
|
+
<MaxUidConsumer>
|
|
33
|
+
{(maxUser) =>
|
|
34
|
+
[...minUsers, ...maxUser].map((user) =>
|
|
35
|
+
user.uid === 'local' ? (
|
|
36
|
+
<MeParticipant
|
|
37
|
+
name={getParticipantName(user.uid)}
|
|
38
|
+
p_style={p_style}
|
|
39
|
+
key={user.uid}
|
|
40
|
+
/>
|
|
41
|
+
) : user.uid === 1 ? (
|
|
42
|
+
<ScreenshareParticipants
|
|
43
|
+
name={getParticipantName(user.uid)}
|
|
44
|
+
p_styles={p_style}
|
|
45
|
+
key={user.uid}
|
|
46
|
+
/>
|
|
47
|
+
) : (
|
|
48
|
+
<RemoteParticipants
|
|
49
|
+
name={getParticipantName(user.uid)}
|
|
50
|
+
p_styles={p_style}
|
|
51
|
+
user={user}
|
|
52
|
+
showControls={
|
|
53
|
+
userList[user.uid]?.type !== UserType.ScreenShare
|
|
54
|
+
}
|
|
55
|
+
isHost={isHost}
|
|
56
|
+
key={user.uid}
|
|
57
|
+
/>
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
</MaxUidConsumer>
|
|
62
|
+
)}
|
|
63
|
+
</MinUidConsumer>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
********************************************
|
|
3
|
+
Copyright © 2021 Agora Lab, Inc., all rights reserved.
|
|
4
|
+
AppBuilder and all associated components, source code, APIs, services, and documentation
|
|
5
|
+
(the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
|
|
6
|
+
accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
|
|
7
|
+
Use without a license or in violation of any license terms and conditions (including use for
|
|
8
|
+
any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
|
|
9
|
+
information visit https://appbuilder.agora.io.
|
|
10
|
+
*********************************************
|
|
11
|
+
*/
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import {View} from 'react-native';
|
|
14
|
+
import {LocalAudioMute, LocalVideoMute} from '../../../agora-rn-uikit';
|
|
15
|
+
import {LocalUserContext} from '../../../agora-rn-uikit';
|
|
16
|
+
import ParticipantName from './ParticipantName';
|
|
17
|
+
|
|
18
|
+
const MeParticipant = (props: any) => {
|
|
19
|
+
const {p_style, name} = props;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<View style={p_style.participantRow}>
|
|
23
|
+
<ParticipantName value={name} />
|
|
24
|
+
<View style={p_style.participantActionContainer}>
|
|
25
|
+
<LocalUserContext>
|
|
26
|
+
<View style={[p_style.actionBtnIcon, {marginRight: 10}]}>
|
|
27
|
+
<LocalAudioMute btnText=" " variant="text" />
|
|
28
|
+
</View>
|
|
29
|
+
<View style={p_style.actionBtnIcon}>
|
|
30
|
+
<LocalVideoMute btnText=" " variant="text" />
|
|
31
|
+
</View>
|
|
32
|
+
</LocalUserContext>
|
|
33
|
+
</View>
|
|
34
|
+
</View>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
export default MeParticipant;
|