@stream-io/video-react-native-sdk 0.0.1-alpha.141
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/CHANGELOG.md +739 -0
- package/LICENSE +219 -0
- package/README.md +19 -0
- package/dist/__tests__/components/ActiveCall.test.d.ts +1 -0
- package/dist/__tests__/components/ActiveCall.test.js +89 -0
- package/dist/__tests__/components/ActiveCall.test.js.map +1 -0
- package/dist/__tests__/components/Avatar.test.d.ts +1 -0
- package/dist/__tests__/components/Avatar.test.js +34 -0
- package/dist/__tests__/components/Avatar.test.js.map +1 -0
- package/dist/__tests__/components/ParticipantView.test.d.ts +1 -0
- package/dist/__tests__/components/ParticipantView.test.js +66 -0
- package/dist/__tests__/components/ParticipantView.test.js.map +1 -0
- package/dist/__tests__/mocks/call.d.ts +2 -0
- package/dist/__tests__/mocks/call.js +21 -0
- package/dist/__tests__/mocks/call.js.map +1 -0
- package/dist/__tests__/mocks/client.d.ts +2 -0
- package/dist/__tests__/mocks/client.js +31 -0
- package/dist/__tests__/mocks/client.js.map +1 -0
- package/dist/__tests__/mocks/participant.d.ts +3 -0
- package/dist/__tests__/mocks/participant.js +25 -0
- package/dist/__tests__/mocks/participant.js.map +1 -0
- package/dist/__tests__/utils/RNTLTools.d.ts +15 -0
- package/dist/__tests__/utils/RNTLTools.js +54 -0
- package/dist/__tests__/utils/RNTLTools.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/jest-setup.d.ts +1 -0
- package/dist/jest-setup.js +34 -0
- package/dist/jest-setup.js.map +1 -0
- package/dist/src/components/ActiveCall.d.ts +18 -0
- package/dist/src/components/ActiveCall.js +117 -0
- package/dist/src/components/ActiveCall.js.map +1 -0
- package/dist/src/components/Avatar.d.ts +25 -0
- package/dist/src/components/Avatar.js +55 -0
- package/dist/src/components/Avatar.js.map +1 -0
- package/dist/src/components/CallControlsButton.d.ts +22 -0
- package/dist/src/components/CallControlsButton.js +43 -0
- package/dist/src/components/CallControlsButton.js.map +1 -0
- package/dist/src/components/CallControlsView.d.ts +9 -0
- package/dist/src/components/CallControlsView.js +95 -0
- package/dist/src/components/CallControlsView.js.map +1 -0
- package/dist/src/components/CallParticipantsBadge.d.ts +1 -0
- package/dist/src/components/CallParticipantsBadge.js +48 -0
- package/dist/src/components/CallParticipantsBadge.js.map +1 -0
- package/dist/src/components/CallParticipantsInfoView.d.ts +21 -0
- package/dist/src/components/CallParticipantsInfoView.js +213 -0
- package/dist/src/components/CallParticipantsInfoView.js.map +1 -0
- package/dist/src/components/CallParticipantsList.d.ts +25 -0
- package/dist/src/components/CallParticipantsList.js +124 -0
- package/dist/src/components/CallParticipantsList.js.map +1 -0
- package/dist/src/components/CallParticipantsOptions.d.ts +8 -0
- package/dist/src/components/CallParticipantsOptions.js +189 -0
- package/dist/src/components/CallParticipantsOptions.js.map +1 -0
- package/dist/src/components/CallParticipantsSpotlightView.d.ts +1 -0
- package/dist/src/components/CallParticipantsSpotlightView.js +40 -0
- package/dist/src/components/CallParticipantsSpotlightView.js.map +1 -0
- package/dist/src/components/CallParticipantsView.d.ts +1 -0
- package/dist/src/components/CallParticipantsView.js +31 -0
- package/dist/src/components/CallParticipantsView.js.map +1 -0
- package/dist/src/components/IncomingCallView.d.ts +1 -0
- package/dist/src/components/IncomingCallView.js +98 -0
- package/dist/src/components/IncomingCallView.js.map +1 -0
- package/dist/src/components/LobbyView.d.ts +1 -0
- package/dist/src/components/LobbyView.js +215 -0
- package/dist/src/components/LobbyView.js.map +1 -0
- package/dist/src/components/LocalVideoView.d.ts +34 -0
- package/dist/src/components/LocalVideoView.js +100 -0
- package/dist/src/components/LocalVideoView.js.map +1 -0
- package/dist/src/components/NetworkQualityIndicator.d.ts +8 -0
- package/dist/src/components/NetworkQualityIndicator.js +36 -0
- package/dist/src/components/NetworkQualityIndicator.js.map +1 -0
- package/dist/src/components/OutgoingCallView.d.ts +1 -0
- package/dist/src/components/OutgoingCallView.js +103 -0
- package/dist/src/components/OutgoingCallView.js.map +1 -0
- package/dist/src/components/ParticipantReaction.d.ts +7 -0
- package/dist/src/components/ParticipantReaction.js +45 -0
- package/dist/src/components/ParticipantReaction.js.map +1 -0
- package/dist/src/components/ParticipantView.d.ts +42 -0
- package/dist/src/components/ParticipantView.js +256 -0
- package/dist/src/components/ParticipantView.js.map +1 -0
- package/dist/src/components/ReactionsModal.d.ts +7 -0
- package/dist/src/components/ReactionsModal.js +65 -0
- package/dist/src/components/ReactionsModal.js.map +1 -0
- package/dist/src/components/ToggleAudioButton.d.ts +1 -0
- package/dist/src/components/ToggleAudioButton.js +78 -0
- package/dist/src/components/ToggleAudioButton.js.map +1 -0
- package/dist/src/components/ToggleVideoButton.d.ts +1 -0
- package/dist/src/components/ToggleVideoButton.js +78 -0
- package/dist/src/components/ToggleVideoButton.js.map +1 -0
- package/dist/src/components/UserInfoView.d.ts +13 -0
- package/dist/src/components/UserInfoView.js +82 -0
- package/dist/src/components/UserInfoView.js.map +1 -0
- package/dist/src/components/VideoRenderer.d.ts +65 -0
- package/dist/src/components/VideoRenderer.js +18 -0
- package/dist/src/components/VideoRenderer.js.map +1 -0
- package/dist/src/components/index.d.ts +12 -0
- package/dist/src/components/index.js +29 -0
- package/dist/src/components/index.js.map +1 -0
- package/dist/src/constants/A11yLabels.d.ts +20 -0
- package/dist/src/constants/A11yLabels.js +29 -0
- package/dist/src/constants/A11yLabels.js.map +1 -0
- package/dist/src/constants/index.d.ts +11 -0
- package/dist/src/constants/index.js +30 -0
- package/dist/src/constants/index.js.map +1 -0
- package/dist/src/contexts/StreamVideoContext/createStoreContext.d.ts +14 -0
- package/dist/src/contexts/StreamVideoContext/createStoreContext.js +100 -0
- package/dist/src/contexts/StreamVideoContext/createStoreContext.js.map +1 -0
- package/dist/src/contexts/StreamVideoContext/index.d.ts +30 -0
- package/dist/src/contexts/StreamVideoContext/index.js +18 -0
- package/dist/src/contexts/StreamVideoContext/index.js.map +1 -0
- package/dist/src/contexts/index.d.ts +1 -0
- package/dist/src/contexts/index.js +18 -0
- package/dist/src/contexts/index.js.map +1 -0
- package/dist/src/hooks/index.d.ts +10 -0
- package/dist/src/hooks/index.js +27 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/useCallControls.d.ts +14 -0
- package/dist/src/hooks/useCallControls.js +148 -0
- package/dist/src/hooks/useCallControls.js.map +1 -0
- package/dist/src/hooks/useCallCycleEffect.d.ts +8 -0
- package/dist/src/hooks/useCallCycleEffect.js +62 -0
- package/dist/src/hooks/useCallCycleEffect.js.map +1 -0
- package/dist/src/hooks/useCallKeep.d.ts +11 -0
- package/dist/src/hooks/useCallKeep.js +41 -0
- package/dist/src/hooks/useCallKeep.js.map +1 -0
- package/dist/src/hooks/useCreateStreamVideoClient.d.ts +33 -0
- package/dist/src/hooks/useCreateStreamVideoClient.js +40 -0
- package/dist/src/hooks/useCreateStreamVideoClient.js.map +1 -0
- package/dist/src/hooks/useIncallManager.d.ts +13 -0
- package/dist/src/hooks/useIncallManager.js +24 -0
- package/dist/src/hooks/useIncallManager.js.map +1 -0
- package/dist/src/hooks/useLocalVideoStream.d.ts +7 -0
- package/dist/src/hooks/useLocalVideoStream.js +34 -0
- package/dist/src/hooks/useLocalVideoStream.js.map +1 -0
- package/dist/src/hooks/useMutingState.d.ts +12 -0
- package/dist/src/hooks/useMutingState.js +25 -0
- package/dist/src/hooks/useMutingState.js.map +1 -0
- package/dist/src/hooks/usePermissionNotification.d.ts +18 -0
- package/dist/src/hooks/usePermissionNotification.js +33 -0
- package/dist/src/hooks/usePermissionNotification.js.map +1 -0
- package/dist/src/hooks/usePermissionRequest.d.ts +1 -0
- package/dist/src/hooks/usePermissionRequest.js +62 -0
- package/dist/src/hooks/usePermissionRequest.js.map +1 -0
- package/dist/src/hooks/usePublishMediaStreams.d.ts +6 -0
- package/dist/src/hooks/usePublishMediaStreams.js +49 -0
- package/dist/src/hooks/usePublishMediaStreams.js.map +1 -0
- package/dist/src/icons/ArrowRight.d.ts +5 -0
- package/dist/src/icons/ArrowRight.js +9 -0
- package/dist/src/icons/ArrowRight.js.map +1 -0
- package/dist/src/icons/CameraSwitch.d.ts +5 -0
- package/dist/src/icons/CameraSwitch.js +9 -0
- package/dist/src/icons/CameraSwitch.js.map +1 -0
- package/dist/src/icons/Chat.d.ts +5 -0
- package/dist/src/icons/Chat.js +9 -0
- package/dist/src/icons/Chat.js.map +1 -0
- package/dist/src/icons/Cross.d.ts +5 -0
- package/dist/src/icons/Cross.js +9 -0
- package/dist/src/icons/Cross.js.map +1 -0
- package/dist/src/icons/Mic.d.ts +5 -0
- package/dist/src/icons/Mic.js +10 -0
- package/dist/src/icons/Mic.js.map +1 -0
- package/dist/src/icons/MicOff.d.ts +5 -0
- package/dist/src/icons/MicOff.js +9 -0
- package/dist/src/icons/MicOff.js.map +1 -0
- package/dist/src/icons/Participants.d.ts +5 -0
- package/dist/src/icons/Participants.js +9 -0
- package/dist/src/icons/Participants.js.map +1 -0
- package/dist/src/icons/Phone.d.ts +5 -0
- package/dist/src/icons/Phone.js +9 -0
- package/dist/src/icons/Phone.js.map +1 -0
- package/dist/src/icons/PhoneDown.d.ts +5 -0
- package/dist/src/icons/PhoneDown.js +10 -0
- package/dist/src/icons/PhoneDown.js.map +1 -0
- package/dist/src/icons/Pin.d.ts +5 -0
- package/dist/src/icons/Pin.js +9 -0
- package/dist/src/icons/Pin.js.map +1 -0
- package/dist/src/icons/Reaction.d.ts +5 -0
- package/dist/src/icons/Reaction.js +9 -0
- package/dist/src/icons/Reaction.js.map +1 -0
- package/dist/src/icons/ScreenShare.d.ts +5 -0
- package/dist/src/icons/ScreenShare.js +40 -0
- package/dist/src/icons/ScreenShare.js.map +1 -0
- package/dist/src/icons/Settings.d.ts +5 -0
- package/dist/src/icons/Settings.js +34 -0
- package/dist/src/icons/Settings.js.map +1 -0
- package/dist/src/icons/Spotlight.d.ts +5 -0
- package/dist/src/icons/Spotlight.js +9 -0
- package/dist/src/icons/Spotlight.js.map +1 -0
- package/dist/src/icons/ThreeDots.d.ts +5 -0
- package/dist/src/icons/ThreeDots.js +11 -0
- package/dist/src/icons/ThreeDots.js.map +1 -0
- package/dist/src/icons/Video.d.ts +5 -0
- package/dist/src/icons/Video.js +9 -0
- package/dist/src/icons/Video.js.map +1 -0
- package/dist/src/icons/VideoDisabled.d.ts +5 -0
- package/dist/src/icons/VideoDisabled.js +10 -0
- package/dist/src/icons/VideoDisabled.js.map +1 -0
- package/dist/src/icons/VideoOff.d.ts +5 -0
- package/dist/src/icons/VideoOff.js +10 -0
- package/dist/src/icons/VideoOff.js.map +1 -0
- package/dist/src/icons/VideoSlash.d.ts +5 -0
- package/dist/src/icons/VideoSlash.js +10 -0
- package/dist/src/icons/VideoSlash.js.map +1 -0
- package/dist/src/icons/index.d.ts +19 -0
- package/dist/src/icons/index.js +36 -0
- package/dist/src/icons/index.js.map +1 -0
- package/dist/src/providers/MediaDevices.d.ts +9 -0
- package/dist/src/providers/MediaDevices.js +43 -0
- package/dist/src/providers/MediaDevices.js.map +1 -0
- package/dist/src/providers/StreamCall.d.ts +81 -0
- package/dist/src/providers/StreamCall.js +74 -0
- package/dist/src/providers/StreamCall.js.map +1 -0
- package/dist/src/providers/StreamVideo.d.ts +10 -0
- package/dist/src/providers/StreamVideo.js +66 -0
- package/dist/src/providers/StreamVideo.js.map +1 -0
- package/dist/src/providers/index.d.ts +2 -0
- package/dist/src/providers/index.js +19 -0
- package/dist/src/providers/index.js.map +1 -0
- package/dist/src/theme/avatar.d.ts +2 -0
- package/dist/src/theme/avatar.js +11 -0
- package/dist/src/theme/avatar.js.map +1 -0
- package/dist/src/theme/button.d.ts +2 -0
- package/dist/src/theme/button.js +11 -0
- package/dist/src/theme/button.js.map +1 -0
- package/dist/src/theme/colors.d.ts +3 -0
- package/dist/src/theme/colors.js +49 -0
- package/dist/src/theme/colors.js.map +1 -0
- package/dist/src/theme/constants.d.ts +47 -0
- package/dist/src/theme/constants.js +54 -0
- package/dist/src/theme/constants.js.map +1 -0
- package/dist/src/theme/fonts.d.ts +2 -0
- package/dist/src/theme/fonts.js +67 -0
- package/dist/src/theme/fonts.js.map +1 -0
- package/dist/src/theme/icon.d.ts +2 -0
- package/dist/src/theme/icon.js +11 -0
- package/dist/src/theme/icon.js.map +1 -0
- package/dist/src/theme/index.d.ts +2 -0
- package/dist/src/theme/index.js +26 -0
- package/dist/src/theme/index.js.map +1 -0
- package/dist/src/theme/margin.d.ts +2 -0
- package/dist/src/theme/margin.js +11 -0
- package/dist/src/theme/margin.js.map +1 -0
- package/dist/src/theme/padding.d.ts +2 -0
- package/dist/src/theme/padding.js +11 -0
- package/dist/src/theme/padding.js.map +1 -0
- package/dist/src/theme/rounded.d.ts +2 -0
- package/dist/src/theme/rounded.js +11 -0
- package/dist/src/theme/rounded.js.map +1 -0
- package/dist/src/theme/spacing.d.ts +2 -0
- package/dist/src/theme/spacing.js +11 -0
- package/dist/src/theme/spacing.js.map +1 -0
- package/dist/src/theme/types.d.ts +45 -0
- package/dist/src/theme/types.js +3 -0
- package/dist/src/theme/types.js.map +1 -0
- package/dist/src/utils/StreamVideoRN.d.ts +25 -0
- package/dist/src/utils/StreamVideoRN.js +23 -0
- package/dist/src/utils/StreamVideoRN.js.map +1 -0
- package/dist/src/utils/hooks/index.d.ts +3 -0
- package/dist/src/utils/hooks/index.js +20 -0
- package/dist/src/utils/hooks/index.js.map +1 -0
- package/dist/src/utils/hooks/useAppStateListener.d.ts +1 -0
- package/dist/src/utils/hooks/useAppStateListener.js +41 -0
- package/dist/src/utils/hooks/useAppStateListener.js.map +1 -0
- package/dist/src/utils/hooks/useDebouncedValue.d.ts +7 -0
- package/dist/src/utils/hooks/useDebouncedValue.js +22 -0
- package/dist/src/utils/hooks/useDebouncedValue.js.map +1 -0
- package/dist/src/utils/hooks/usePrevious.d.ts +1 -0
- package/dist/src/utils/hooks/usePrevious.js +13 -0
- package/dist/src/utils/hooks/usePrevious.js.map +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.js +53 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/verifyAndroidBluetoothPermissions.d.ts +1 -0
- package/dist/src/utils/verifyAndroidBluetoothPermissions.js +30 -0
- package/dist/src/utils/verifyAndroidBluetoothPermissions.js.map +1 -0
- package/index.ts +22 -0
- package/package.json +70 -0
- package/src/components/ActiveCall.tsx +122 -0
- package/src/components/Avatar.tsx +88 -0
- package/src/components/CallControlsButton.tsx +72 -0
- package/src/components/CallControlsView.tsx +128 -0
- package/src/components/CallParticipantsBadge.tsx +57 -0
- package/src/components/CallParticipantsInfoView.tsx +287 -0
- package/src/components/CallParticipantsList.tsx +172 -0
- package/src/components/CallParticipantsOptions.tsx +248 -0
- package/src/components/CallParticipantsSpotlightView.tsx +53 -0
- package/src/components/CallParticipantsView.tsx +30 -0
- package/src/components/IncomingCallView.tsx +133 -0
- package/src/components/LobbyView.tsx +236 -0
- package/src/components/LocalVideoView.tsx +141 -0
- package/src/components/NetworkQualityIndicator.tsx +72 -0
- package/src/components/OutgoingCallView.tsx +133 -0
- package/src/components/ParticipantReaction.tsx +59 -0
- package/src/components/ParticipantView.tsx +315 -0
- package/src/components/ReactionsModal.tsx +90 -0
- package/src/components/ToggleAudioButton.tsx +103 -0
- package/src/components/ToggleVideoButton.tsx +103 -0
- package/src/components/UserInfoView.tsx +118 -0
- package/src/components/VideoRenderer.tsx +85 -0
- package/src/components/index.ts +12 -0
- package/src/constants/A11yLabels.ts +24 -0
- package/src/constants/index.ts +28 -0
- package/src/contexts/StreamVideoContext/createStoreContext.tsx +117 -0
- package/src/contexts/StreamVideoContext/index.tsx +37 -0
- package/src/contexts/index.ts +1 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/useCallControls.tsx +186 -0
- package/src/hooks/useCallCycleEffect.tsx +68 -0
- package/src/hooks/useCallKeep.tsx +51 -0
- package/src/hooks/useCreateStreamVideoClient.tsx +77 -0
- package/src/hooks/useIncallManager.tsx +23 -0
- package/src/hooks/useLocalVideoStream.ts +36 -0
- package/src/hooks/useMutingState.ts +24 -0
- package/src/hooks/usePermissionNotification.tsx +53 -0
- package/src/hooks/usePermissionRequest.tsx +67 -0
- package/src/hooks/usePublishMediaStreams.ts +59 -0
- package/src/icons/ArrowRight.tsx +16 -0
- package/src/icons/CameraSwitch.tsx +14 -0
- package/src/icons/Chat.tsx +14 -0
- package/src/icons/Cross.tsx +14 -0
- package/src/icons/Mic.tsx +18 -0
- package/src/icons/MicOff.tsx +14 -0
- package/src/icons/Participants.tsx +16 -0
- package/src/icons/Phone.tsx +14 -0
- package/src/icons/PhoneDown.tsx +15 -0
- package/src/icons/Pin.tsx +16 -0
- package/src/icons/Reaction.tsx +14 -0
- package/src/icons/ScreenShare.tsx +32 -0
- package/src/icons/Settings.tsx +13 -0
- package/src/icons/Spotlight.tsx +16 -0
- package/src/icons/ThreeDots.tsx +13 -0
- package/src/icons/Video.tsx +14 -0
- package/src/icons/VideoDisabled.tsx +22 -0
- package/src/icons/VideoOff.tsx +20 -0
- package/src/icons/VideoSlash.tsx +15 -0
- package/src/icons/index.tsx +19 -0
- package/src/providers/MediaDevices.tsx +45 -0
- package/src/providers/StreamCall.tsx +136 -0
- package/src/providers/StreamVideo.tsx +50 -0
- package/src/providers/index.ts +2 -0
- package/src/theme/avatar.ts +9 -0
- package/src/theme/button.ts +9 -0
- package/src/theme/colors.ts +49 -0
- package/src/theme/constants.ts +51 -0
- package/src/theme/fonts.ts +65 -0
- package/src/theme/icon.ts +9 -0
- package/src/theme/index.ts +25 -0
- package/src/theme/margin.ts +9 -0
- package/src/theme/padding.ts +9 -0
- package/src/theme/rounded.ts +9 -0
- package/src/theme/spacing.ts +9 -0
- package/src/theme/types.ts +64 -0
- package/src/utils/StreamVideoRN.ts +35 -0
- package/src/utils/hooks/index.ts +3 -0
- package/src/utils/hooks/useAppStateListener.ts +48 -0
- package/src/utils/hooks/useDebouncedValue.ts +21 -0
- package/src/utils/hooks/usePrevious.ts +11 -0
- package/src/utils/index.ts +45 -0
- package/src/utils/verifyAndroidBluetoothPermissions.ts +31 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OwnCapability,
|
|
3
|
+
SfuModels,
|
|
4
|
+
StreamVideoParticipant,
|
|
5
|
+
} from '@stream-io/video-client';
|
|
6
|
+
import {
|
|
7
|
+
Restricted,
|
|
8
|
+
useCall,
|
|
9
|
+
useConnectedUser,
|
|
10
|
+
useHasPermissions,
|
|
11
|
+
useParticipantCount,
|
|
12
|
+
useParticipants,
|
|
13
|
+
} from '@stream-io/video-react-bindings';
|
|
14
|
+
import {
|
|
15
|
+
Alert,
|
|
16
|
+
FlatList,
|
|
17
|
+
Modal,
|
|
18
|
+
Pressable,
|
|
19
|
+
StyleSheet,
|
|
20
|
+
Text,
|
|
21
|
+
View,
|
|
22
|
+
} from 'react-native';
|
|
23
|
+
import { ArrowRight, Cross, MicOff, ScreenShare, VideoSlash } from '../icons';
|
|
24
|
+
import React, { useCallback, useState } from 'react';
|
|
25
|
+
import { generateParticipantTitle } from '../utils';
|
|
26
|
+
import { CallParticipantOptions } from './CallParticipantsOptions';
|
|
27
|
+
import { Avatar } from './Avatar';
|
|
28
|
+
import { theme } from '../theme';
|
|
29
|
+
|
|
30
|
+
type CallParticipantInfoViewType = {
|
|
31
|
+
participant: StreamVideoParticipant;
|
|
32
|
+
setSelectedParticipant: React.Dispatch<
|
|
33
|
+
React.SetStateAction<StreamVideoParticipant | undefined>
|
|
34
|
+
>;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const CallParticipantInfoItem = (props: CallParticipantInfoViewType) => {
|
|
38
|
+
const { participant, setSelectedParticipant } = props;
|
|
39
|
+
const connectedUser = useConnectedUser();
|
|
40
|
+
const participantIsLoggedInUser = participant.userId === connectedUser?.id;
|
|
41
|
+
const userHasMuteUsersCapability = useHasPermissions(
|
|
42
|
+
OwnCapability.MUTE_USERS,
|
|
43
|
+
);
|
|
44
|
+
const userHasUpdateCallPermissionsCapability = useHasPermissions(
|
|
45
|
+
OwnCapability.UPDATE_CALL_PERMISSIONS,
|
|
46
|
+
);
|
|
47
|
+
const userHasBlockUserCapability = useHasPermissions(
|
|
48
|
+
OwnCapability.BLOCK_USERS,
|
|
49
|
+
);
|
|
50
|
+
const optionsOpenHandler = useCallback(() => {
|
|
51
|
+
if (!participantIsLoggedInUser) setSelectedParticipant(participant);
|
|
52
|
+
}, [participant, setSelectedParticipant, participantIsLoggedInUser]);
|
|
53
|
+
|
|
54
|
+
if (!participant) return null;
|
|
55
|
+
const { publishedTracks } = participant;
|
|
56
|
+
const isAudioMuted = !publishedTracks.includes(SfuModels.TrackType.AUDIO);
|
|
57
|
+
const isVideoMuted = !publishedTracks.includes(SfuModels.TrackType.VIDEO);
|
|
58
|
+
const isScreenSharing = publishedTracks.includes(
|
|
59
|
+
SfuModels.TrackType.SCREEN_SHARE,
|
|
60
|
+
);
|
|
61
|
+
const isParticipantItemPressable =
|
|
62
|
+
userHasBlockUserCapability ||
|
|
63
|
+
userHasMuteUsersCapability ||
|
|
64
|
+
userHasUpdateCallPermissionsCapability;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Pressable
|
|
68
|
+
style={styles.participant}
|
|
69
|
+
onPress={optionsOpenHandler}
|
|
70
|
+
disabled={!isParticipantItemPressable}
|
|
71
|
+
>
|
|
72
|
+
<Avatar radius={theme.avatar.xs} participant={participant} />
|
|
73
|
+
|
|
74
|
+
<Text style={styles.name}>
|
|
75
|
+
{(participant.name || generateParticipantTitle(participant.userId)) +
|
|
76
|
+
(participantIsLoggedInUser ? ' (You)' : '')}
|
|
77
|
+
</Text>
|
|
78
|
+
<View style={styles.icons}>
|
|
79
|
+
{isScreenSharing && (
|
|
80
|
+
<View style={[styles.svgContainerStyle, theme.icon.md]}>
|
|
81
|
+
<ScreenShare color={theme.light.info} />
|
|
82
|
+
</View>
|
|
83
|
+
)}
|
|
84
|
+
{isAudioMuted && (
|
|
85
|
+
<View style={[styles.svgContainerStyle, theme.icon.sm]}>
|
|
86
|
+
<MicOff color={theme.light.error} />
|
|
87
|
+
</View>
|
|
88
|
+
)}
|
|
89
|
+
{isVideoMuted && (
|
|
90
|
+
<View style={[styles.svgContainerStyle, theme.icon.sm]}>
|
|
91
|
+
<VideoSlash color={theme.light.error} />
|
|
92
|
+
</View>
|
|
93
|
+
)}
|
|
94
|
+
{!participantIsLoggedInUser && (
|
|
95
|
+
<Restricted
|
|
96
|
+
requiredGrants={[
|
|
97
|
+
OwnCapability.MUTE_USERS,
|
|
98
|
+
OwnCapability.UPDATE_CALL_PERMISSIONS,
|
|
99
|
+
OwnCapability.BLOCK_USERS,
|
|
100
|
+
]}
|
|
101
|
+
>
|
|
102
|
+
<View style={[styles.svgContainerStyle, theme.icon.sm]}>
|
|
103
|
+
<ArrowRight color={theme.light.text_high_emphasis} />
|
|
104
|
+
</View>
|
|
105
|
+
</Restricted>
|
|
106
|
+
)}
|
|
107
|
+
</View>
|
|
108
|
+
</Pressable>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export interface CallParticipantsInfoViewType {
|
|
113
|
+
/**
|
|
114
|
+
* Boolean that decided whether the CallPartcipantsInfoView modal should be open or not.
|
|
115
|
+
*/
|
|
116
|
+
isCallParticipantsViewVisible: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* SetState function to set the value of the boolean field `isCallParticipantsViewVisible` depending upon whether the CallPartcipantsInfoView modal should be open or not.
|
|
119
|
+
*/
|
|
120
|
+
setIsCallParticipantsViewVisible: React.Dispatch<
|
|
121
|
+
React.SetStateAction<boolean>
|
|
122
|
+
>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Shows information about the call, it's participants in the call and
|
|
127
|
+
* their mute states, handler to trigger options (TBD, permissions not impl)
|
|
128
|
+
* and options to invite more people to the call.
|
|
129
|
+
*
|
|
130
|
+
* | Participants List | Options Modal is Open |
|
|
131
|
+
* | :--- | :----: |
|
|
132
|
+
* | | |
|
|
133
|
+
**/
|
|
134
|
+
export const CallParticipantsInfoView = ({
|
|
135
|
+
isCallParticipantsViewVisible,
|
|
136
|
+
setIsCallParticipantsViewVisible,
|
|
137
|
+
}: CallParticipantsInfoViewType) => {
|
|
138
|
+
const participants = useParticipants();
|
|
139
|
+
const participantCount = useParticipantCount();
|
|
140
|
+
const [selectedParticipant, setSelectedParticipant] = useState<
|
|
141
|
+
StreamVideoParticipant | undefined
|
|
142
|
+
>(undefined);
|
|
143
|
+
const call = useCall();
|
|
144
|
+
|
|
145
|
+
const muteAllParticipantsHandler = async () => {
|
|
146
|
+
try {
|
|
147
|
+
await call?.muteAllUsers('audio');
|
|
148
|
+
Alert.alert('Users Muted Successfully');
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.log('Error muting users', error);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const onCloseCallParticipantsViewVisible = () => {
|
|
155
|
+
setIsCallParticipantsViewVisible(false);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const renderItem = useCallback(
|
|
159
|
+
({ item }: { item: StreamVideoParticipant }) => {
|
|
160
|
+
return (
|
|
161
|
+
<CallParticipantInfoItem
|
|
162
|
+
key={item.sessionId}
|
|
163
|
+
participant={item}
|
|
164
|
+
setSelectedParticipant={setSelectedParticipant}
|
|
165
|
+
/>
|
|
166
|
+
);
|
|
167
|
+
},
|
|
168
|
+
[],
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<Modal
|
|
173
|
+
animationType="slide"
|
|
174
|
+
transparent={true}
|
|
175
|
+
visible={isCallParticipantsViewVisible}
|
|
176
|
+
onRequestClose={onCloseCallParticipantsViewVisible}
|
|
177
|
+
>
|
|
178
|
+
<View style={styles.container}>
|
|
179
|
+
<View style={styles.content}>
|
|
180
|
+
<View style={styles.header}>
|
|
181
|
+
<View style={styles.leftHeaderElement} />
|
|
182
|
+
<Text style={styles.headerText}>
|
|
183
|
+
Participants ({participantCount})
|
|
184
|
+
</Text>
|
|
185
|
+
<Pressable
|
|
186
|
+
style={[styles.closeIcon, theme.icon.sm]}
|
|
187
|
+
onPress={onCloseCallParticipantsViewVisible}
|
|
188
|
+
>
|
|
189
|
+
<Cross color={theme.light.primary} />
|
|
190
|
+
</Pressable>
|
|
191
|
+
</View>
|
|
192
|
+
<View style={styles.buttonGroup}>
|
|
193
|
+
<Restricted requiredGrants={[OwnCapability.MUTE_USERS]}>
|
|
194
|
+
<Pressable
|
|
195
|
+
style={styles.button}
|
|
196
|
+
onPress={muteAllParticipantsHandler}
|
|
197
|
+
>
|
|
198
|
+
<Text style={styles.buttonText}>Mute All</Text>
|
|
199
|
+
</Pressable>
|
|
200
|
+
</Restricted>
|
|
201
|
+
</View>
|
|
202
|
+
<FlatList data={participants} renderItem={renderItem} />
|
|
203
|
+
{selectedParticipant && (
|
|
204
|
+
<View style={[StyleSheet.absoluteFill, styles.modal]}>
|
|
205
|
+
<CallParticipantOptions
|
|
206
|
+
participant={selectedParticipant}
|
|
207
|
+
setSelectedParticipant={setSelectedParticipant}
|
|
208
|
+
/>
|
|
209
|
+
</View>
|
|
210
|
+
)}
|
|
211
|
+
</View>
|
|
212
|
+
</View>
|
|
213
|
+
</Modal>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const styles = StyleSheet.create({
|
|
218
|
+
container: {
|
|
219
|
+
flex: 1,
|
|
220
|
+
justifyContent: 'center',
|
|
221
|
+
},
|
|
222
|
+
content: {
|
|
223
|
+
flex: 1,
|
|
224
|
+
backgroundColor: theme.light.bars,
|
|
225
|
+
borderRadius: theme.rounded.md,
|
|
226
|
+
marginVertical: theme.margin.lg,
|
|
227
|
+
marginHorizontal: theme.margin.md,
|
|
228
|
+
},
|
|
229
|
+
header: {
|
|
230
|
+
display: 'flex',
|
|
231
|
+
flexDirection: 'row',
|
|
232
|
+
justifyContent: 'space-between',
|
|
233
|
+
alignItems: 'center',
|
|
234
|
+
paddingVertical: theme.padding.md,
|
|
235
|
+
width: '100%',
|
|
236
|
+
},
|
|
237
|
+
leftHeaderElement: {
|
|
238
|
+
marginLeft: theme.margin.md,
|
|
239
|
+
},
|
|
240
|
+
headerText: {
|
|
241
|
+
...theme.fonts.bodyBold,
|
|
242
|
+
color: theme.light.text_high_emphasis,
|
|
243
|
+
},
|
|
244
|
+
closeIcon: {
|
|
245
|
+
marginRight: theme.margin.md,
|
|
246
|
+
},
|
|
247
|
+
buttonGroup: {},
|
|
248
|
+
button: {
|
|
249
|
+
backgroundColor: theme.light.primary,
|
|
250
|
+
borderRadius: theme.rounded.lg,
|
|
251
|
+
padding: theme.padding.md,
|
|
252
|
+
margin: theme.margin.lg,
|
|
253
|
+
},
|
|
254
|
+
buttonText: {
|
|
255
|
+
textAlign: 'center',
|
|
256
|
+
color: theme.light.static_white,
|
|
257
|
+
...theme.fonts.subtitleBold,
|
|
258
|
+
},
|
|
259
|
+
participant: {
|
|
260
|
+
paddingHorizontal: theme.padding.sm,
|
|
261
|
+
paddingVertical: theme.padding.xs,
|
|
262
|
+
display: 'flex',
|
|
263
|
+
flexDirection: 'row',
|
|
264
|
+
alignItems: 'center',
|
|
265
|
+
borderBottomColor: theme.light.borders,
|
|
266
|
+
borderBottomWidth: 1,
|
|
267
|
+
},
|
|
268
|
+
name: {
|
|
269
|
+
marginLeft: theme.margin.sm,
|
|
270
|
+
color: theme.light.text_high_emphasis,
|
|
271
|
+
...theme.fonts.subtitleBold,
|
|
272
|
+
},
|
|
273
|
+
icons: {
|
|
274
|
+
position: 'absolute',
|
|
275
|
+
right: theme.spacing.lg,
|
|
276
|
+
display: 'flex',
|
|
277
|
+
flexDirection: 'row',
|
|
278
|
+
},
|
|
279
|
+
svgContainerStyle: {
|
|
280
|
+
marginLeft: theme.margin.sm,
|
|
281
|
+
},
|
|
282
|
+
modal: {
|
|
283
|
+
alignItems: 'center',
|
|
284
|
+
justifyContent: 'center',
|
|
285
|
+
backgroundColor: theme.light.overlay,
|
|
286
|
+
},
|
|
287
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useMemo,
|
|
4
|
+
useReducer,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import { FlatList, StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
|
9
|
+
import { ParticipantView } from './ParticipantView';
|
|
10
|
+
import {
|
|
11
|
+
StreamVideoLocalParticipant,
|
|
12
|
+
StreamVideoParticipant,
|
|
13
|
+
StreamVideoParticipantPatches,
|
|
14
|
+
VisibilityState,
|
|
15
|
+
} from '@stream-io/video-client';
|
|
16
|
+
import { theme } from '../theme';
|
|
17
|
+
import { useDebouncedValue } from '../utils/hooks/useDebouncedValue';
|
|
18
|
+
import { useCall } from '@stream-io/video-react-bindings';
|
|
19
|
+
import { A11yComponents } from '../constants/A11yLabels';
|
|
20
|
+
|
|
21
|
+
type FlatListProps = React.ComponentProps<
|
|
22
|
+
typeof FlatList<StreamVideoParticipant | StreamVideoLocalParticipant>
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
const VIEWABILITY_CONFIG: FlatListProps['viewabilityConfig'] = {
|
|
26
|
+
waitForInteraction: false,
|
|
27
|
+
itemVisiblePercentThreshold: 60,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The props for the CallParticipantsList component
|
|
32
|
+
*/
|
|
33
|
+
interface CallParticipantsListProps {
|
|
34
|
+
/**
|
|
35
|
+
* The list of participants to display in the list
|
|
36
|
+
*/
|
|
37
|
+
participants: (StreamVideoParticipant | StreamVideoLocalParticipant)[];
|
|
38
|
+
/**
|
|
39
|
+
* The number of columns to display in the list of participants while in vertical or horizontal scrolling mode
|
|
40
|
+
* @default 2
|
|
41
|
+
*/
|
|
42
|
+
numColumns?: number;
|
|
43
|
+
/**
|
|
44
|
+
* If true, the list will be displayed in horizontal scrolling mode
|
|
45
|
+
*/
|
|
46
|
+
horizontal?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The CallParticipantsList component displays a list of participants in a FlatList
|
|
51
|
+
* NOTE: this component depends on a flex container to calculate the width and height of the participant view, hence it should be used only in a flex parent container
|
|
52
|
+
*/
|
|
53
|
+
export const CallParticipantsList = (props: CallParticipantsListProps) => {
|
|
54
|
+
const { numColumns = 2, horizontal, participants } = props;
|
|
55
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
56
|
+
|
|
57
|
+
// we use a HashSet to track the currently viewable participants
|
|
58
|
+
// and a separate force update state to rerender the component to inform that the HashSet has changed
|
|
59
|
+
// NOTE: we use set instead of array or object for O(1) lookup, add and delete
|
|
60
|
+
const viewableParticipantSessionIds = useRef<Set<string>>(new Set());
|
|
61
|
+
const [_forceUpdateValue, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
62
|
+
const forceUpdateValue = useDebouncedValue(_forceUpdateValue, 500); // we debounce forced value to avoid multiple viewability change continuous rerenders due to callbacks that occurs simultaneously during a large list scroll or when scrolling is completed
|
|
63
|
+
|
|
64
|
+
// we use a ref to store the active call object
|
|
65
|
+
// so that it can be used in the onViewableItemsChanged callback
|
|
66
|
+
const activeCall = useCall();
|
|
67
|
+
const activeCallRef = useRef(activeCall);
|
|
68
|
+
activeCallRef.current = activeCall;
|
|
69
|
+
// This is the function that gets called when the user scrolls the list of participants.
|
|
70
|
+
// It updates viewableParticipantSessionIds HashSet with the session IDs
|
|
71
|
+
// of the participants that are currently visible.
|
|
72
|
+
const onViewableItemsChanged = useRef<
|
|
73
|
+
FlatListProps['onViewableItemsChanged']
|
|
74
|
+
>(({ viewableItems }) => {
|
|
75
|
+
const participantPatches: StreamVideoParticipantPatches = {};
|
|
76
|
+
let mustUpdate = false;
|
|
77
|
+
const newVisibleParticipantSessionIds = new Set<string>(
|
|
78
|
+
viewableItems.map((v) => v.key),
|
|
79
|
+
);
|
|
80
|
+
const oldVisibleParticipantSessionIds =
|
|
81
|
+
viewableParticipantSessionIds.current;
|
|
82
|
+
newVisibleParticipantSessionIds.forEach((key) => {
|
|
83
|
+
if (!oldVisibleParticipantSessionIds.has(key)) {
|
|
84
|
+
mustUpdate = true;
|
|
85
|
+
participantPatches[key] = {
|
|
86
|
+
viewportVisibilityState: VisibilityState.VISIBLE,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
oldVisibleParticipantSessionIds.forEach((key) => {
|
|
91
|
+
if (!newVisibleParticipantSessionIds.has(key)) {
|
|
92
|
+
mustUpdate = true;
|
|
93
|
+
participantPatches[key] = {
|
|
94
|
+
viewportVisibilityState: VisibilityState.INVISIBLE,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
viewableParticipantSessionIds.current = newVisibleParticipantSessionIds;
|
|
99
|
+
if (mustUpdate) {
|
|
100
|
+
activeCallRef.current?.state.updateParticipants(participantPatches);
|
|
101
|
+
forceUpdate();
|
|
102
|
+
}
|
|
103
|
+
}).current;
|
|
104
|
+
|
|
105
|
+
// NOTE: key must be sessionId always as it is used to track viewable participants
|
|
106
|
+
const keyExtractor = useRef<FlatListProps['keyExtractor']>(
|
|
107
|
+
(item) => item.sessionId,
|
|
108
|
+
).current;
|
|
109
|
+
|
|
110
|
+
const onLayout = useRef<FlatListProps['onLayout']>((event) => {
|
|
111
|
+
const { width } = event.nativeEvent.layout;
|
|
112
|
+
setContainerWidth(width);
|
|
113
|
+
}).current;
|
|
114
|
+
|
|
115
|
+
const itemContainerStyle = useMemo<StyleProp<ViewStyle>>(() => {
|
|
116
|
+
// we calculate the size of the participant view based on the containerWidth (the phone's screen width),
|
|
117
|
+
// number of columns and the margin between the views
|
|
118
|
+
const size = containerWidth / numColumns - theme.margin.sm * 2;
|
|
119
|
+
const style = { width: size, height: size };
|
|
120
|
+
if (horizontal) {
|
|
121
|
+
return [styles.participantWrapperHorizontal, style];
|
|
122
|
+
}
|
|
123
|
+
return [styles.participantWrapperVertical, style];
|
|
124
|
+
}, [horizontal, numColumns, containerWidth]);
|
|
125
|
+
|
|
126
|
+
const renderItem = useCallback<NonNullable<FlatListProps['renderItem']>>(
|
|
127
|
+
({ item: participant }) => {
|
|
128
|
+
const isVisible = viewableParticipantSessionIds.current.has(
|
|
129
|
+
participant.sessionId,
|
|
130
|
+
);
|
|
131
|
+
return (
|
|
132
|
+
<ParticipantView
|
|
133
|
+
participant={participant}
|
|
134
|
+
containerStyle={itemContainerStyle}
|
|
135
|
+
kind="video"
|
|
136
|
+
isVisible={isVisible}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
[itemContainerStyle],
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<FlatList
|
|
145
|
+
onLayout={onLayout}
|
|
146
|
+
key={!horizontal ? numColumns : undefined} // setting numColumns as key is a strict requirement of react-native to support changing numColumns on the fly
|
|
147
|
+
data={participants}
|
|
148
|
+
keyExtractor={keyExtractor}
|
|
149
|
+
viewabilityConfig={VIEWABILITY_CONFIG}
|
|
150
|
+
onViewableItemsChanged={onViewableItemsChanged}
|
|
151
|
+
renderItem={renderItem}
|
|
152
|
+
numColumns={!horizontal ? numColumns : undefined}
|
|
153
|
+
horizontal={horizontal}
|
|
154
|
+
showsHorizontalScrollIndicator={false}
|
|
155
|
+
extraData={`${forceUpdateValue}${containerWidth}`} // this is important to force re-render when visibility changes
|
|
156
|
+
accessibilityLabel={A11yComponents.CALL_PARTICIPANTS_LIST}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const styles = StyleSheet.create({
|
|
162
|
+
participantWrapperVertical: {
|
|
163
|
+
margin: theme.margin.sm,
|
|
164
|
+
overflow: 'hidden',
|
|
165
|
+
borderRadius: theme.rounded.sm,
|
|
166
|
+
},
|
|
167
|
+
participantWrapperHorizontal: {
|
|
168
|
+
marginHorizontal: theme.margin.sm,
|
|
169
|
+
overflow: 'hidden',
|
|
170
|
+
borderRadius: theme.rounded.sm,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OwnCapability,
|
|
3
|
+
SfuModels,
|
|
4
|
+
StreamVideoParticipant,
|
|
5
|
+
} from '@stream-io/video-client';
|
|
6
|
+
import { Cross, VideoDisabled } from '../icons';
|
|
7
|
+
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
8
|
+
import { generateParticipantTitle } from '../utils';
|
|
9
|
+
import { useCallback } from 'react';
|
|
10
|
+
import { Avatar } from './Avatar';
|
|
11
|
+
import { theme } from '../theme';
|
|
12
|
+
import { useCall, useHasPermissions } from '@stream-io/video-react-bindings';
|
|
13
|
+
|
|
14
|
+
type CallParticipantOptionType = {
|
|
15
|
+
title: string;
|
|
16
|
+
icon?: JSX.Element;
|
|
17
|
+
onPressHandler: () => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type CallParticipantOptionsType = {
|
|
21
|
+
participant: StreamVideoParticipant;
|
|
22
|
+
setSelectedParticipant: React.Dispatch<
|
|
23
|
+
React.SetStateAction<StreamVideoParticipant | undefined>
|
|
24
|
+
>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const CallParticipantOptions = (props: CallParticipantOptionsType) => {
|
|
28
|
+
const { participant, setSelectedParticipant } = props;
|
|
29
|
+
const call = useCall();
|
|
30
|
+
|
|
31
|
+
const grantPermission = async (permission: string) => {
|
|
32
|
+
await call?.updateUserPermissions({
|
|
33
|
+
user_id: participant.userId,
|
|
34
|
+
grant_permissions: [permission],
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const revokePermission = async (permission: string) => {
|
|
39
|
+
await call?.updateUserPermissions({
|
|
40
|
+
user_id: participant.userId,
|
|
41
|
+
revoke_permissions: [permission],
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const muteUser = async (mediaType: 'audio' | 'video') => {
|
|
46
|
+
await call?.muteUser(participant.userId, mediaType);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const muteUserAudio = async () => {
|
|
50
|
+
await muteUser('audio');
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const muteUserVideo = async () => {
|
|
54
|
+
await muteUser('video');
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const blockUser = async () => {
|
|
58
|
+
await call?.blockUser(participant.userId);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const userHasMuteUsersCapability = useHasPermissions(
|
|
62
|
+
OwnCapability.MUTE_USERS,
|
|
63
|
+
);
|
|
64
|
+
const userHasUpdateCallPermissionsCapability = useHasPermissions(
|
|
65
|
+
OwnCapability.UPDATE_CALL_PERMISSIONS,
|
|
66
|
+
);
|
|
67
|
+
const userHasBlockUserCapability = useHasPermissions(
|
|
68
|
+
OwnCapability.BLOCK_USERS,
|
|
69
|
+
);
|
|
70
|
+
const participantCanPublishVideo = participant.publishedTracks.includes(
|
|
71
|
+
SfuModels.TrackType.VIDEO,
|
|
72
|
+
);
|
|
73
|
+
const participantCanPublishAudio = participant.publishedTracks.includes(
|
|
74
|
+
SfuModels.TrackType.AUDIO,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const callMediaStreamMutePermissions: (CallParticipantOptionType | null)[] =
|
|
78
|
+
userHasMuteUsersCapability
|
|
79
|
+
? [
|
|
80
|
+
participantCanPublishVideo
|
|
81
|
+
? {
|
|
82
|
+
title: 'Mute Video',
|
|
83
|
+
onPressHandler: muteUserVideo,
|
|
84
|
+
}
|
|
85
|
+
: null,
|
|
86
|
+
participantCanPublishAudio
|
|
87
|
+
? {
|
|
88
|
+
title: 'Mute Audio',
|
|
89
|
+
onPressHandler: muteUserAudio,
|
|
90
|
+
}
|
|
91
|
+
: null,
|
|
92
|
+
]
|
|
93
|
+
: [];
|
|
94
|
+
|
|
95
|
+
const callMediaStreamPermissions: (CallParticipantOptionType | null)[] =
|
|
96
|
+
userHasUpdateCallPermissionsCapability
|
|
97
|
+
? [
|
|
98
|
+
{
|
|
99
|
+
icon: <VideoDisabled color={theme.light.text_high_emphasis} />,
|
|
100
|
+
title: 'Disable Video',
|
|
101
|
+
onPressHandler: async () =>
|
|
102
|
+
await revokePermission(OwnCapability.SEND_VIDEO),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: 'Disable Audio',
|
|
106
|
+
onPressHandler: async () =>
|
|
107
|
+
await revokePermission(OwnCapability.SEND_AUDIO),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
title: 'Allow Audio',
|
|
111
|
+
onPressHandler: async () =>
|
|
112
|
+
await grantPermission(OwnCapability.SEND_AUDIO),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
title: 'Allow Video',
|
|
116
|
+
onPressHandler: async () =>
|
|
117
|
+
await grantPermission(OwnCapability.SEND_VIDEO),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
title: 'Allow Screen Sharing',
|
|
121
|
+
onPressHandler: async () =>
|
|
122
|
+
await grantPermission(OwnCapability.SCREENSHARE),
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
title: 'Disable Screen Sharing',
|
|
126
|
+
onPressHandler: async () =>
|
|
127
|
+
await revokePermission(OwnCapability.SCREENSHARE),
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
: [];
|
|
131
|
+
|
|
132
|
+
const options: (CallParticipantOptionType | null)[] = [
|
|
133
|
+
userHasBlockUserCapability
|
|
134
|
+
? {
|
|
135
|
+
title: 'Block',
|
|
136
|
+
onPressHandler: blockUser,
|
|
137
|
+
}
|
|
138
|
+
: null,
|
|
139
|
+
...callMediaStreamMutePermissions,
|
|
140
|
+
...callMediaStreamPermissions,
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
const onCloseParticipantOptions = useCallback(() => {
|
|
144
|
+
setSelectedParticipant(undefined);
|
|
145
|
+
}, [setSelectedParticipant]);
|
|
146
|
+
|
|
147
|
+
const showYouLabel = participant.isLoggedInUser;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<View style={styles.container}>
|
|
151
|
+
<View style={styles.menu}>
|
|
152
|
+
<View style={styles.participantInfo}>
|
|
153
|
+
<View style={styles.userInfo}>
|
|
154
|
+
<Avatar radius={theme.avatar.xs} participant={participant} />
|
|
155
|
+
|
|
156
|
+
<Text style={styles.name}>
|
|
157
|
+
{generateParticipantTitle(participant.userId) +
|
|
158
|
+
(showYouLabel ? ' (You)' : '')}
|
|
159
|
+
</Text>
|
|
160
|
+
</View>
|
|
161
|
+
|
|
162
|
+
<Pressable
|
|
163
|
+
style={[styles.svgContainerStyle, theme.icon.sm]}
|
|
164
|
+
onPress={onCloseParticipantOptions}
|
|
165
|
+
>
|
|
166
|
+
<Cross color={theme.light.primary} />
|
|
167
|
+
</Pressable>
|
|
168
|
+
</View>
|
|
169
|
+
<View style={styles.options}>
|
|
170
|
+
{options.map((option, index) => {
|
|
171
|
+
if (!option) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const applyBottomPadding =
|
|
175
|
+
index < options.length - 1 ? styles.borderBottom : null;
|
|
176
|
+
|
|
177
|
+
const onPressHandler = () => {
|
|
178
|
+
option?.onPressHandler();
|
|
179
|
+
onCloseParticipantOptions();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<Pressable
|
|
184
|
+
style={[applyBottomPadding, styles.option]}
|
|
185
|
+
key={option.title}
|
|
186
|
+
onPress={onPressHandler}
|
|
187
|
+
>
|
|
188
|
+
<View style={[styles.svgContainerStyle, theme.icon.sm]}>
|
|
189
|
+
{option.icon}
|
|
190
|
+
</View>
|
|
191
|
+
<Text style={styles.title}>{option.title}</Text>
|
|
192
|
+
</Pressable>
|
|
193
|
+
);
|
|
194
|
+
})}
|
|
195
|
+
</View>
|
|
196
|
+
</View>
|
|
197
|
+
</View>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const styles = StyleSheet.create({
|
|
202
|
+
container: {
|
|
203
|
+
width: '100%',
|
|
204
|
+
height: '100%',
|
|
205
|
+
display: 'flex',
|
|
206
|
+
justifyContent: 'center',
|
|
207
|
+
paddingHorizontal: theme.padding.xl,
|
|
208
|
+
},
|
|
209
|
+
menu: {
|
|
210
|
+
backgroundColor: theme.light.bars,
|
|
211
|
+
borderRadius: theme.rounded.md,
|
|
212
|
+
},
|
|
213
|
+
participantInfo: {
|
|
214
|
+
display: 'flex',
|
|
215
|
+
flexDirection: 'row',
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
justifyContent: 'space-between',
|
|
218
|
+
padding: theme.padding.md,
|
|
219
|
+
},
|
|
220
|
+
userInfo: {
|
|
221
|
+
display: 'flex',
|
|
222
|
+
flexDirection: 'row',
|
|
223
|
+
alignItems: 'center',
|
|
224
|
+
},
|
|
225
|
+
name: {
|
|
226
|
+
marginLeft: theme.margin.sm,
|
|
227
|
+
...theme.fonts.subtitleBold,
|
|
228
|
+
color: theme.light.text_high_emphasis,
|
|
229
|
+
},
|
|
230
|
+
svgContainerStyle: {},
|
|
231
|
+
options: {},
|
|
232
|
+
option: {
|
|
233
|
+
paddingHorizontal: theme.padding.md,
|
|
234
|
+
paddingVertical: theme.padding.sm,
|
|
235
|
+
display: 'flex',
|
|
236
|
+
flexDirection: 'row',
|
|
237
|
+
alignItems: 'center',
|
|
238
|
+
},
|
|
239
|
+
title: {
|
|
240
|
+
marginLeft: theme.margin.md,
|
|
241
|
+
color: theme.light.text_high_emphasis,
|
|
242
|
+
...theme.fonts.subtitle,
|
|
243
|
+
},
|
|
244
|
+
borderBottom: {
|
|
245
|
+
borderBottomColor: theme.light.borders,
|
|
246
|
+
borderBottomWidth: 1,
|
|
247
|
+
},
|
|
248
|
+
});
|