diff --git a/react/features/base/color-scheme/defaultScheme.js b/react/features/base/color-scheme/defaultScheme.js index 9dff58655..dd0ab4c96 100644 --- a/react/features/base/color-scheme/defaultScheme.js +++ b/react/features/base/color-scheme/defaultScheme.js @@ -1,5 +1,3 @@ -// @flow - import { ColorPalette, getRGBAFormat } from '../styles'; /** @@ -14,21 +12,6 @@ export default { icon: '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': {}, 'Header': { background: ColorPalette.blue, diff --git a/react/features/base/react/components/native/Linkify.js b/react/features/base/react/components/native/Linkify.js index ccceb77a9..95ac3c7f7 100644 --- a/react/features/base/react/components/native/Linkify.js +++ b/react/features/base/react/components/native/Linkify.js @@ -19,7 +19,12 @@ type Props = { /** * 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 { return ( - + { this.props.children } diff --git a/react/features/chat/components/native/Chat.js b/react/features/chat/components/native/Chat.js index 68297702e..ec578bee0 100644 --- a/react/features/chat/components/native/Chat.js +++ b/react/features/chat/components/native/Chat.js @@ -1,4 +1,4 @@ -// @flow +/* eslint-disable react/no-multi-comp */ import { useIsFocused } from '@react-navigation/native'; import React, { useEffect } from 'react'; @@ -6,7 +6,8 @@ import React, { useEffect } from 'react'; import { translate } from '../../../base/i18n'; import JitsiScreen from '../../../base/modal/components/JitsiScreen'; import { connect } from '../../../base/redux'; -import { closeChat } from '../../actions.any'; +import { TabBarLabelCounter } from '../../../mobile/navigation/components/TabBarLabelCounter'; +import { closeChat } from '../../actions.native'; import AbstractChat, { type Props as AbstractProps, _mapStateToProps @@ -17,14 +18,8 @@ import MessageContainer from './MessageContainer'; import MessageRecipient from './MessageRecipient'; import styles from './styles'; - type Props = AbstractProps & { - /** - * Is this screen focused or not(React Navigation). - */ - isChatScreenFocused: boolean, - /** * Default prop for navigating between screen components(React Navigation). */ @@ -41,7 +36,6 @@ type Props = AbstractProps & { * the mobile client. */ class Chat extends AbstractChat { - /** * Implements React's {@link Component#render()}. * @@ -49,7 +43,7 @@ class Chat extends AbstractChat { */ render() { const { _messages, route } = this.props; - const privateMessageRecipient = route.params?.privateMessageRecipient; + const privateMessageRecipient = route?.params?.privateMessageRecipient; return ( { } export default translate(connect(_mapStateToProps)(props => { - const { - _nbUnreadMessages, - navigation, - t - } = props; - const isChatScreenFocused = useIsFocused(); + const { _nbUnreadMessages, dispatch, navigation, t } = props; + const unreadMessagesNr = _nbUnreadMessages > 0; - const nrUnreadMessages - = !isChatScreenFocused && _nbUnreadMessages > 0 - ? `(${_nbUnreadMessages})` : ''; + const isFocused = useIsFocused(); useEffect(() => { - navigation.setOptions({ - tabBarLabel: `${t('chat.tabs.chat')} ${nrUnreadMessages}` + navigation?.setOptions({ + tabBarLabel: () => ( + + ) }); - return () => props.dispatch(closeChat()); - }, [ nrUnreadMessages ]); + return () => isFocused && dispatch(closeChat()); + }, [ isFocused, _nbUnreadMessages ]); return ( - + ); })); diff --git a/react/features/chat/components/native/ChatButton.js b/react/features/chat/components/native/ChatButton.js index b65c4d2ad..0cca6b107 100644 --- a/react/features/chat/components/native/ChatButton.js +++ b/react/features/chat/components/native/ChatButton.js @@ -1,5 +1,3 @@ -// @flow - import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; import { translate } from '../../../base/i18n'; import { IconChatUnread, IconMessage } from '../../../base/icons'; @@ -10,9 +8,9 @@ import { } from '../../../base/toolbox/components'; import { navigate } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; import { screen } from '../../../mobile/navigation/routes'; +import { getUnreadPollCount } from '../../../polls/functions'; import { getUnreadCount } from '../../functions'; - type Props = AbstractButtonProps & { /** @@ -72,7 +70,9 @@ function _mapStateToProps(state, ownProps) { return { _isPollsDisabled: disablePolls, - _unreadMessageCount: getUnreadCount(state), + + // The toggled icon should also be available for new polls + _unreadMessageCount: getUnreadCount(state) || getUnreadPollCount(state), visible }; } diff --git a/react/features/chat/components/native/ChatMessage.js b/react/features/chat/components/native/ChatMessage.js index 942ea6e67..b8adfd1c0 100644 --- a/react/features/chat/components/native/ChatMessage.js +++ b/react/features/chat/components/native/ChatMessage.js @@ -1,30 +1,19 @@ -// @flow - import React from 'react'; import { Text, View } from 'react-native'; import { Avatar } from '../../../base/avatar'; -import { ColorSchemeRegistry } from '../../../base/color-scheme'; import { translate } from '../../../base/i18n'; import { Linkify } from '../../../base/react'; import { connect } from '../../../base/redux'; -import { type StyleType } from '../../../base/styles'; import { isGifMessage } from '../../../gifs/functions'; import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants'; import { replaceNonUnicodeEmojis } from '../../functions'; -import AbstractChatMessage, { type Props as AbstractProps } from '../AbstractChatMessage'; +import AbstractChatMessage, { type Props } from '../AbstractChatMessage'; import GifMessage from './GifMessage'; import PrivateMessageButton from './PrivateMessageButton'; import styles from './styles'; -type Props = AbstractProps & { - - /** - * The color-schemed stylesheet of the feature. - */ - _styles: StyleType -}; /** * Renders a single chat message. @@ -36,7 +25,7 @@ class ChatMessage extends AbstractChatMessage { * @inheritdoc */ render() { - const { _styles, message, knocking } = this.props; + const { message, knocking } = this.props; const localMessage = message.messageType === MESSAGE_TYPE_LOCAL; const { privateMessage, lobbyChat } = message; @@ -56,7 +45,7 @@ class ChatMessage extends AbstractChatMessage { detailsWrapperStyle.push(styles.ownMessageDetailsWrapper); // The bubble needs some additional styling - messageBubbleStyle.push(_styles.localMessageBubble); + messageBubbleStyle.push(styles.localMessageBubble); } else if (message.messageType === MESSAGE_TYPE_ERROR) { // This is a system message. @@ -66,15 +55,15 @@ class ChatMessage extends AbstractChatMessage { // This is a remote message sent by a remote participant. // The bubble needs some additional styling - messageBubbleStyle.push(_styles.remoteMessageBubble); + messageBubbleStyle.push(styles.remoteMessageBubble); } if (privateMessage) { - messageBubbleStyle.push(_styles.privateMessageBubble); + messageBubbleStyle.push(styles.privateMessageBubble); } if (lobbyChat && !knocking) { - messageBubbleStyle.push(_styles.lobbyMessageBubble); + messageBubbleStyle.push(styles.lobbyMessageBubble); } const messageText = replaceNonUnicodeEmojis(this._getMessageText()); @@ -86,11 +75,13 @@ class ChatMessage extends AbstractChatMessage { { this._renderDisplayName() } - {isGifMessage(messageText) + { isGifMessage(messageText) ? : ( - - {messageText} + + { messageText } )} { this._renderPrivateNotice() } @@ -134,14 +125,14 @@ class ChatMessage extends AbstractChatMessage { * @returns {React$Element<*> | null} */ _renderDisplayName() { - const { _styles, message, showDisplayName } = this.props; + const { message, showDisplayName } = this.props; if (!showDisplayName) { return null; } return ( - + { message.displayName } ); @@ -153,14 +144,14 @@ class ChatMessage extends AbstractChatMessage { * @returns {React$Element<*> | null} */ _renderPrivateNotice() { - const { _styles, message, knocking } = this.props; + const { message, knocking } = this.props; if (!(message.privateMessage || (message.lobbyChat && !knocking))) { return null; } return ( - + { this._getPrivateNoticeMessage() } ); @@ -172,7 +163,7 @@ class ChatMessage extends AbstractChatMessage { * @returns {React$Element<*> | null} */ _renderPrivateReplyButton() { - const { _styles, message, knocking } = this.props; + const { message, knocking } = this.props; const { messageType, privateMessage, lobbyChat } = message; if (!(privateMessage || lobbyChat) || messageType === MESSAGE_TYPE_LOCAL || knocking) { @@ -180,13 +171,13 @@ class ChatMessage extends AbstractChatMessage { } return ( - + + toggledStyles = { styles.replyStyles } /> ); } @@ -217,7 +208,6 @@ class ChatMessage extends AbstractChatMessage { */ function _mapStateToProps(state) { return { - _styles: ColorSchemeRegistry.get(state, 'Chat'), knocking: state['features/lobby'].knocking }; } diff --git a/react/features/chat/components/native/ChatMessageGroup.js b/react/features/chat/components/native/ChatMessageGroup.js index 7a93c4f8a..6af29a10d 100644 --- a/react/features/chat/components/native/ChatMessageGroup.js +++ b/react/features/chat/components/native/ChatMessageGroup.js @@ -1,6 +1,4 @@ -// @flow - -import React, { Component } from 'react'; +import React, { Component, ReactElement } from 'react'; import { FlatList } from 'react-native'; import { MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from '../../constants'; @@ -60,7 +58,7 @@ export default class ChatMessageGroup extends Component { return `key_${index}`; } - _renderMessage: Object => React$Element<*>; + _renderMessage: Object => ReactElement; /** * Renders a single chat message. diff --git a/react/features/chat/components/native/ChatPrivacyDialog.js b/react/features/chat/components/native/ChatPrivacyDialog.js index 77f43a191..25f46ee79 100644 --- a/react/features/chat/components/native/ChatPrivacyDialog.js +++ b/react/features/chat/components/native/ChatPrivacyDialog.js @@ -1,5 +1,3 @@ -// @flow - import React from 'react'; import { ConfirmDialog } from '../../../base/dialog'; diff --git a/react/features/chat/components/native/MessageContainer.js b/react/features/chat/components/native/MessageContainer.js index 1254fec75..032057fc2 100644 --- a/react/features/chat/components/native/MessageContainer.js +++ b/react/features/chat/components/native/MessageContainer.js @@ -1,12 +1,8 @@ -// @flow - -import React from 'react'; +import React, { ReactElement } from 'react'; import { FlatList, Text, View } from 'react-native'; -import { ColorSchemeRegistry } from '../../../base/color-scheme'; import { translate } from '../../../base/i18n'; import { connect } from '../../../base/redux'; -import { StyleType } from '../../../base/styles'; import AbstractMessageContainer, { type Props as AbstractProps } from '../AbstractMessageContainer'; @@ -15,11 +11,6 @@ import styles from './styles'; type Props = AbstractProps & { - /** - * The color-schemed stylesheet of the feature. - */ - _styles: StyleType, - /** * Function to be used to translate i18n labels. */ @@ -82,7 +73,7 @@ class MessageContainer extends AbstractMessageContainer { return `key_${index}`; } - _renderListEmptyComponent: () => React$Element; + _renderListEmptyComponent: () => ReactElement; /** * Renders a message when there are no messages in the chat yet. @@ -90,18 +81,18 @@ class MessageContainer extends AbstractMessageContainer { * @returns {React$Element} */ _renderListEmptyComponent() { - const { _styles, t } = this.props; + const { t } = this.props; return ( - + { t('chat.noMessagesMessage') } ); } - _renderMessageGroup: Object => React$Element; + _renderMessageGroup: Object => ReactElement; /** * Renders a single chat message. @@ -114,16 +105,4 @@ class MessageContainer extends AbstractMessageContainer { } } -/** - * 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)); +export default translate(connect()(MessageContainer)); diff --git a/react/features/chat/components/native/MessageRecipient.js b/react/features/chat/components/native/MessageRecipient.js index 774662626..4acc79e73 100644 --- a/react/features/chat/components/native/MessageRecipient.js +++ b/react/features/chat/components/native/MessageRecipient.js @@ -1,13 +1,9 @@ -// @flow - import React from 'react'; import { Text, TouchableHighlight, View } from 'react-native'; -import { ColorSchemeRegistry } from '../../../base/color-scheme'; import { translate } from '../../../base/i18n'; -import { Icon, IconCloseCircle } from '../../../base/icons'; +import { Icon, IconCloseLarge } from '../../../base/icons'; import { connect } from '../../../base/redux'; -import { type StyleType } from '../../../base/styles'; import { setParams } from '../../../mobile/navigation/components/conference/ConferenceNavigationContainerRef'; @@ -16,14 +12,11 @@ import AbstractMessageRecipient, { type Props as AbstractProps } from '../AbstractMessageRecipient'; +import styles from './styles'; + type Props = AbstractProps & { - /** - * The color-schemed stylesheet of the feature. - */ - _styles: StyleType, - /** * The Redux dispatch function. */ @@ -99,14 +92,17 @@ class MessageRecipient extends AbstractMessageRecipient { * @returns {ReactElement} */ render() { - const { _styles, privateMessageRecipient, t, - isLobbyChatActive, lobbyMessageRecipient } = this.props; - + const { + isLobbyChatActive, + lobbyMessageRecipient, + privateMessageRecipient, + t + } = this.props; if (isLobbyChatActive) { return ( - - + + { t('chat.lobbyChatMessageTo', { recipient: lobbyMessageRecipient.name }) } @@ -114,8 +110,8 @@ class MessageRecipient extends AbstractMessageRecipient { + src = { IconCloseLarge } + style = { styles.messageRecipientCancelIcon } /> ); @@ -126,8 +122,8 @@ class MessageRecipient extends AbstractMessageRecipient { } return ( - - + + { t('chat.messageTo', { recipient: privateMessageRecipient.name }) } @@ -136,8 +132,8 @@ class MessageRecipient extends AbstractMessageRecipient { onPress = { this._onResetPrivateMessageRecipient } underlayColor = { 'transparent' }> + src = { IconCloseLarge } + style = { styles.messageRecipientCancelIcon } /> ); @@ -154,7 +150,6 @@ function _mapStateToProps(state) { const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat']; return { - _styles: ColorSchemeRegistry.get(state, 'Chat'), isLobbyChatActive, lobbyMessageRecipient }; diff --git a/react/features/chat/components/native/PrivateMessageButton.js b/react/features/chat/components/native/PrivateMessageButton.js index 49166b8ab..032d3b593 100644 --- a/react/features/chat/components/native/PrivateMessageButton.js +++ b/react/features/chat/components/native/PrivateMessageButton.js @@ -1,5 +1,3 @@ -// @flow - import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; import { translate } from '../../../base/i18n'; 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 { screen } from '../../../mobile/navigation/routes'; - export type Props = AbstractButtonProps & { /** @@ -103,7 +100,7 @@ class PrivateMessageButton extends AbstractButton { * @param {Props} ownProps - The own props of the component. * @returns {Props} */ -export function _mapStateToProps(state: Object, ownProps: Props): $Shape { +export function _mapStateToProps(state: Object, ownProps: Props) { const enabled = getFeatureFlag(state, CHAT_ENABLED, true); const { disablePolls } = state['features/base/config']; const { visible = enabled, isLobbyMessage, participantID } = ownProps; diff --git a/react/features/chat/components/native/styles.js b/react/features/chat/components/native/styles.js index 4c2da43d8..ff992b64c 100644 --- a/react/features/chat/components/native/styles.js +++ b/react/features/chat/components/native/styles.js @@ -1,11 +1,19 @@ -// @flow - -import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme'; -import { BoxModel, ColorPalette } from '../../../base/styles'; +import { BoxModel } from '../../../base/styles'; import BaseTheme from '../../../base/ui/components/BaseTheme.native'; 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. * @@ -16,16 +24,72 @@ const BUBBLE_RADIUS = 8; */ 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. */ avatarWrapper: { - marginRight: 8, + marginRight: BaseTheme.spacing[2], width: 32 }, chatLink: { - color: ColorPalette.blue + color: BaseTheme.palette.link01 + }, + + chatMessage: { + ...BaseTheme.typography.bodyShortRegular, + color: BaseTheme.palette.text01 }, /** @@ -61,7 +125,7 @@ export default { }, customInput: { - width: 280 + width: 272 }, messageBubble: { @@ -117,7 +181,7 @@ export default { * Text node for the timestamp. */ timeText: { - color: 'rgb(164, 184, 209)', + color: BaseTheme.palette.text03, fontSize: 13 }, @@ -154,97 +218,35 @@ export default { width: 250, height: undefined, flexGrow: 1 - } -}; - -ColorSchemeRegistry.register('Chat', { - /** - * Background of the chat screen. - */ - backdrop: { - backgroundColor: schemeColor('background'), - flex: 1 }, - /** - * The text node for the display name. - */ - displayName: { - color: schemeColor('displayName'), - fontSize: 13 - }, - - emptyComponentText: { - color: BaseTheme.palette.text03, - textAlign: 'center' - }, - - lobbyMessageBubble: { - backgroundColor: schemeColor('lobbyMsgBackground') - }, - - lobbyMsgNotice: { - color: schemeColor('lobbyMsgNotice'), - fontSize: 11, - marginTop: 6 - }, - - lobbyMessageRecipientContainer: { - alignItems: 'center', - backgroundColor: schemeColor('lobbyMsgBackground'), - flexDirection: 'row', - padding: BoxModel.padding + senderDisplayName: { + ...BaseTheme.typography.bodyShortBold, + color: BaseTheme.palette.text02 }, localMessageBubble: { - backgroundColor: schemeColor('localMsgBackground'), + backgroundColor: BaseTheme.palette.ui04, borderTopRightRadius: 0 }, + lobbyMessageRecipientContainer: { + ...recipientContainer, + backgroundColor: BaseTheme.palette.support06 + }, + messageRecipientCancelIcon: { - color: schemeColor('icon'), + color: BaseTheme.palette.icon01, fontSize: 18 }, messageRecipientContainer: { - alignItems: 'center', - backgroundColor: schemeColor('privateMsgBackground'), - flexDirection: 'row', - padding: BoxModel.padding + ...recipientContainer }, messageRecipientText: { - color: schemeColor('text'), + ...BaseTheme.typography.bodyShortRegular, + color: BaseTheme.palette.text01, 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 - } } -}); +}; diff --git a/react/features/chat/components/web/Chat.js b/react/features/chat/components/web/Chat.js index b31ef02a4..aab8f3365 100644 --- a/react/features/chat/components/web/Chat.js +++ b/react/features/chat/components/web/Chat.js @@ -1,5 +1,3 @@ -// @flow - import clsx from 'clsx'; import React from 'react'; diff --git a/react/features/mobile/navigation/components/TabBarLabelCounter.tsx b/react/features/mobile/navigation/components/TabBarLabelCounter.tsx new file mode 100644 index 000000000..a0d7ff6c1 --- /dev/null +++ b/react/features/mobile/navigation/components/TabBarLabelCounter.tsx @@ -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 ( + }> + + { label && label } + + { + activeUnreadNr && ( + }> + }> + { nbUnread } + + + ) + } + + ); +}; diff --git a/react/features/mobile/navigation/components/chat/components/ChatAndPollsNavigator.tsx b/react/features/mobile/navigation/components/chat/components/ChatAndPollsNavigator.tsx index 9ef3d50d0..e22161dea 100644 --- a/react/features/mobile/navigation/components/chat/components/ChatAndPollsNavigator.tsx +++ b/react/features/mobile/navigation/components/chat/components/ChatAndPollsNavigator.tsx @@ -1,9 +1,11 @@ /* eslint-disable lines-around-comment */ import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'; +// @ts-ignore import React from 'react'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { IReduxState } from '../../../../../app/types'; import { getClientHeight, getClientWidth @@ -11,6 +13,8 @@ import { } from '../../../../../base/modal/components/functions'; // @ts-ignore import { Chat } from '../../../../../chat'; +import { setIsPollsTabFocused } from '../../../../../chat/actions.native'; +import { resetNbUnreadPollsMessages } from '../../../../../polls/actions'; // @ts-ignore import { PollsPane } from '../../../../../polls/components'; // @ts-ignore @@ -23,6 +27,11 @@ const ChatTab = createMaterialTopTabNavigator(); const ChatAndPolls = () => { const clientHeight = useSelector(getClientHeight); 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 ( // @ts-ignore @@ -32,12 +41,24 @@ const ChatAndPolls = () => { height: clientHeight, width: clientWidth }} + initialRouteName = { initialRouteName } screenOptions = { chatTabBarOptions }> { + dispatch(setIsPollsTabFocused(false)); + } + }} name = { screen.conference.chatandpolls.tab.chat } /> { + dispatch(setIsPollsTabFocused(true)); + dispatch(resetNbUnreadPollsMessages); + } + }} name = { screen.conference.chatandpolls.tab.polls } /> ); diff --git a/react/features/mobile/navigation/components/styles.js b/react/features/mobile/navigation/components/styles.js index e18b821b8..067fe1469 100644 --- a/react/features/mobile/navigation/components/styles.js +++ b/react/features/mobile/navigation/components/styles.js @@ -1,9 +1,14 @@ 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; +const unreadCounterDescription = { + ...BaseTheme.typography.bodyShortBoldLarge, + color: BaseTheme.palette.text03 +}; + /** * Styles of the navigation feature. */ @@ -27,5 +32,35 @@ export const navigationStyles = { connectingScreenText: { 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 } }; diff --git a/react/features/mobile/navigation/screenOptions.js b/react/features/mobile/navigation/screenOptions.js index 44e63b02c..725f28e91 100644 --- a/react/features/mobile/navigation/screenOptions.js +++ b/react/features/mobile/navigation/screenOptions.js @@ -67,19 +67,14 @@ export const conferenceScreenOptions = fullScreenOptions; * Tab bar options for chat screen. */ export const chatTabBarOptions = { - tabBarActiveTintColor: BaseTheme.palette.field02, - tabBarLabelStyle: { - fontSize: BaseTheme.typography.labelRegular.fontSize, - textTransform: 'capitalize' - }, - tabBarInactiveTintColor: BaseTheme.palette.text03, + swipeEnabled: false, tabBarIndicatorStyle: { - backgroundColor: BaseTheme.palette.field02 + backgroundColor: BaseTheme.palette.link01Active }, tabBarStyle: { backgroundColor: BaseTheme.palette.ui01, borderBottomColor: BaseTheme.palette.border05, - borderBottomWidth: 1 + borderBottomWidth: 0.4 } }; diff --git a/react/features/polls/components/native/PollsList.js b/react/features/polls/components/native/PollsList.js index 26077aa0a..609af0b0d 100644 --- a/react/features/polls/components/native/PollsList.js +++ b/react/features/polls/components/native/PollsList.js @@ -4,7 +4,7 @@ import { FlatList, View } from 'react-native'; import { Text } from 'react-native-paper'; 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'; @@ -41,7 +41,7 @@ const PollsList = () => { + src = { IconMessage } /> { t('polls.results.empty') diff --git a/react/features/polls/components/native/PollsPane.js b/react/features/polls/components/native/PollsPane.js index 34bf5d9d8..e57ec7332 100644 --- a/react/features/polls/components/native/PollsPane.js +++ b/react/features/polls/components/native/PollsPane.js @@ -1,14 +1,14 @@ /* 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 { useSelector } from 'react-redux'; import JitsiScreen from '../../../base/modal/components/JitsiScreen'; import Button from '../../../base/ui/components/native/Button'; import { BUTTON_TYPES } from '../../../base/ui/constants.native'; -import { getUnreadPollCount } from '../../functions'; +import { TabBarLabelCounter } + from '../../../mobile/navigation/components/TabBarLabelCounter'; import AbstractPollsPane from '../AbstractPollsPane'; import type { AbstractProps } from '../AbstractPollsPane'; @@ -19,19 +19,25 @@ import { chatStyles } from './styles'; const PollsPane = (props: AbstractProps) => { const { createMode, onCreate, setCreateMode, t } = props; - const isPollsScreenFocused = useIsFocused(); const navigation = useNavigation(); - const nbUnreadPolls = useSelector(getUnreadPollCount); - - const nrUnreadPolls = !isPollsScreenFocused && nbUnreadPolls > 0 - ? `(${nbUnreadPolls})` - : ''; + const { isPollsTabFocused } = useSelector(state => state['features/chat']); + const { nbUnreadPolls } = useSelector(state => state['features/polls']); useEffect(() => { + const activeUnreadPollsNr = !isPollsTabFocused && nbUnreadPolls > 0; + navigation.setOptions({ - tabBarLabel: `${t('chat.tabs.polls')} ${nrUnreadPolls}` + // eslint-disable-next-line react/no-multi-comp + tabBarLabel: () => ( + + ) }); - }, [ nrUnreadPolls ]); + + }, [ isPollsTabFocused, nbUnreadPolls ]); return (