agora-appbuilder-core 4.0.29-beta-13 → 4.0.29-beta-15
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/package.json +1 -1
- package/template/_react-native.config.js +7 -0
- package/template/customization-api/app-state.ts +0 -2
- package/template/customization-api/typeDefinition.ts +0 -1
- package/template/customization-api/utils.ts +0 -1
- package/template/src/auth/AuthProvider.tsx +1 -8
- package/template/src/auth/config.ts +1 -6
- package/template/src/components/ChatContext.ts +3 -7
- package/template/src/components/Precall.native.tsx +2 -13
- package/template/src/components/Precall.tsx +2 -14
- package/template/src/pages/video-call/VideoCallMobileView.tsx +71 -88
- package/template/src/pages/video-call/VideoComponent.tsx +6 -27
- package/template/src/utils/useGetHostUids.ts +0 -6
- package/template/src/utils/useHideShareTile.tsx +0 -16
package/package.json
CHANGED
|
@@ -61,5 +61,3 @@ export type {ChatUIControlsInterface} from '../src/components/chat-ui/useChatUIC
|
|
|
61
61
|
export {useVirtualBackground} from '../src/app-state/useVirtualBackground';
|
|
62
62
|
export {useBeautyEffects} from '../src/app-state/useBeautyEffects';
|
|
63
63
|
export {useLiveStreamDataContext} from '../src/components/contexts/LiveStreamDataContext';
|
|
64
|
-
export {useRtm} from '../src/components/ChatContext';
|
|
65
|
-
export {useGetHostIds} from '../src/utils/useGetHostUids';
|
|
@@ -35,7 +35,6 @@ export interface PreCallInterface extends BeforeAndAfterInterface {
|
|
|
35
35
|
joinButton?: React.ComponentType;
|
|
36
36
|
textBox?: React.ComponentType;
|
|
37
37
|
virtualBackgroundPanel?: React.ComponentType<VBPanelProps>;
|
|
38
|
-
wrapper?: React.ComponentType;
|
|
39
38
|
}
|
|
40
39
|
export interface ChatCmpInterface {
|
|
41
40
|
//commented for v1 release
|
|
@@ -47,4 +47,3 @@ export {getSessionId} from '../src/utils/common';
|
|
|
47
47
|
export {default as ThemeConfig} from '../src/theme';
|
|
48
48
|
export {default as hexadecimalTransparency} from '../src/utils/hexadecimalTransparency';
|
|
49
49
|
export {useFullScreen} from '../src/utils/useFullScreen';
|
|
50
|
-
export {useHideShareTitle} from '../src/utils/useHideShareTile';
|
|
@@ -510,15 +510,8 @@ const AuthProvider = (props: AuthProviderProps) => {
|
|
|
510
510
|
'API unauth_login Trying to authenticate user',
|
|
511
511
|
{requestId: requestId, startReqTs},
|
|
512
512
|
);
|
|
513
|
-
let user_id_unauth = null;
|
|
514
|
-
try {
|
|
515
|
-
if (isWeb()) {
|
|
516
|
-
const urlParams = new URLSearchParams(window?.location?.search);
|
|
517
|
-
user_id_unauth = urlParams.get('user_id');
|
|
518
|
-
}
|
|
519
|
-
} catch (error) {}
|
|
520
513
|
|
|
521
|
-
fetch(GET_UNAUTH_FLOW_API_ENDPOINT(
|
|
514
|
+
fetch(GET_UNAUTH_FLOW_API_ENDPOINT(), {
|
|
522
515
|
credentials: 'include',
|
|
523
516
|
headers: {
|
|
524
517
|
'X-Request-Id': requestId,
|
|
@@ -46,12 +46,7 @@ export const getOriginURL = () => {
|
|
|
46
46
|
return isWeb() ? `${window.location.origin}` : `${$config.FRONTEND_ENDPOINT}`;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
export const GET_UNAUTH_FLOW_API_ENDPOINT = (
|
|
50
|
-
if (user_id) {
|
|
51
|
-
return `${$config.BACKEND_ENDPOINT}/v1/login?project_id=${
|
|
52
|
-
$config.PROJECT_ID
|
|
53
|
-
}&platform_id=${getPlatformId()}&user_id=${user_id}`;
|
|
54
|
-
}
|
|
49
|
+
export const GET_UNAUTH_FLOW_API_ENDPOINT = () => {
|
|
55
50
|
return `${$config.BACKEND_ENDPOINT}/v1/login?project_id=${
|
|
56
51
|
$config.PROJECT_ID
|
|
57
52
|
}&platform_id=${getPlatformId()}`;
|
|
@@ -13,7 +13,6 @@ import RtmEngine from 'agora-react-native-rtm';
|
|
|
13
13
|
import {UidType} from '../../agora-rn-uikit';
|
|
14
14
|
import {createContext, SetStateAction} from 'react';
|
|
15
15
|
import {ChatMessageType} from './chat-messages/useChatMessages';
|
|
16
|
-
import {createHook} from 'customization-implementation';
|
|
17
16
|
|
|
18
17
|
export interface ChatBubbleProps {
|
|
19
18
|
isLocal: boolean;
|
|
@@ -61,7 +60,7 @@ export enum messageActionType {
|
|
|
61
60
|
Normal = '1',
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
export interface
|
|
63
|
+
export interface chatContext {
|
|
65
64
|
hasUserJoinedRTM: boolean;
|
|
66
65
|
rtmInitTimstamp: number;
|
|
67
66
|
engine: RtmEngine;
|
|
@@ -81,9 +80,6 @@ export enum controlMessageEnum {
|
|
|
81
80
|
kickScreenshare = '9',
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
const
|
|
83
|
+
const ChatContext = createContext(null as unknown as chatContext);
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
export {useRtm};
|
|
89
|
-
export default RtmContext;
|
|
85
|
+
export default ChatContext;
|
|
@@ -227,7 +227,6 @@ const Precall = (props: any) => {
|
|
|
227
227
|
VirtualBackgroundComponent,
|
|
228
228
|
PrecallAfterView,
|
|
229
229
|
PrecallBeforeView,
|
|
230
|
-
wrapper: PrecallWrapper,
|
|
231
230
|
} = useCustomization(data => {
|
|
232
231
|
const components: {
|
|
233
232
|
PrecallAfterView: React.ComponentType;
|
|
@@ -236,7 +235,6 @@ const Precall = (props: any) => {
|
|
|
236
235
|
VideoPreview: React.ComponentType;
|
|
237
236
|
VirtualBackgroundComponent: React.ComponentType<VBPanelProps>;
|
|
238
237
|
MeetingName: React.ComponentType<MeetingTitleProps>;
|
|
239
|
-
wrapper: React.ComponentType;
|
|
240
238
|
} = {
|
|
241
239
|
PrecallAfterView: React.Fragment,
|
|
242
240
|
PrecallBeforeView: React.Fragment,
|
|
@@ -244,7 +242,6 @@ const Precall = (props: any) => {
|
|
|
244
242
|
VideoPreview: PreCallVideoPreview,
|
|
245
243
|
DeviceSelect: PreCallSelectDevice,
|
|
246
244
|
VirtualBackgroundComponent: VBPanel,
|
|
247
|
-
wrapper: React.Fragment,
|
|
248
245
|
};
|
|
249
246
|
// commented for v1 release
|
|
250
247
|
// if (
|
|
@@ -291,14 +288,6 @@ const Precall = (props: any) => {
|
|
|
291
288
|
// }
|
|
292
289
|
// }
|
|
293
290
|
// }
|
|
294
|
-
if (
|
|
295
|
-
data?.components?.precall?.wrapper &&
|
|
296
|
-
typeof data?.components?.precall?.wrapper !== 'object'
|
|
297
|
-
) {
|
|
298
|
-
if (isValidReactComponent(data?.components?.precall?.wrapper)) {
|
|
299
|
-
components.wrapper = data?.components?.precall?.wrapper;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
291
|
if (
|
|
303
292
|
data?.components?.precall?.virtualBackgroundPanel &&
|
|
304
293
|
typeof data?.components?.precall.virtualBackgroundPanel !== 'object' &&
|
|
@@ -368,7 +357,7 @@ const Precall = (props: any) => {
|
|
|
368
357
|
return FpePrecallComponent ? (
|
|
369
358
|
<FpePrecallComponent />
|
|
370
359
|
) : (
|
|
371
|
-
|
|
360
|
+
<>
|
|
372
361
|
<PrecallBeforeView />
|
|
373
362
|
<View style={{flex: 1}}>
|
|
374
363
|
<ScrollView
|
|
@@ -465,7 +454,7 @@ const Precall = (props: any) => {
|
|
|
465
454
|
</ScrollView>
|
|
466
455
|
</View>
|
|
467
456
|
<PrecallAfterView />
|
|
468
|
-
|
|
457
|
+
</>
|
|
469
458
|
);
|
|
470
459
|
};
|
|
471
460
|
|
|
@@ -248,7 +248,6 @@ const Precall = () => {
|
|
|
248
248
|
VirtualBackgroundComponent,
|
|
249
249
|
PrecallAfterView,
|
|
250
250
|
PrecallBeforeView,
|
|
251
|
-
wrapper: PrecallWrapper,
|
|
252
251
|
} = useCustomization(data => {
|
|
253
252
|
const components: {
|
|
254
253
|
PrecallAfterView: React.ComponentType;
|
|
@@ -257,7 +256,6 @@ const Precall = () => {
|
|
|
257
256
|
VirtualBackgroundComponent: React.ComponentType<VBPanelProps>;
|
|
258
257
|
VideoPreview: React.ComponentType;
|
|
259
258
|
MeetingName: React.ComponentType<MeetingTitleProps>;
|
|
260
|
-
wrapper: React.ComponentType;
|
|
261
259
|
} = {
|
|
262
260
|
PrecallAfterView: React.Fragment,
|
|
263
261
|
PrecallBeforeView: React.Fragment,
|
|
@@ -265,7 +263,6 @@ const Precall = () => {
|
|
|
265
263
|
VideoPreview: PreCallVideoPreview,
|
|
266
264
|
DeviceSelect: PreCallSelectDevice,
|
|
267
265
|
VirtualBackgroundComponent: VBPanel,
|
|
268
|
-
wrapper: React.Fragment,
|
|
269
266
|
};
|
|
270
267
|
// commented for v1 release
|
|
271
268
|
// if (
|
|
@@ -313,15 +310,6 @@ const Precall = () => {
|
|
|
313
310
|
// }
|
|
314
311
|
// }
|
|
315
312
|
|
|
316
|
-
if (
|
|
317
|
-
data?.components?.precall?.wrapper &&
|
|
318
|
-
typeof data?.components?.precall?.wrapper !== 'object'
|
|
319
|
-
) {
|
|
320
|
-
if (isValidReactComponent(data?.components?.precall?.wrapper)) {
|
|
321
|
-
components.wrapper = data?.components?.precall?.wrapper;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
313
|
if (
|
|
326
314
|
data?.components?.precall?.virtualBackgroundPanel &&
|
|
327
315
|
typeof data?.components?.precall.virtualBackgroundPanel !== 'object' &&
|
|
@@ -400,7 +388,7 @@ const Precall = () => {
|
|
|
400
388
|
return FpePrecallComponent ? (
|
|
401
389
|
<FpePrecallComponent />
|
|
402
390
|
) : (
|
|
403
|
-
|
|
391
|
+
<>
|
|
404
392
|
<PrecallBeforeView />
|
|
405
393
|
{$config.EVENT_MODE &&
|
|
406
394
|
rtcProps.role == ClientRoleType.ClientRoleAudience ? (
|
|
@@ -541,7 +529,7 @@ const Precall = () => {
|
|
|
541
529
|
</View>
|
|
542
530
|
)}
|
|
543
531
|
<PrecallAfterView />
|
|
544
|
-
|
|
532
|
+
</>
|
|
545
533
|
);
|
|
546
534
|
};
|
|
547
535
|
|
|
@@ -184,100 +184,87 @@ const VideoCallMobileView = props => {
|
|
|
184
184
|
|
|
185
185
|
const VideoCallView = React.memo(() => {
|
|
186
186
|
//toolbar changes
|
|
187
|
-
const {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
} = {
|
|
201
|
-
BottombarComponent: ActionSheet,
|
|
202
|
-
BottombarProps: {},
|
|
203
|
-
TopbarComponent: NavbarMobile,
|
|
204
|
-
TopbarProps: {},
|
|
205
|
-
VideocallWrapper: ContainerView,
|
|
206
|
-
};
|
|
207
|
-
if (
|
|
208
|
-
data?.components?.videoCall &&
|
|
209
|
-
typeof data?.components?.videoCall === 'object'
|
|
210
|
-
) {
|
|
187
|
+
const {BottombarComponent, BottombarProps, TopbarComponent, TopbarProps} =
|
|
188
|
+
useCustomization(data => {
|
|
189
|
+
let components: {
|
|
190
|
+
BottombarComponent: React.ComponentType<any>;
|
|
191
|
+
BottombarProps?: ToolbarPresetProps['items'];
|
|
192
|
+
TopbarComponent: React.ComponentType<NavbarProps>;
|
|
193
|
+
TopbarProps?: ToolbarPresetProps['items'];
|
|
194
|
+
} = {
|
|
195
|
+
BottombarComponent: ActionSheet,
|
|
196
|
+
BottombarProps: {},
|
|
197
|
+
TopbarComponent: NavbarMobile,
|
|
198
|
+
TopbarProps: {},
|
|
199
|
+
};
|
|
211
200
|
if (
|
|
212
|
-
data?.components?.videoCall
|
|
213
|
-
typeof data?.components?.videoCall
|
|
214
|
-
isValidReactComponent(data?.components?.videoCall.bottomToolBar)
|
|
201
|
+
data?.components?.videoCall &&
|
|
202
|
+
typeof data?.components?.videoCall === 'object'
|
|
215
203
|
) {
|
|
216
|
-
|
|
217
|
-
data?.components?.videoCall
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
) {
|
|
232
|
-
components.TopbarComponent = data?.components?.videoCall.topToolBar;
|
|
233
|
-
}
|
|
204
|
+
if (
|
|
205
|
+
data?.components?.videoCall?.bottomToolBar &&
|
|
206
|
+
typeof data?.components?.videoCall.bottomToolBar !== 'object' &&
|
|
207
|
+
isValidReactComponent(data?.components?.videoCall.bottomToolBar)
|
|
208
|
+
) {
|
|
209
|
+
components.BottombarComponent =
|
|
210
|
+
data?.components?.videoCall.bottomToolBar;
|
|
211
|
+
}
|
|
212
|
+
if (
|
|
213
|
+
data?.components?.videoCall?.bottomToolBar &&
|
|
214
|
+
typeof data?.components?.videoCall?.bottomToolBar === 'object' &&
|
|
215
|
+
Object.keys(data?.components?.videoCall.bottomToolBar)?.length
|
|
216
|
+
) {
|
|
217
|
+
components.BottombarProps = data?.components?.videoCall.bottomToolBar;
|
|
218
|
+
}
|
|
234
219
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
220
|
+
if (
|
|
221
|
+
data?.components?.videoCall?.topToolBar &&
|
|
222
|
+
typeof data?.components?.videoCall?.topToolBar !== 'object' &&
|
|
223
|
+
isValidReactComponent(data?.components?.videoCall.topToolBar)
|
|
224
|
+
) {
|
|
225
|
+
components.TopbarComponent = data?.components?.videoCall.topToolBar;
|
|
226
|
+
}
|
|
242
227
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
228
|
+
if (
|
|
229
|
+
data?.components?.videoCall?.topToolBar &&
|
|
230
|
+
typeof data?.components?.videoCall?.topToolBar === 'object' &&
|
|
231
|
+
Object.keys(data?.components?.videoCall.topToolBar).length
|
|
232
|
+
) {
|
|
233
|
+
components.TopbarProps = data?.components?.videoCall.topToolBar;
|
|
234
|
+
}
|
|
249
235
|
}
|
|
250
|
-
}
|
|
251
236
|
|
|
252
|
-
|
|
253
|
-
|
|
237
|
+
return components;
|
|
238
|
+
});
|
|
254
239
|
|
|
255
240
|
return (
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
{
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
<TopbarComponent />
|
|
262
|
-
)}
|
|
263
|
-
</ToolbarProvider>
|
|
264
|
-
<View style={styles.videoView}>
|
|
265
|
-
<VideoComponent />
|
|
266
|
-
<CaptionContainer />
|
|
267
|
-
</View>
|
|
268
|
-
<ToolbarProvider value={{position: ToolbarPosition.bottom}}>
|
|
269
|
-
<ActionSheetProvider>
|
|
270
|
-
{Object.keys(BottombarProps)?.length ? (
|
|
271
|
-
<BottombarComponent
|
|
272
|
-
items={BottombarProps}
|
|
273
|
-
includeDefaultItems={false}
|
|
274
|
-
/>
|
|
241
|
+
<View style={styles.container}>
|
|
242
|
+
<>
|
|
243
|
+
<ToolbarProvider value={{position: ToolbarPosition.top}}>
|
|
244
|
+
{Object.keys(TopbarProps)?.length ? (
|
|
245
|
+
<TopbarComponent items={TopbarProps} includeDefaultItems={false} />
|
|
275
246
|
) : (
|
|
276
|
-
<
|
|
247
|
+
<TopbarComponent />
|
|
277
248
|
)}
|
|
278
|
-
</
|
|
279
|
-
|
|
280
|
-
|
|
249
|
+
</ToolbarProvider>
|
|
250
|
+
<View style={styles.videoView}>
|
|
251
|
+
<VideoComponent />
|
|
252
|
+
<CaptionContainer />
|
|
253
|
+
</View>
|
|
254
|
+
<ToolbarProvider value={{position: ToolbarPosition.bottom}}>
|
|
255
|
+
<ActionSheetProvider>
|
|
256
|
+
{Object.keys(BottombarProps)?.length ? (
|
|
257
|
+
<BottombarComponent
|
|
258
|
+
items={BottombarProps}
|
|
259
|
+
includeDefaultItems={false}
|
|
260
|
+
/>
|
|
261
|
+
) : (
|
|
262
|
+
<BottombarComponent />
|
|
263
|
+
)}
|
|
264
|
+
</ActionSheetProvider>
|
|
265
|
+
</ToolbarProvider>
|
|
266
|
+
</>
|
|
267
|
+
</View>
|
|
281
268
|
);
|
|
282
269
|
});
|
|
283
270
|
|
|
@@ -310,7 +297,3 @@ const styles = StyleSheet.create({
|
|
|
310
297
|
flexDirection: 'row',
|
|
311
298
|
},
|
|
312
299
|
});
|
|
313
|
-
|
|
314
|
-
const ContainerView = props => {
|
|
315
|
-
return <View style={styles.container}>{props.children}</View>;
|
|
316
|
-
};
|
|
@@ -10,8 +10,6 @@ import {DispatchContext} from '../../../agora-rn-uikit';
|
|
|
10
10
|
import MeetingInfoGridTile from '../../components/meeting-info-invite/MeetingInfoGridTile';
|
|
11
11
|
import Spacer from '../../atoms/Spacer';
|
|
12
12
|
import {useLiveStreamDataContext} from '../../components/contexts/LiveStreamDataContext';
|
|
13
|
-
import {useCustomization} from 'customization-implementation';
|
|
14
|
-
import useMount from '../../components/useMount';
|
|
15
13
|
|
|
16
14
|
const VideoComponent = () => {
|
|
17
15
|
const {dispatch} = useContext(DispatchContext);
|
|
@@ -23,42 +21,23 @@ const VideoComponent = () => {
|
|
|
23
21
|
const isDesktop = useIsDesktop();
|
|
24
22
|
const {audienceUids, hostUids} = useLiveStreamDataContext();
|
|
25
23
|
const [showNoUserInfo, setShowNoUserInfo] = useState(false);
|
|
26
|
-
const isCustomLayoutUsed = useCustomization(config => {
|
|
27
|
-
if (
|
|
28
|
-
typeof config?.components?.videoCall === 'object' &&
|
|
29
|
-
config?.components?.videoCall?.customLayout
|
|
30
|
-
) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
});
|
|
35
24
|
|
|
36
25
|
const {roomPreference} = useRoomInfo();
|
|
37
26
|
|
|
38
27
|
const disableShareTile = roomPreference?.disableShareTile;
|
|
39
|
-
const disableShareTileRef = useRef(disableShareTile);
|
|
40
|
-
|
|
41
|
-
useMount(() => {
|
|
42
|
-
//show share tile after 2.5 seconds because RTC user join take few seconds.
|
|
43
|
-
//meanwhile we don't want to show the share tile
|
|
44
|
-
setTimeout(() => {
|
|
45
|
-
if (!disableShareTileRef.current) {
|
|
46
|
-
setShowNoUserInfo(true);
|
|
47
|
-
}
|
|
48
|
-
}, 2500);
|
|
49
|
-
});
|
|
50
28
|
|
|
51
29
|
useEffect(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
30
|
+
if (!disableShareTile) {
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
setShowNoUserInfo(true);
|
|
33
|
+
}, 2500);
|
|
55
34
|
}
|
|
56
35
|
}, [disableShareTile]);
|
|
57
36
|
|
|
58
37
|
const currentLayoutRef = useRef(currentLayout);
|
|
59
38
|
const gridLayoutName = getGridLayoutName();
|
|
60
39
|
useEffect(() => {
|
|
61
|
-
if (activeUids && activeUids.length === 1
|
|
40
|
+
if (activeUids && activeUids.length === 1) {
|
|
62
41
|
if (pinnedUid) {
|
|
63
42
|
dispatch({type: 'UserPin', value: [0]});
|
|
64
43
|
dispatch({type: 'UserSecondaryPin', value: [0]});
|
|
@@ -67,7 +46,7 @@ const VideoComponent = () => {
|
|
|
67
46
|
setLayout(gridLayoutName);
|
|
68
47
|
}
|
|
69
48
|
}
|
|
70
|
-
}, [activeUids
|
|
49
|
+
}, [activeUids]);
|
|
71
50
|
|
|
72
51
|
useEffect(() => {
|
|
73
52
|
currentLayoutRef.current = currentLayout;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {useSetRoomInfo} from '../components/room-info/useSetRoomInfo';
|
|
2
|
-
|
|
3
|
-
export const useHideShareTitle = () => {
|
|
4
|
-
const {setRoomInfo} = useSetRoomInfo();
|
|
5
|
-
return (disableShareTile: boolean) => {
|
|
6
|
-
setRoomInfo(prevState => {
|
|
7
|
-
return {
|
|
8
|
-
...prevState,
|
|
9
|
-
roomPreference: {
|
|
10
|
-
...prevState?.roomPreference,
|
|
11
|
-
disableShareTile: disableShareTile,
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
};
|