You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ContextMenuMeetingParticipantDetails.js 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // @flow
  2. import React, { useCallback, useState } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. import { TouchableOpacity, View } from 'react-native';
  5. import { Divider, Text } from 'react-native-paper';
  6. import { useDispatch, useSelector, useStore } from 'react-redux';
  7. import { Avatar } from '../../../base/avatar';
  8. import { hideDialog, openDialog } from '../../../base/dialog';
  9. import BottomSheet from '../../../base/dialog/components/native/BottomSheet';
  10. import {
  11. Icon, IconCloseCircle, IconConnectionActive, IconMessage,
  12. IconMicrophoneEmptySlash,
  13. IconMuteEveryoneElse, IconVideoOff
  14. } from '../../../base/icons';
  15. import { isLocalParticipantModerator } from '../../../base/participants';
  16. import { getIsParticipantVideoMuted } from '../../../base/tracks';
  17. import { openChat } from '../../../chat/actions.native';
  18. import {
  19. KickRemoteParticipantDialog,
  20. MuteEveryoneDialog,
  21. MuteRemoteParticipantDialog,
  22. MuteRemoteParticipantsVideoDialog,
  23. VolumeSlider
  24. } from '../../../video-menu';
  25. import styles from './styles';
  26. type Props = {
  27. /**
  28. * Participant reference
  29. */
  30. participant: Object
  31. };
  32. export const ContextMenuMeetingParticipantDetails = ({ participant: p }: Props) => {
  33. const [ volume, setVolume ] = useState(undefined);
  34. const store = useStore();
  35. const startSilent = store.getState['features/base/config'];
  36. const dispatch = useDispatch();
  37. const cancel = useCallback(() => dispatch(hideDialog()), [ dispatch ]);
  38. const changeVolume = useCallback(() => setVolume(volume), [ volume ]);
  39. const displayName = p.name;
  40. const isLocalModerator = useSelector(isLocalParticipantModerator);
  41. const isParticipantVideoMuted = useSelector(getIsParticipantVideoMuted(p));
  42. const kickRemoteParticipant = useCallback(() => {
  43. dispatch(openDialog(KickRemoteParticipantDialog, {
  44. participantID: p.id
  45. }));
  46. }, [ dispatch, p ]);
  47. const muteAudio = useCallback(() => {
  48. dispatch(openDialog(MuteRemoteParticipantDialog, {
  49. participantID: p.id
  50. }));
  51. }, [ dispatch, p ]);
  52. const muteEveryoneElse = useCallback(() => {
  53. dispatch(openDialog(MuteEveryoneDialog, {
  54. exclude: [ p.id ]
  55. }));
  56. }, [ dispatch, p ]);
  57. const muteVideo = useCallback(() => {
  58. dispatch(openDialog(MuteRemoteParticipantsVideoDialog, {
  59. participantID: p.id
  60. }));
  61. }, [ dispatch, p ]);
  62. const onVolumeChange = startSilent ? undefined : changeVolume;
  63. const sendPrivateMessage = useCallback(() => {
  64. dispatch(hideDialog());
  65. dispatch(openChat(p));
  66. }, [ dispatch, p ]);
  67. const { t } = useTranslation();
  68. return (
  69. <BottomSheet
  70. onCancel = { cancel }
  71. style = { styles.contextMenuMore }>
  72. <View
  73. style = { styles.contextMenuItemSection }>
  74. <Avatar
  75. className = 'participant-avatar'
  76. participantId = { p.id }
  77. size = { 20 } />
  78. <View style = { styles.contextMenuItemText }>
  79. <Text style = { styles.contextMenuItemName }>
  80. { displayName }
  81. </Text>
  82. </View>
  83. </View>
  84. <Divider style = { styles.divider } />
  85. {
  86. isLocalModerator
  87. && <TouchableOpacity
  88. onPress = { muteAudio }
  89. style = { styles.contextMenuItem }>
  90. <Icon
  91. size = { 20 }
  92. src = { IconMicrophoneEmptySlash }
  93. style = { styles.contextMenuItemIcon } />
  94. <Text style = { styles.contextMenuItemText }>
  95. { t('participantsPane.actions.mute') }
  96. </Text>
  97. </TouchableOpacity>
  98. }
  99. {
  100. isLocalModerator
  101. && <TouchableOpacity
  102. onPress = { muteEveryoneElse }
  103. style = { styles.contextMenuItem }>
  104. <Icon
  105. size = { 20 }
  106. src = { IconMuteEveryoneElse }
  107. style = { styles.contextMenuItemIcon } />
  108. <Text style = { styles.contextMenuItemText }>
  109. { t('participantsPane.actions.muteEveryoneElse') }
  110. </Text>
  111. </TouchableOpacity>
  112. }
  113. <Divider style = { styles.divider } />
  114. {
  115. isLocalModerator && (
  116. isParticipantVideoMuted
  117. || <TouchableOpacity
  118. onPress = { muteVideo }
  119. style = { styles.contextMenuItemSection }>
  120. <Icon
  121. size = { 20 }
  122. src = { IconVideoOff }
  123. style = { styles.contextMenuItemIcon } />
  124. <Text style = { styles.contextMenuItemText }>
  125. { t('participantsPane.actions.stopVideo') }
  126. </Text>
  127. </TouchableOpacity>
  128. )
  129. }
  130. {
  131. isLocalModerator
  132. && <TouchableOpacity
  133. onPress = { kickRemoteParticipant }
  134. style = { styles.contextMenuItem }>
  135. <Icon
  136. size = { 20 }
  137. src = { IconCloseCircle }
  138. style = { styles.contextMenuItemIcon } />
  139. <Text style = { styles.contextMenuItemText }>
  140. { t('videothumbnail.kick') }
  141. </Text>
  142. </TouchableOpacity>
  143. }
  144. <TouchableOpacity
  145. onPress = { sendPrivateMessage }
  146. style = { styles.contextMenuItem }>
  147. <Icon
  148. size = { 20 }
  149. src = { IconMessage }
  150. style = { styles.contextMenuItemIcon } />
  151. <Text style = { styles.contextMenuItemText }>
  152. { t('toolbar.accessibilityLabel.privateMessage') }
  153. </Text>
  154. </TouchableOpacity>
  155. <TouchableOpacity
  156. style = { styles.contextMenuItemSection }>
  157. <Icon
  158. size = { 20 }
  159. src = { IconConnectionActive }
  160. style = { styles.contextMenuItemIcon } />
  161. <Text style = { styles.contextMenuItemText }>{ t('participantsPane.actions.networkStats') }</Text>
  162. </TouchableOpacity>
  163. <Divider style = { styles.divider } />
  164. <VolumeSlider
  165. initialValue = { volume }
  166. onChange = { onVolumeChange } />
  167. </BottomSheet>
  168. );
  169. };