feat(chat/polls/native) Update chat and polls UI (#12550)

* feat(chat/polls/native): update ui
pull/12585/head jitsi-meet_8085
Calinteodor 3 years ago committed by GitHub
parent 1259e54d46
commit 971fe0481f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      react/features/base/color-scheme/defaultScheme.js
  2. 11
      react/features/base/react/components/native/Linkify.js
  3. 44
      react/features/chat/components/native/Chat.js
  4. 8
      react/features/chat/components/native/ChatButton.js
  5. 46
      react/features/chat/components/native/ChatMessage.js
  6. 6
      react/features/chat/components/native/ChatMessageGroup.js
  7. 2
      react/features/chat/components/native/ChatPrivacyDialog.js
  8. 33
      react/features/chat/components/native/MessageContainer.js
  9. 39
      react/features/chat/components/native/MessageRecipient.js
  10. 5
      react/features/chat/components/native/PrivateMessageButton.js
  11. 168
      react/features/chat/components/native/styles.js
  12. 2
      react/features/chat/components/web/Chat.js
  13. 41
      react/features/mobile/navigation/components/TabBarLabelCounter.tsx
  14. 23
      react/features/mobile/navigation/components/chat/components/ChatAndPollsNavigator.tsx
  15. 37
      react/features/mobile/navigation/components/styles.js
  16. 11
      react/features/mobile/navigation/screenOptions.js
  17. 4
      react/features/polls/components/native/PollsList.js
  18. 28
      react/features/polls/components/native/PollsPane.js
  19. 40
      react/features/polls/components/native/styles.js
  20. 1
      react/features/polls/components/web/PollsPane.tsx

@ -1,5 +1,3 @@
// @flow
import { ColorPalette, getRGBAFormat } from '../styles'; import { ColorPalette, getRGBAFormat } from '../styles';
/** /**
@ -14,21 +12,6 @@ export default {
icon: 'rgb(28, 32, 37)', icon: 'rgb(28, 32, 37)',
text: 'rgb(28, 32, 37)' text: 'rgb(28, 32, 37)'
}, },
'Chat': {
displayName: 'rgb(94, 109, 121)',
localMsgBackground: 'rgb(215, 230, 249)',
lobbyMsgBackground: 'rgb(106, 80, 211)',
lobbyMsgNotice: 'rgb(16, 10, 41)',
privateMsgBackground: 'rgb(250, 219, 219)',
privateMsgNotice: 'rgb(186, 39, 58)',
remoteMsgBackground: 'rgb(241, 242, 246)',
replyBorder: 'rgb(219, 197, 200)',
replyIcon: 'rgb(94, 109, 121)'
},
'Conference': {
inviteButtonBackground: 'rgb(0, 119, 225)',
onVideoText: 'white'
},
'Dialog': {}, 'Dialog': {},
'Header': { 'Header': {
background: ColorPalette.blue, background: ColorPalette.blue,

@ -19,7 +19,12 @@ type Props = {
/** /**
* The extra styles to be applied to links. * The extra styles to be applied to links.
*/ */
linkStyle: StyleType linkStyle: StyleType,
/**
* The extra styles to be applied to text.
*/
style?: StyleType
}; };
/** /**
@ -46,7 +51,9 @@ export default class Linkify extends Component<Props> {
return ( return (
<ReactLinkify <ReactLinkify
componentDecorator = { this._componentDecorator }> componentDecorator = { this._componentDecorator }>
<Text selectable = { true }> <Text
selectable = { true }
style = { this.props.style }>
{ this.props.children } { this.props.children }
</Text> </Text>
</ReactLinkify> </ReactLinkify>

@ -1,4 +1,4 @@
// @flow /* eslint-disable react/no-multi-comp */
import { useIsFocused } from '@react-navigation/native'; import { useIsFocused } from '@react-navigation/native';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
@ -6,7 +6,8 @@ import React, { useEffect } from 'react';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import JitsiScreen from '../../../base/modal/components/JitsiScreen'; import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { closeChat } from '../../actions.any'; import { TabBarLabelCounter } from '../../../mobile/navigation/components/TabBarLabelCounter';
import { closeChat } from '../../actions.native';
import AbstractChat, { import AbstractChat, {
type Props as AbstractProps, type Props as AbstractProps,
_mapStateToProps _mapStateToProps
@ -17,14 +18,8 @@ import MessageContainer from './MessageContainer';
import MessageRecipient from './MessageRecipient'; import MessageRecipient from './MessageRecipient';
import styles from './styles'; import styles from './styles';
type Props = AbstractProps & { type Props = AbstractProps & {
/**
* Is this screen focused or not(React Navigation).
*/
isChatScreenFocused: boolean,
/** /**
* Default prop for navigating between screen components(React Navigation). * Default prop for navigating between screen components(React Navigation).
*/ */
@ -41,7 +36,6 @@ type Props = AbstractProps & {
* the mobile client. * the mobile client.
*/ */
class Chat extends AbstractChat<Props> { class Chat extends AbstractChat<Props> {
/** /**
* Implements React's {@link Component#render()}. * Implements React's {@link Component#render()}.
* *
@ -49,7 +43,7 @@ class Chat extends AbstractChat<Props> {
*/ */
render() { render() {
const { _messages, route } = this.props; const { _messages, route } = this.props;
const privateMessageRecipient = route.params?.privateMessageRecipient; const privateMessageRecipient = route?.params?.privateMessageRecipient;
return ( return (
<JitsiScreen <JitsiScreen
@ -68,28 +62,26 @@ class Chat extends AbstractChat<Props> {
} }
export default translate(connect(_mapStateToProps)(props => { export default translate(connect(_mapStateToProps)(props => {
const { const { _nbUnreadMessages, dispatch, navigation, t } = props;
_nbUnreadMessages, const unreadMessagesNr = _nbUnreadMessages > 0;
navigation,
t
} = props;
const isChatScreenFocused = useIsFocused();
const nrUnreadMessages const isFocused = useIsFocused();
= !isChatScreenFocused && _nbUnreadMessages > 0
? `(${_nbUnreadMessages})` : '';
useEffect(() => { useEffect(() => {
navigation.setOptions({ navigation?.setOptions({
tabBarLabel: `${t('chat.tabs.chat')} ${nrUnreadMessages}` tabBarLabel: () => (
<TabBarLabelCounter
activeUnreadNr = { unreadMessagesNr }
isFocused = { isFocused }
label = { t('chat.tabs.chat') }
nbUnread = { _nbUnreadMessages } />
)
}); });
return () => props.dispatch(closeChat()); return () => isFocused && dispatch(closeChat());
}, [ nrUnreadMessages ]); }, [ isFocused, _nbUnreadMessages ]);
return ( return (
<Chat <Chat { ...props } />
{ ...props }
isChatScreenFocused = { isChatScreenFocused } />
); );
})); }));

@ -1,5 +1,3 @@
// @flow
import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { IconChatUnread, IconMessage } from '../../../base/icons'; import { IconChatUnread, IconMessage } from '../../../base/icons';
@ -10,9 +8,9 @@ import {
} from '../../../base/toolbox/components'; } from '../../../base/toolbox/components';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes'; import { screen } from '../../../mobile/navigation/routes';
import { getUnreadPollCount } from '../../../polls/functions';
import { getUnreadCount } from '../../functions'; import { getUnreadCount } from '../../functions';
type Props = AbstractButtonProps & { type Props = AbstractButtonProps & {
/** /**
@ -72,7 +70,9 @@ function _mapStateToProps(state, ownProps) {
return { return {
_isPollsDisabled: disablePolls, _isPollsDisabled: disablePolls,
_unreadMessageCount: getUnreadCount(state),
// The toggled icon should also be available for new polls
_unreadMessageCount: getUnreadCount(state) || getUnreadPollCount(state),
visible visible
}; };
} }

@ -1,30 +1,19 @@
// @flow
import React from 'react'; import React from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { Avatar } from '../../../base/avatar'; import { Avatar } from '../../../base/avatar';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { Linkify } from '../../../base/react'; import { Linkify } from '../../../base/react';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { type StyleType } from '../../../base/styles';
import { isGifMessage } from '../../../gifs/functions'; import { isGifMessage } from '../../../gifs/functions';
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants'; import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
import { replaceNonUnicodeEmojis } from '../../functions'; import { replaceNonUnicodeEmojis } from '../../functions';
import AbstractChatMessage, { type Props as AbstractProps } from '../AbstractChatMessage'; import AbstractChatMessage, { type Props } from '../AbstractChatMessage';
import GifMessage from './GifMessage'; import GifMessage from './GifMessage';
import PrivateMessageButton from './PrivateMessageButton'; import PrivateMessageButton from './PrivateMessageButton';
import styles from './styles'; import styles from './styles';
type Props = AbstractProps & {
/**
* The color-schemed stylesheet of the feature.
*/
_styles: StyleType
};
/** /**
* Renders a single chat message. * Renders a single chat message.
@ -36,7 +25,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const { _styles, message, knocking } = this.props; const { message, knocking } = this.props;
const localMessage = message.messageType === MESSAGE_TYPE_LOCAL; const localMessage = message.messageType === MESSAGE_TYPE_LOCAL;
const { privateMessage, lobbyChat } = message; const { privateMessage, lobbyChat } = message;
@ -56,7 +45,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper); detailsWrapperStyle.push(styles.ownMessageDetailsWrapper);
// The bubble needs some additional styling // The bubble needs some additional styling
messageBubbleStyle.push(_styles.localMessageBubble); messageBubbleStyle.push(styles.localMessageBubble);
} else if (message.messageType === MESSAGE_TYPE_ERROR) { } else if (message.messageType === MESSAGE_TYPE_ERROR) {
// This is a system message. // This is a system message.
@ -66,15 +55,15 @@ class ChatMessage extends AbstractChatMessage<Props> {
// This is a remote message sent by a remote participant. // This is a remote message sent by a remote participant.
// The bubble needs some additional styling // The bubble needs some additional styling
messageBubbleStyle.push(_styles.remoteMessageBubble); messageBubbleStyle.push(styles.remoteMessageBubble);
} }
if (privateMessage) { if (privateMessage) {
messageBubbleStyle.push(_styles.privateMessageBubble); messageBubbleStyle.push(styles.privateMessageBubble);
} }
if (lobbyChat && !knocking) { if (lobbyChat && !knocking) {
messageBubbleStyle.push(_styles.lobbyMessageBubble); messageBubbleStyle.push(styles.lobbyMessageBubble);
} }
const messageText = replaceNonUnicodeEmojis(this._getMessageText()); const messageText = replaceNonUnicodeEmojis(this._getMessageText());
@ -86,11 +75,13 @@ class ChatMessage extends AbstractChatMessage<Props> {
<View style = { messageBubbleStyle }> <View style = { messageBubbleStyle }>
<View style = { styles.textWrapper } > <View style = { styles.textWrapper } >
{ this._renderDisplayName() } { this._renderDisplayName() }
{isGifMessage(messageText) { isGifMessage(messageText)
? <GifMessage message = { messageText } /> ? <GifMessage message = { messageText } />
: ( : (
<Linkify linkStyle = { styles.chatLink }> <Linkify
{messageText} linkStyle = { styles.chatLink }
style = { styles.chatMessage }>
{ messageText }
</Linkify> </Linkify>
)} )}
{ this._renderPrivateNotice() } { this._renderPrivateNotice() }
@ -134,14 +125,14 @@ class ChatMessage extends AbstractChatMessage<Props> {
* @returns {React$Element<*> | null} * @returns {React$Element<*> | null}
*/ */
_renderDisplayName() { _renderDisplayName() {
const { _styles, message, showDisplayName } = this.props; const { message, showDisplayName } = this.props;
if (!showDisplayName) { if (!showDisplayName) {
return null; return null;
} }
return ( return (
<Text style = { _styles.displayName }> <Text style = { styles.senderDisplayName }>
{ message.displayName } { message.displayName }
</Text> </Text>
); );
@ -153,14 +144,14 @@ class ChatMessage extends AbstractChatMessage<Props> {
* @returns {React$Element<*> | null} * @returns {React$Element<*> | null}
*/ */
_renderPrivateNotice() { _renderPrivateNotice() {
const { _styles, message, knocking } = this.props; const { message, knocking } = this.props;
if (!(message.privateMessage || (message.lobbyChat && !knocking))) { if (!(message.privateMessage || (message.lobbyChat && !knocking))) {
return null; return null;
} }
return ( return (
<Text style = { message.lobbyChat ? _styles.lobbyMsgNotice : _styles.privateNotice }> <Text style = { message.lobbyChat ? styles.lobbyMsgNotice : styles.privateNotice }>
{ this._getPrivateNoticeMessage() } { this._getPrivateNoticeMessage() }
</Text> </Text>
); );
@ -172,7 +163,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
* @returns {React$Element<*> | null} * @returns {React$Element<*> | null}
*/ */
_renderPrivateReplyButton() { _renderPrivateReplyButton() {
const { _styles, message, knocking } = this.props; const { message, knocking } = this.props;
const { messageType, privateMessage, lobbyChat } = message; const { messageType, privateMessage, lobbyChat } = message;
if (!(privateMessage || lobbyChat) || messageType === MESSAGE_TYPE_LOCAL || knocking) { if (!(privateMessage || lobbyChat) || messageType === MESSAGE_TYPE_LOCAL || knocking) {
@ -180,13 +171,13 @@ class ChatMessage extends AbstractChatMessage<Props> {
} }
return ( return (
<View style = { _styles.replyContainer }> <View style = { styles.replyContainer }>
<PrivateMessageButton <PrivateMessageButton
isLobbyMessage = { lobbyChat } isLobbyMessage = { lobbyChat }
participantID = { message.id } participantID = { message.id }
reply = { true } reply = { true }
showLabel = { false } showLabel = { false }
toggledStyles = { _styles.replyStyles } /> toggledStyles = { styles.replyStyles } />
</View> </View>
); );
} }
@ -217,7 +208,6 @@ class ChatMessage extends AbstractChatMessage<Props> {
*/ */
function _mapStateToProps(state) { function _mapStateToProps(state) {
return { return {
_styles: ColorSchemeRegistry.get(state, 'Chat'),
knocking: state['features/lobby'].knocking knocking: state['features/lobby'].knocking
}; };
} }

@ -1,6 +1,4 @@
// @flow import React, { Component, ReactElement } from 'react';
import React, { Component } from 'react';
import { FlatList } from 'react-native'; import { FlatList } from 'react-native';
import { MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from '../../constants'; import { MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from '../../constants';
@ -60,7 +58,7 @@ export default class ChatMessageGroup extends Component<Props> {
return `key_${index}`; return `key_${index}`;
} }
_renderMessage: Object => React$Element<*>; _renderMessage: Object => ReactElement;
/** /**
* Renders a single chat message. * Renders a single chat message.

@ -1,5 +1,3 @@
// @flow
import React from 'react'; import React from 'react';
import { ConfirmDialog } from '../../../base/dialog'; import { ConfirmDialog } from '../../../base/dialog';

@ -1,12 +1,8 @@
// @flow import React, { ReactElement } from 'react';
import React from 'react';
import { FlatList, Text, View } from 'react-native'; import { FlatList, Text, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import AbstractMessageContainer, { type Props as AbstractProps } import AbstractMessageContainer, { type Props as AbstractProps }
from '../AbstractMessageContainer'; from '../AbstractMessageContainer';
@ -15,11 +11,6 @@ import styles from './styles';
type Props = AbstractProps & { type Props = AbstractProps & {
/**
* The color-schemed stylesheet of the feature.
*/
_styles: StyleType,
/** /**
* Function to be used to translate i18n labels. * Function to be used to translate i18n labels.
*/ */
@ -82,7 +73,7 @@ class MessageContainer extends AbstractMessageContainer<Props> {
return `key_${index}`; return `key_${index}`;
} }
_renderListEmptyComponent: () => React$Element<any>; _renderListEmptyComponent: () => ReactElement;
/** /**
* Renders a message when there are no messages in the chat yet. * Renders a message when there are no messages in the chat yet.
@ -90,18 +81,18 @@ class MessageContainer extends AbstractMessageContainer<Props> {
* @returns {React$Element<any>} * @returns {React$Element<any>}
*/ */
_renderListEmptyComponent() { _renderListEmptyComponent() {
const { _styles, t } = this.props; const { t } = this.props;
return ( return (
<View style = { styles.emptyComponentWrapper }> <View style = { styles.emptyComponentWrapper }>
<Text style = { _styles.emptyComponentText }> <Text style = { styles.emptyComponentText }>
{ t('chat.noMessagesMessage') } { t('chat.noMessagesMessage') }
</Text> </Text>
</View> </View>
); );
} }
_renderMessageGroup: Object => React$Element<any>; _renderMessageGroup: Object => ReactElement;
/** /**
* Renders a single chat message. * Renders a single chat message.
@ -114,16 +105,4 @@ class MessageContainer extends AbstractMessageContainer<Props> {
} }
} }
/** export default translate(connect()(MessageContainer));
* Maps part of the redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
*/
function _mapStateToProps(state) {
return {
_styles: ColorSchemeRegistry.get(state, 'Chat')
};
}
export default translate(connect(_mapStateToProps)(MessageContainer));

@ -1,13 +1,9 @@
// @flow
import React from 'react'; import React from 'react';
import { Text, TouchableHighlight, View } from 'react-native'; import { Text, TouchableHighlight, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { Icon, IconCloseCircle } from '../../../base/icons'; import { Icon, IconCloseLarge } from '../../../base/icons';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { type StyleType } from '../../../base/styles';
import { import {
setParams setParams
} from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
@ -16,13 +12,10 @@ import AbstractMessageRecipient, {
type Props as AbstractProps type Props as AbstractProps
} from '../AbstractMessageRecipient'; } from '../AbstractMessageRecipient';
import styles from './styles';
type Props = AbstractProps & {
/** type Props = AbstractProps & {
* The color-schemed stylesheet of the feature.
*/
_styles: StyleType,
/** /**
* The Redux dispatch function. * The Redux dispatch function.
@ -99,14 +92,17 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
* @returns {ReactElement} * @returns {ReactElement}
*/ */
render() { render() {
const { _styles, privateMessageRecipient, t, const {
isLobbyChatActive, lobbyMessageRecipient } = this.props; isLobbyChatActive,
lobbyMessageRecipient,
privateMessageRecipient,
t
} = this.props;
if (isLobbyChatActive) { if (isLobbyChatActive) {
return ( return (
<View style = { _styles.lobbyMessageRecipientContainer }> <View style = { styles.lobbyMessageRecipientContainer }>
<Text style = { _styles.messageRecipientText }> <Text style = { styles.messageRecipientText }>
{ t('chat.lobbyChatMessageTo', { { t('chat.lobbyChatMessageTo', {
recipient: lobbyMessageRecipient.name recipient: lobbyMessageRecipient.name
}) } }) }
@ -114,8 +110,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
<TouchableHighlight <TouchableHighlight
onPress = { this._onResetLobbyMessageRecipient }> onPress = { this._onResetLobbyMessageRecipient }>
<Icon <Icon
src = { IconCloseCircle } src = { IconCloseLarge }
style = { _styles.messageRecipientCancelIcon } /> style = { styles.messageRecipientCancelIcon } />
</TouchableHighlight> </TouchableHighlight>
</View> </View>
); );
@ -126,8 +122,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
} }
return ( return (
<View style = { _styles.messageRecipientContainer }> <View style = { styles.messageRecipientContainer }>
<Text style = { _styles.messageRecipientText }> <Text style = { styles.messageRecipientText }>
{ t('chat.messageTo', { { t('chat.messageTo', {
recipient: privateMessageRecipient.name recipient: privateMessageRecipient.name
}) } }) }
@ -136,8 +132,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
onPress = { this._onResetPrivateMessageRecipient } onPress = { this._onResetPrivateMessageRecipient }
underlayColor = { 'transparent' }> underlayColor = { 'transparent' }>
<Icon <Icon
src = { IconCloseCircle } src = { IconCloseLarge }
style = { _styles.messageRecipientCancelIcon } /> style = { styles.messageRecipientCancelIcon } />
</TouchableHighlight> </TouchableHighlight>
</View> </View>
); );
@ -154,7 +150,6 @@ function _mapStateToProps(state) {
const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat']; const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat'];
return { return {
_styles: ColorSchemeRegistry.get(state, 'Chat'),
isLobbyChatActive, isLobbyChatActive,
lobbyMessageRecipient lobbyMessageRecipient
}; };

@ -1,5 +1,3 @@
// @flow
import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { IconMessage, IconReply } from '../../../base/icons'; import { IconMessage, IconReply } from '../../../base/icons';
@ -10,7 +8,6 @@ import { handleLobbyChatInitialized, openChat } from '../../../chat/actions';
import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
import { screen } from '../../../mobile/navigation/routes'; import { screen } from '../../../mobile/navigation/routes';
export type Props = AbstractButtonProps & { export type Props = AbstractButtonProps & {
/** /**
@ -103,7 +100,7 @@ class PrivateMessageButton extends AbstractButton<Props, any> {
* @param {Props} ownProps - The own props of the component. * @param {Props} ownProps - The own props of the component.
* @returns {Props} * @returns {Props}
*/ */
export function _mapStateToProps(state: Object, ownProps: Props): $Shape<Props> { export function _mapStateToProps(state: Object, ownProps: Props) {
const enabled = getFeatureFlag(state, CHAT_ENABLED, true); const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
const { disablePolls } = state['features/base/config']; const { disablePolls } = state['features/base/config'];
const { visible = enabled, isLobbyMessage, participantID } = ownProps; const { visible = enabled, isLobbyMessage, participantID } = ownProps;

@ -1,11 +1,19 @@
// @flow import { BoxModel } from '../../../base/styles';
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
import { BoxModel, ColorPalette } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native'; import BaseTheme from '../../../base/ui/components/BaseTheme.native';
const BUBBLE_RADIUS = 8; const BUBBLE_RADIUS = 8;
const recipientContainer = {
alignItems: 'center',
backgroundColor: BaseTheme.palette.support05,
borderRadius: BaseTheme.shape.borderRadius,
flexDirection: 'row',
height: 48,
marginBottom: BaseTheme.spacing[3],
marginHorizontal: BaseTheme.spacing[3],
padding: BaseTheme.spacing[2]
};
/** /**
* The styles of the feature chat. * The styles of the feature chat.
* *
@ -16,16 +24,72 @@ const BUBBLE_RADIUS = 8;
*/ */
export default { export default {
/**
* Background of the chat screen.
*/
backdrop: {
backgroundColor: BaseTheme.palette.ui10,
flex: 1
},
emptyComponentText: {
color: BaseTheme.palette.text03,
textAlign: 'center'
},
lobbyMessageBubble: {
backgroundColor: BaseTheme.palette.support06
},
lobbyMsgNotice: {
color: BaseTheme.palette.text04,
fontSize: 11,
marginTop: 6
},
privateNotice: {
...BaseTheme.palette.bodyShortRegular,
color: BaseTheme.palette.text02
},
privateMessageBubble: {
backgroundColor: BaseTheme.palette.support05
},
remoteMessageBubble: {
backgroundColor: BaseTheme.palette.ui02,
borderTopLeftRadius: 0
},
replyContainer: {
alignSelf: 'stretch',
justifyContent: 'center'
},
replyStyles: {
iconStyle: {
color: BaseTheme.palette.icon01,
fontSize: 22,
padding: BaseTheme.spacing[2]
},
underlayColor: 'transparent'
},
/** /**
* Wrapper View for the avatar. * Wrapper View for the avatar.
*/ */
avatarWrapper: { avatarWrapper: {
marginRight: 8, marginRight: BaseTheme.spacing[2],
width: 32 width: 32
}, },
chatLink: { chatLink: {
color: ColorPalette.blue color: BaseTheme.palette.link01
},
chatMessage: {
...BaseTheme.typography.bodyShortRegular,
color: BaseTheme.palette.text01
}, },
/** /**
@ -61,7 +125,7 @@ export default {
}, },
customInput: { customInput: {
width: 280 width: 272
}, },
messageBubble: { messageBubble: {
@ -117,7 +181,7 @@ export default {
* Text node for the timestamp. * Text node for the timestamp.
*/ */
timeText: { timeText: {
color: 'rgb(164, 184, 209)', color: BaseTheme.palette.text03,
fontSize: 13 fontSize: 13
}, },
@ -154,97 +218,35 @@ export default {
width: 250, width: 250,
height: undefined, height: undefined,
flexGrow: 1 flexGrow: 1
}
};
ColorSchemeRegistry.register('Chat', {
/**
* Background of the chat screen.
*/
backdrop: {
backgroundColor: schemeColor('background'),
flex: 1
}, },
/** senderDisplayName: {
* The text node for the display name. ...BaseTheme.typography.bodyShortBold,
*/ color: BaseTheme.palette.text02
displayName: {
color: schemeColor('displayName'),
fontSize: 13
}, },
emptyComponentText: { localMessageBubble: {
color: BaseTheme.palette.text03, backgroundColor: BaseTheme.palette.ui04,
textAlign: 'center' borderTopRightRadius: 0
},
lobbyMessageBubble: {
backgroundColor: schemeColor('lobbyMsgBackground')
},
lobbyMsgNotice: {
color: schemeColor('lobbyMsgNotice'),
fontSize: 11,
marginTop: 6
}, },
lobbyMessageRecipientContainer: { lobbyMessageRecipientContainer: {
alignItems: 'center', ...recipientContainer,
backgroundColor: schemeColor('lobbyMsgBackground'), backgroundColor: BaseTheme.palette.support06
flexDirection: 'row',
padding: BoxModel.padding
},
localMessageBubble: {
backgroundColor: schemeColor('localMsgBackground'),
borderTopRightRadius: 0
}, },
messageRecipientCancelIcon: { messageRecipientCancelIcon: {
color: schemeColor('icon'), color: BaseTheme.palette.icon01,
fontSize: 18 fontSize: 18
}, },
messageRecipientContainer: { messageRecipientContainer: {
alignItems: 'center', ...recipientContainer
backgroundColor: schemeColor('privateMsgBackground'),
flexDirection: 'row',
padding: BoxModel.padding
}, },
messageRecipientText: { messageRecipientText: {
color: schemeColor('text'), ...BaseTheme.typography.bodyShortRegular,
color: BaseTheme.palette.text01,
flex: 1 flex: 1
},
privateNotice: {
color: schemeColor('privateMsgNotice'),
fontSize: 11,
marginTop: 6
},
privateMessageBubble: {
backgroundColor: schemeColor('privateMsgBackground')
},
remoteMessageBubble: {
backgroundColor: schemeColor('remoteMsgBackground'),
borderTopLeftRadius: 0
},
replyContainer: {
alignSelf: 'stretch',
borderLeftColor: schemeColor('replyBorder'),
borderLeftWidth: 1,
justifyContent: 'center'
},
replyStyles: {
iconStyle: {
color: schemeColor('replyIcon'),
fontSize: 22,
padding: 8
}
} }
}); };

@ -1,5 +1,3 @@
// @flow
import clsx from 'clsx'; import clsx from 'clsx';
import React from 'react'; import React from 'react';

@ -0,0 +1,41 @@
// @ts-ignore
import React from 'react';
import { StyleProp, Text, TextStyle, View } from 'react-native';
// @ts-ignore
import { navigationStyles } from './styles';
interface ITabBarLabelCounterProps {
activeUnreadNr: boolean;
isFocused: boolean;
label: string;
nbUnread?: number;
}
export const TabBarLabelCounter = ({ activeUnreadNr, isFocused, label, nbUnread }: ITabBarLabelCounterProps) => {
const labelStyles = isFocused
? navigationStyles.unreadCounterDescriptionFocused
: navigationStyles.unreadCounterDescription;
return (
<View
style = {
navigationStyles.unreadCounterContainer as StyleProp<TextStyle> }>
<Text
style = { labelStyles }>
{ label && label }
</Text>
{
activeUnreadNr && (
<View
style = { navigationStyles.unreadCounterCircle as StyleProp<TextStyle> }>
<Text
style = { navigationStyles.unreadCounter as StyleProp<TextStyle> }>
{ nbUnread }
</Text>
</View>
)
}
</View>
);
};

@ -1,9 +1,11 @@
/* eslint-disable lines-around-comment */ /* eslint-disable lines-around-comment */
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
// @ts-ignore
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { IReduxState } from '../../../../../app/types';
import { import {
getClientHeight, getClientHeight,
getClientWidth getClientWidth
@ -11,6 +13,8 @@ import {
} from '../../../../../base/modal/components/functions'; } from '../../../../../base/modal/components/functions';
// @ts-ignore // @ts-ignore
import { Chat } from '../../../../../chat'; import { Chat } from '../../../../../chat';
import { setIsPollsTabFocused } from '../../../../../chat/actions.native';
import { resetNbUnreadPollsMessages } from '../../../../../polls/actions';
// @ts-ignore // @ts-ignore
import { PollsPane } from '../../../../../polls/components'; import { PollsPane } from '../../../../../polls/components';
// @ts-ignore // @ts-ignore
@ -23,6 +27,11 @@ const ChatTab = createMaterialTopTabNavigator();
const ChatAndPolls = () => { const ChatAndPolls = () => {
const clientHeight = useSelector(getClientHeight); const clientHeight = useSelector(getClientHeight);
const clientWidth = useSelector(getClientWidth); const clientWidth = useSelector(getClientWidth);
const dispatch = useDispatch();
const { isPollsTabFocused } = useSelector((state: IReduxState) => state['features/chat']);
const initialRouteName = isPollsTabFocused
? screen.conference.chatandpolls.tab.polls
: screen.conference.chatandpolls.tab.chat;
return ( return (
// @ts-ignore // @ts-ignore
@ -32,12 +41,24 @@ const ChatAndPolls = () => {
height: clientHeight, height: clientHeight,
width: clientWidth width: clientWidth
}} }}
initialRouteName = { initialRouteName }
screenOptions = { chatTabBarOptions }> screenOptions = { chatTabBarOptions }>
<ChatTab.Screen <ChatTab.Screen
component = { Chat } component = { Chat }
listeners = {{
tabPress: () => {
dispatch(setIsPollsTabFocused(false));
}
}}
name = { screen.conference.chatandpolls.tab.chat } /> name = { screen.conference.chatandpolls.tab.chat } />
<ChatTab.Screen <ChatTab.Screen
component = { PollsPane } component = { PollsPane }
listeners = {{
tabPress: () => {
dispatch(setIsPollsTabFocused(true));
dispatch(resetNbUnreadPollsMessages);
}
}}
name = { screen.conference.chatandpolls.tab.polls } /> name = { screen.conference.chatandpolls.tab.polls } />
</ChatTab.Navigator> </ChatTab.Navigator>
); );

@ -1,9 +1,14 @@
import { BoxModel } from '../../../base/styles'; import { BoxModel } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme'; import BaseTheme from '../../../base/ui/components/BaseTheme.native';
export const TEXT_COLOR = BaseTheme.palette.text01; export const TEXT_COLOR = BaseTheme.palette.text01;
const unreadCounterDescription = {
...BaseTheme.typography.bodyShortBoldLarge,
color: BaseTheme.palette.text03
};
/** /**
* Styles of the navigation feature. * Styles of the navigation feature.
*/ */
@ -27,5 +32,35 @@ export const navigationStyles = {
connectingScreenText: { connectingScreenText: {
color: TEXT_COLOR color: TEXT_COLOR
},
unreadCounterContainer: {
alignItems: 'center',
display: 'flex',
flexDirection: 'row'
},
unreadCounterDescription: {
...unreadCounterDescription
},
unreadCounterDescriptionFocused: {
...unreadCounterDescription,
color: BaseTheme.palette.text01
},
unreadCounterCircle: {
backgroundColor: BaseTheme.palette.warning01,
borderRadius: BaseTheme.spacing[4] / 2,
height: BaseTheme.spacing[4],
justifyContent: 'center',
marginLeft: BaseTheme.spacing[2],
width: BaseTheme.spacing[4]
},
unreadCounter: {
...BaseTheme.typography.bodyShortBold,
alignSelf: 'center',
color: BaseTheme.palette.text04
} }
}; };

@ -67,19 +67,14 @@ export const conferenceScreenOptions = fullScreenOptions;
* Tab bar options for chat screen. * Tab bar options for chat screen.
*/ */
export const chatTabBarOptions = { export const chatTabBarOptions = {
tabBarActiveTintColor: BaseTheme.palette.field02, swipeEnabled: false,
tabBarLabelStyle: {
fontSize: BaseTheme.typography.labelRegular.fontSize,
textTransform: 'capitalize'
},
tabBarInactiveTintColor: BaseTheme.palette.text03,
tabBarIndicatorStyle: { tabBarIndicatorStyle: {
backgroundColor: BaseTheme.palette.field02 backgroundColor: BaseTheme.palette.link01Active
}, },
tabBarStyle: { tabBarStyle: {
backgroundColor: BaseTheme.palette.ui01, backgroundColor: BaseTheme.palette.ui01,
borderBottomColor: BaseTheme.palette.border05, borderBottomColor: BaseTheme.palette.border05,
borderBottomWidth: 1 borderBottomWidth: 0.4
} }
}; };

@ -4,7 +4,7 @@ import { FlatList, View } from 'react-native';
import { Text } from 'react-native-paper'; import { Text } from 'react-native-paper';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { Icon, IconChatUnread } from '../../../base/icons'; import { Icon, IconMessage } from '../../../base/icons';
import BaseTheme from '../../../base/ui/components/BaseTheme.native'; import BaseTheme from '../../../base/ui/components/BaseTheme.native';
@ -41,7 +41,7 @@ const PollsList = () => {
<Icon <Icon
color = { BaseTheme.palette.icon03 } color = { BaseTheme.palette.icon03 }
size = { 160 } size = { 160 }
src = { IconChatUnread } /> src = { IconMessage } />
<Text style = { chatStyles.noPollText } > <Text style = { chatStyles.noPollText } >
{ {
t('polls.results.empty') t('polls.results.empty')

@ -1,14 +1,14 @@
/* eslint-disable react-native/no-color-literals */ /* eslint-disable react-native/no-color-literals */
// @flow
import { useIsFocused, useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import JitsiScreen from '../../../base/modal/components/JitsiScreen'; import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import Button from '../../../base/ui/components/native/Button'; import Button from '../../../base/ui/components/native/Button';
import { BUTTON_TYPES } from '../../../base/ui/constants.native'; import { BUTTON_TYPES } from '../../../base/ui/constants.native';
import { getUnreadPollCount } from '../../functions'; import { TabBarLabelCounter }
from '../../../mobile/navigation/components/TabBarLabelCounter';
import AbstractPollsPane from '../AbstractPollsPane'; import AbstractPollsPane from '../AbstractPollsPane';
import type { AbstractProps } from '../AbstractPollsPane'; import type { AbstractProps } from '../AbstractPollsPane';
@ -19,19 +19,25 @@ import { chatStyles } from './styles';
const PollsPane = (props: AbstractProps) => { const PollsPane = (props: AbstractProps) => {
const { createMode, onCreate, setCreateMode, t } = props; const { createMode, onCreate, setCreateMode, t } = props;
const isPollsScreenFocused = useIsFocused();
const navigation = useNavigation(); const navigation = useNavigation();
const nbUnreadPolls = useSelector(getUnreadPollCount); const { isPollsTabFocused } = useSelector(state => state['features/chat']);
const { nbUnreadPolls } = useSelector(state => state['features/polls']);
const nrUnreadPolls = !isPollsScreenFocused && nbUnreadPolls > 0
? `(${nbUnreadPolls})`
: '';
useEffect(() => { useEffect(() => {
const activeUnreadPollsNr = !isPollsTabFocused && nbUnreadPolls > 0;
navigation.setOptions({ navigation.setOptions({
tabBarLabel: `${t('chat.tabs.polls')} ${nrUnreadPolls}` // eslint-disable-next-line react/no-multi-comp
tabBarLabel: () => (
<TabBarLabelCounter
activeUnreadNr = { activeUnreadPollsNr }
isFocused = { isPollsTabFocused }
label = { t('chat.tabs.polls') }
nbUnread = { nbUnreadPolls } />
)
}); });
}, [ nrUnreadPolls ]);
}, [ isPollsTabFocused, nbUnreadPolls ]);
return ( return (
<JitsiScreen <JitsiScreen

@ -110,10 +110,12 @@ export const chatStyles = createStyleSheet({
noPollContent: { noPollContent: {
alignItems: 'center', alignItems: 'center',
flex: 1,
flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
paddingTop: '4%' position: 'absolute',
bottom: 0,
left: 0,
right: 0,
top: '25%'
}, },
noPollText: { noPollText: {
@ -149,7 +151,7 @@ export const chatStyles = createStyleSheet({
pollCreateButton: { pollCreateButton: {
flex: 1, flex: 1,
marginHorizontal: BaseTheme.spacing[2] marginHorizontal: BaseTheme.spacing[1]
}, },
pollSendLabel: { pollSendLabel: {
@ -191,7 +193,8 @@ export const chatStyles = createStyleSheet({
}, },
pollCreateAddButton: { pollCreateAddButton: {
margin: BaseTheme.spacing[2] marginHorizontal: BaseTheme.spacing[1],
marginVertical: BaseTheme.spacing[2]
}, },
toggleText: { toggleText: {
@ -200,8 +203,8 @@ export const chatStyles = createStyleSheet({
}, },
createPollButton: { createPollButton: {
marginHorizontal: BaseTheme.spacing[4], marginHorizontal: BaseTheme.spacing[3],
marginVertical: '8%' marginVertical: 34
}, },
pollPane: { pollPane: {
@ -218,5 +221,28 @@ export const chatStyles = createStyleSheet({
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
marginHorizontal: BaseTheme.spacing[2] marginHorizontal: BaseTheme.spacing[2]
},
unreadPollsCounterContainer: {
display: 'flex',
flexDirection: 'row'
},
unreadPollsCounterDescription: {
color: BaseTheme.palette.text01
},
unreadPollsCounterCircle: {
backgroundColor: BaseTheme.palette.warning01,
borderRadius: BaseTheme.spacing[3] / 2,
height: BaseTheme.spacing[3],
justifyContent: 'center',
marginLeft: BaseTheme.spacing[2],
width: BaseTheme.spacing[3]
},
unreadPollsCounter: {
alignSelf: 'center',
color: BaseTheme.palette.text04
} }
}); });

@ -1,4 +1,5 @@
/* eslint-disable lines-around-comment */ /* eslint-disable lines-around-comment */
// @ts-ignore
import React from 'react'; import React from 'react';
import Button from '../../../base/ui/components/web/Button'; import Button from '../../../base/ui/components/web/Button';

Loading…
Cancel
Save