feat(native-participants-pane) updated styles for button component and participant item related components

This commit is contained in:
Calin Chitu 2021-05-19 13:08:30 +03:00 committed by Hristo Terezov
parent 6597bfc2aa
commit 79edc1b358
23 changed files with 650 additions and 42 deletions

View File

@ -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 ];

View File

@ -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';

View File

@ -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<any>}
*/
function Button({
iconButton,
iconStyle,
iconSize,
iconSrc,
content,
contentStyle,
mode,
onPress,
style,
theme
}: Props) {
if (iconButton) {
buttonContent
= (<View
/* eslint-disable-next-line react-native/no-inline-styles */
style = {{
height: 0,
width: 0
}}>
<Icon
size = { iconSize }
src = { iconSrc }
style = { iconStyle } />
</View>);
} else {
buttonContent = content;
}
return (
<PaperButton
contentStyle = { contentStyle }
mode = { mode }
onPress = { onPress }
style = { style }
theme = { theme }>
{ buttonContent }
</PaperButton>
);
}
export default Button;

View File

@ -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 (
<ParticipantItem
audioMuteState = { MediaState.None }
participant = { p }
videoMuteState = { MediaState.None }>
<Button
onClick = { reject }
style = { styles.participantActionButton }>
{t('lobby.reject')}
</Button>
<Button
onClick = { admit }
style = { styles.participantActionButton }>
{t('lobby.admit')}
</Button>
</ParticipantItem>
);
};

View File

@ -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;

View File

@ -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) => (
<ParticipantItem
participant = { p } />
);

View File

@ -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 (
<View style = { styles.lobbyListContainer }>
<Text>
{
t('participantsPane.headings.participantsList',
{ count: participants.length }
)
}
</Text>
{
participants.map(p => (
<MeetingParticipantItem
key = { p.id }
participant = { p } />)
)}
</View>
);
};

View File

@ -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<any>}
*/
function ParticipantItem({
audioMuteState = MediaState.None,
videoMuteState = MediaState.None,
participant: p
}: Props) {
const { t } = useTranslation();
const name = useSelector(getParticipantDisplayNameWithId(p.id));
return (
<View style = { styles.participantContainer } >
<Avatar
className = 'participant-avatar'
participant = { p.id }
size = { 32 } />
<View style = { styles.participantContent }>
<View style = { styles.participantNameContainer }>
<Text style = { styles.participantName }>
{ name }
</Text>
{ p.local ? <Text>({t('chat.you')})</Text> : null }
</View>
<View style = { styles.participantStates } >
{p.raisedHand && <RaisedHandIndicator />}
{VideoStateIcons[videoMuteState]}
{AudioStateIcons[audioMuteState]}
</View>
</View>
</View>
);
}
export default ParticipantItem;

View File

@ -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,14 +37,21 @@ function ParticipantsPane({ theme }: Props) {
const closePane = useCallback(
() => dispatch(close()),
[ dispatch ]);
const isLocalModerator = useSelector(isLocalParticipantModerator);
const { t } = useTranslation();
const { palette } = theme;
return (
<JitsiModal
showHeaderWithNavigation = { false }
style = { styles.participantsPane }>
<View style = { styles.header }>
<Button
contentStyle = { styles.muteAllContent }
iconButton = { true }
iconSize = { 16 }
iconSrc = { IconClose }
iconStyle = { styles.closeIcon }
mode = 'contained'
onPress = { closePane }
style = { styles.closeButton }
@ -48,14 +59,36 @@ function ParticipantsPane({ theme }: Props) {
colors: {
primary: palette.action02
}
}}>
<Icon
src = { IconClose }
style = { styles.closeIcon } />
</Button>
}} />
</View>
<LobbyParticipantList />
{
isLocalModerator
&& <View style = { styles.footer }>
<Button
content = { t('participantsPane.actions.muteAll') }
contentStyle = { styles.muteAllContent }
onPress = { closePane }
style = { styles.muteAllButton } />
<Button
contentStyle = { styles.muteAllContent }
iconButton = { true }
iconSize = { 16 }
iconSrc = { IconHorizontalPoints }
iconStyle = { styles.moreIcon }
mode = 'contained'
onPress = { closePane }
style = { styles.moreButton }
theme = {{
colors: {
primary: palette.action02
}
}} />
</View>
}
</JitsiModal>
);
}
export default translate(withTheme(ParticipantsPane));
export default withTheme(ParticipantsPane);

View File

@ -0,0 +1,14 @@
import React from 'react';
import { View } from 'react-native';
import { Icon, IconRaisedHandHollow } from '../../../base/icons';
import styles from './styles';
export const RaisedHandIndicator = () => (
<View style = { styles.raisedHandIndicator }>
<Icon
size = { 15 }
src = { IconRaisedHandHollow } />
</View>
);

View File

@ -0,0 +1,102 @@
export const participants = [
{
audioOutputDeviceId: 'default',
avatarURL: undefined,
botType: undefined,
conference: undefined,
connectionStatus: undefined,
dominantSpeaker: false,
email: undefined,
id: 'd0816677',
isFakeParticipant: undefined,
isJigasi: undefined,
loadableAvatarUrl: undefined,
local: true,
name: 'testuser2',
pinned: false,
presence: undefined,
role: 'moderator',
startWithAudioMuted: true,
startWithVideoMuted: true
},
{
audioOutputDeviceId: 'default',
avatarURL: undefined,
botType: undefined,
conference: undefined,
connectionStatus: undefined,
dominantSpeaker: false,
email: undefined,
id: 'a0496597',
isFakeParticipant: undefined,
isJigasi: undefined,
loadableAvatarUrl: undefined,
local: true,
name: 'me',
pinned: false,
presence: undefined,
role: 'participant',
startWithAudioMuted: true,
startWithVideoMuted: false
},
{
audioOutputDeviceId: 'default',
avatarURL: undefined,
botType: undefined,
conference: undefined,
connectionStatus: undefined,
dominantSpeaker: false,
email: undefined,
id: 'b01081018',
isFakeParticipant: undefined,
isJigasi: undefined,
loadableAvatarUrl: undefined,
local: true,
name: 'Tom',
pinned: false,
presence: undefined,
role: 'participant',
startWithAudioMuted: true,
startWithVideoMuted: false
},
{
audioOutputDeviceId: 'default',
avatarURL: undefined,
botType: undefined,
conference: undefined,
connectionStatus: undefined,
dominantSpeaker: false,
email: undefined,
id: 'b0aad221e1',
isFakeParticipant: undefined,
isJigasi: undefined,
loadableAvatarUrl: undefined,
local: true,
name: 'George',
pinned: false,
presence: undefined,
role: 'participant',
startWithAudioMuted: true,
startWithVideoMuted: false
},
{
audioOutputDeviceId: 'default',
avatarURL: undefined,
botType: undefined,
conference: undefined,
connectionStatus: undefined,
dominantSpeaker: false,
email: undefined,
id: 'c0108301',
isFakeParticipant: undefined,
isJigasi: undefined,
loadableAvatarUrl: undefined,
local: true,
name: 'Carlin',
pinned: false,
presence: undefined,
role: 'participant',
startWithAudioMuted: true,
startWithVideoMuted: false
}
];

View File

@ -1,26 +1,172 @@
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
/**
* The style for content.
*/
const flexContent = {
alignItems: 'center',
color: BaseTheme.palette.icon01,
display: 'flex',
flex: 1
};
/**
* The style of the participants pane buttons.
*/
const container = {
alignItems: 'center',
display: 'flex',
flexDirection: 'row',
height: 64,
justifyContent: 'center',
paddingRight: 8,
width: '100%'
};
/**
* The style of the participants pane buttons.
*/
const button = {
alignItems: 'center',
backgroundColor: BaseTheme.palette.action02,
borderRadius: BaseTheme.shape.borderRadius,
display: 'flex',
height: 48,
marginLeft: 'auto'
};
/**
* Small buttons.
*/
const smallButton = {
...button,
width: 48
};
/**
* The style of the participants pane buttons description.
*/
const buttonContent = {
...BaseTheme.typography.labelButtonLarge,
...flexContent,
justifyContent: 'center'
};
/**
* The styles of the native components of the feature {@code participants}.
*/
export default {
participantActionButton: {
backgroundColor: BaseTheme.palette.action01,
borderRadius: BaseTheme.shape.borderRadius,
height: 40,
paddingTop: 8,
paddingBottom: 8,
paddingLeft: 16,
paddingRight: 16,
width: 73
},
participantContainer: {
alignItems: 'center',
color: BaseTheme.palette.text01,
display: 'flex',
fontSize: 13,
height: 64,
margin: BaseTheme.spacing[4],
position: 'relative',
width: 375
},
participantContent: {
...flexContent,
boxShadow: BaseTheme.shape.boxShadow,
height: '100%',
overflow: 'hidden',
paddingRight: BaseTheme.spacing[4]
},
participantNameContainer: {
display: 'flex',
flex: 1,
marginRight: BaseTheme.spacing[3],
overflow: 'hidden'
},
participantName: {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
},
participantsPane: {
backgroundColor: BaseTheme.palette.ui01,
padding: 16
backgroundColor: BaseTheme.palette.ui01
},
participantStates: {
display: 'flex',
justifyContent: 'flex-end'
},
raisedHandIndicator: {
backgroundColor: BaseTheme.palette.warning02,
borderRadius: BaseTheme.shape.borderRadius / 2,
height: 24,
width: 24
},
lobbyListContainer: {
...container
},
header: {
...container,
backgroundColor: 'red'
},
footer: {
...container,
backgroundColor: 'green',
marginTop: 'auto'
},
closeButton: {
alignItems: 'center',
display: 'flex',
height: 48,
justifyContent: 'center',
marginLeft: 'auto'
...smallButton
},
closeIcon: {
display: 'flex',
flex: 1,
fontSize: 24,
justifyContent: 'center'
...buttonContent
},
moreButton: {
...smallButton
},
moreIcon: {
...buttonContent
},
moreButtonPaper: {
...smallButton,
height: 48
},
moreIconPaper: {
alignItems: 'center',
flexDirection: 'row-reverse'
},
muteAllButton: {
...button,
paddingBottom: 12,
paddingLeft: 16,
paddingRight: 16,
paddingTop: 12,
width: 94
},
muteAllContent: {
...buttonContent
}
};

View File

@ -4,9 +4,9 @@ import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { createToolbarEvent, sendAnalytics } from '../../analytics';
import { Icon, IconInviteMore } from '../../base/icons';
import { beginAddPeople } from '../../invite';
import { createToolbarEvent, sendAnalytics } from '../../../analytics';
import { Icon, IconInviteMore } from '../../../base/icons';
import { beginAddPeople } from '../../../invite';
import { ParticipantInviteButton } from './styled';

View File

@ -5,9 +5,9 @@ import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector, useDispatch } from 'react-redux';
import { getLobbyState } from '../../../lobby/functions';
import { withPixelLineHeight } from '../../base/styles/functions.web';
import { admitMultiple } from '../../lobby/actions.web';
import { getLobbyState } from '../../lobby/functions';
import { LobbyParticipantItem } from './LobbyParticipantItem';

View File

@ -2,7 +2,7 @@
import React from 'react';
import { Icon, IconRaisedHandHollow } from '../../base/icons';
import { Icon, IconRaisedHandHollow } from '../../../base/icons';
import { RaisedHandIndicatorBackground } from './styled';