diff --git a/react/features/base/ui/Tokens.js b/react/features/base/ui/Tokens.js index e5a0544f8..9d411a3ef 100644 --- a/react/features/base/ui/Tokens.js +++ b/react/features/base/ui/Tokens.js @@ -33,7 +33,8 @@ export const colors = { success04: '#189B55', success05: '#1EC26A', - warning05: '#F8AE1A' + warning05: '#F8AE1A', + warning06: '#ED9E1B' }; // Mapping between the token used and the color @@ -197,17 +198,21 @@ export const colorMap = { success02: 'success05', // Color for warning messages applied to icons, borders & backgrounds - warning01: 'warning05' + warning01: 'warning05', + + // Color for indicating a raised hand + warning02: 'warning06' }; export const font = { - weightRegular: 400, - weightSemiBold: 600 + weightRegular: '400', + weightSemiBold: '600' }; export const shape = { - borderRadius: 6 + borderRadius: 6, + boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)' }; export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56 ]; diff --git a/react/features/conference/components/web/Conference.js b/react/features/conference/components/web/Conference.js index c82920da3..764d94197 100644 --- a/react/features/conference/components/web/Conference.js +++ b/react/features/conference/components/web/Conference.js @@ -15,7 +15,7 @@ import { Filmstrip } from '../../../filmstrip'; import { CalleeInfoContainer } from '../../../invite'; import { LargeVideo } from '../../../large-video'; import { KnockingParticipantList, LobbyScreen } from '../../../lobby'; -import { ParticipantsPane } from '../../../participants-pane/components'; +import { ParticipantsPane } from '../../../participants-pane/components/web'; import { getParticipantsPaneOpen } from '../../../participants-pane/functions'; import { Prejoin, isPrejoinPageVisible } from '../../../prejoin'; import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web'; diff --git a/react/features/participants-pane/components/native/Button.js b/react/features/participants-pane/components/native/Button.js new file mode 100644 index 000000000..56737f972 --- /dev/null +++ b/react/features/participants-pane/components/native/Button.js @@ -0,0 +1,115 @@ +// @flow + +import React from 'react'; +import { View } from 'react-native'; +import { Button as PaperButton } from 'react-native-paper'; + +import { Icon } from '../../../base/icons'; + +let buttonContent; + +/** + * The type of the React {@code Component} props of {@link Button} + */ +type Props = { + + /** + * Is the button icon type? + */ + iconButton: boolean, + + /** + * Style for the icon + */ + iconStyle: Object, + + /** + * Size of the icon. + */ + iconSize: number, + + /** + * Icon component source. + */ + iconSrc?: Object, + + /** + * Button content. + */ + content: string, + + /** + * Button mode. + */ + mode: string, + + /** + * Style of button's inner content. + */ + contentStyle?: Object, + + /** + * The action to be performed when the button is pressed. + */ + onPress: Function, + + /** + * An external style object passed to the component. + */ + style: Object, + + /** + * Theme to be applied. + */ + theme: Object +}; + +/** + * Close button component. + * + * @returns {React$Element} + */ +function Button({ + iconButton, + iconStyle, + iconSize, + iconSrc, + content, + contentStyle, + mode, + onPress, + style, + theme +}: Props) { + + if (iconButton) { + buttonContent + = ( + + ); + + } else { + buttonContent = content; + } + + return ( + + { buttonContent } + + ); +} + +export default Button; diff --git a/react/features/participants-pane/components/native/LobbyParticipantItem.js b/react/features/participants-pane/components/native/LobbyParticipantItem.js new file mode 100644 index 000000000..e7c3ef443 --- /dev/null +++ b/react/features/participants-pane/components/native/LobbyParticipantItem.js @@ -0,0 +1,45 @@ +// @flow + +import React, { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'react-native-paper'; +import { useDispatch } from 'react-redux'; + +// import { setKnockingParticipantApproval } from '../../../lobby/actions'; +import { MediaState } from '../../constants'; + +import ParticipantItem from './ParticipantItem'; +import styles from './styles'; + +type Props = { + + /** + * Participant reference + */ + participant: Object +}; + +export const LobbyParticipantItem = ({ participant: p }: Props) => { + const dispatch = useDispatch(); + const admit = useCallback(() => dispatch(setKnockingParticipantApproval(p.id, true), [ dispatch ])); + const reject = useCallback(() => dispatch(setKnockingParticipantApproval(p.id, false), [ dispatch ])); + const { t } = useTranslation(); + + return ( + + + + + ); +}; diff --git a/react/features/participants-pane/components/native/LobbyParticipantList.js b/react/features/participants-pane/components/native/LobbyParticipantList.js new file mode 100644 index 000000000..746d4d465 --- /dev/null +++ b/react/features/participants-pane/components/native/LobbyParticipantList.js @@ -0,0 +1,12 @@ +// @flow + +import React from 'react'; +import { View } from 'react-native'; +import { useSelector } from 'react-redux'; + +// import { getLobbyState } from '../../../lobby/functions'; + +import { LobbyParticipantItem } from './LobbyParticipantItem'; +import { participants } from './participants'; + +export const LobbyParticipantList = () => null; diff --git a/react/features/participants-pane/components/native/MeetingParticipantItem.js b/react/features/participants-pane/components/native/MeetingParticipantItem.js new file mode 100644 index 000000000..6c39a4a3c --- /dev/null +++ b/react/features/participants-pane/components/native/MeetingParticipantItem.js @@ -0,0 +1,19 @@ +// @flow + +import React from 'react'; + +import ParticipantItem from './ParticipantItem'; + + +type Props = { + + /** + * Participant reference + */ + participant: Object +}; + +export const MeetingParticipantItem = ({ participant: p }: Props) => ( + +); diff --git a/react/features/participants-pane/components/native/MeetingParticipantList.js b/react/features/participants-pane/components/native/MeetingParticipantList.js new file mode 100644 index 000000000..59f7d3b87 --- /dev/null +++ b/react/features/participants-pane/components/native/MeetingParticipantList.js @@ -0,0 +1,36 @@ +// @flow + +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { View } from 'react-native'; +import { Text } from 'react-native-paper'; +import { useSelector } from 'react-redux'; + +import { getParticipants } from '../../../base/participants'; + +import { MeetingParticipantItem } from './MeetingParticipantItem'; +import styles from './styles'; + + +export const MeetingParticipantList = () => { + const participants = useSelector(getParticipants); + const { t } = useTranslation(); + + return ( + + + { + t('participantsPane.headings.participantsList', + { count: participants.length } + ) + } + + { + participants.map(p => ( + ) + )} + + ); +}; diff --git a/react/features/participants-pane/components/native/ParticipantItem.js b/react/features/participants-pane/components/native/ParticipantItem.js new file mode 100644 index 000000000..fa2af329f --- /dev/null +++ b/react/features/participants-pane/components/native/ParticipantItem.js @@ -0,0 +1,81 @@ +// @flow + +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { View } from 'react-native'; +import { Text } from 'react-native-paper'; +import { useSelector } from 'react-redux'; + +import { Avatar } from '../../../base/avatar'; +import { getParticipantDisplayNameWithId } from '../../../base/participants'; +import { ActionTrigger, MediaState } from '../../constants'; +// import { AudioStateIcons, VideoStateIcons } from '../web/ParticipantItem'; + +import { RaisedHandIndicator } from './RaisedHandIndicator'; +import styles from './styles'; + +type Props = { + + /** + * Type of trigger for the participant actions + */ + actionsTrigger: ActionTrigger, + + /** + * Media state for audio + */ + audioMuteState: MediaState, + + /** + * Callback for when the mouse leaves this component + */ + onLeave?: Function, + + /** + * Participant reference + */ + participant: Object, + + /** + * Media state for video + */ + videoMuteState: MediaState +} + +/** + * Participant item. + * + * @returns {React$Element} + */ +function ParticipantItem({ + audioMuteState = MediaState.None, + videoMuteState = MediaState.None, + participant: p +}: Props) { + const { t } = useTranslation(); + const name = useSelector(getParticipantDisplayNameWithId(p.id)); + + return ( + + + + + + { name } + + { p.local ? ({t('chat.you')}) : null } + + + {p.raisedHand && } + {VideoStateIcons[videoMuteState]} + {AudioStateIcons[audioMuteState]} + + + + ); +} + +export default ParticipantItem; diff --git a/react/features/participants-pane/components/native/ParticipantsPane.js b/react/features/participants-pane/components/native/ParticipantsPane.js index 661bf9a74..be3b18ede 100644 --- a/react/features/participants-pane/components/native/ParticipantsPane.js +++ b/react/features/participants-pane/components/native/ParticipantsPane.js @@ -1,17 +1,21 @@ // @flow import React, { useCallback } from 'react'; -import { Button, withTheme } from 'react-native-paper'; -import { useDispatch } from 'react-redux'; +import { useTranslation } from 'react-i18next'; +import { View } from 'react-native'; +import { withTheme } from 'react-native-paper'; +import { useDispatch, useSelector } from 'react-redux'; - -import { translate } from '../../../base/i18n'; -import { Icon, IconClose } from '../../../base/icons'; +import { IconClose, IconHorizontalPoints } from '../../../base/icons'; import { JitsiModal } from '../../../base/modal'; +import { isLocalParticipantModerator } from '../../../base/participants'; import { close } from '../../actions'; +import Button from './Button'; +import { LobbyParticipantList } from './LobbyParticipantList'; import styles from './styles'; + /** * {@code ParticipantsPane}'s React {@code Component} prop types. */ @@ -33,29 +37,58 @@ function ParticipantsPane({ theme }: Props) { const closePane = useCallback( () => dispatch(close()), [ dispatch ]); - + const isLocalModerator = useSelector(isLocalParticipantModerator); + const { t } = useTranslation(); const { palette } = theme; return ( - + +