feat(chat/polls/native) Update chat and polls UI (#12550)
* feat(chat/polls/native): update ui
This commit is contained in:
parent
1259e54d46
commit
971fe0481f
|
@ -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,
|
||||
|
|
|
@ -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<Props> {
|
|||
return (
|
||||
<ReactLinkify
|
||||
componentDecorator = { this._componentDecorator }>
|
||||
<Text selectable = { true }>
|
||||
<Text
|
||||
selectable = { true }
|
||||
style = { this.props.style }>
|
||||
{ this.props.children }
|
||||
</Text>
|
||||
</ReactLinkify>
|
||||
|
|
|
@ -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<Props> {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
@ -49,7 +43,7 @@ class Chat extends AbstractChat<Props> {
|
|||
*/
|
||||
render() {
|
||||
const { _messages, route } = this.props;
|
||||
const privateMessageRecipient = route.params?.privateMessageRecipient;
|
||||
const privateMessageRecipient = route?.params?.privateMessageRecipient;
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
|
@ -68,28 +62,26 @@ class Chat extends AbstractChat<Props> {
|
|||
}
|
||||
|
||||
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: () => (
|
||||
<TabBarLabelCounter
|
||||
activeUnreadNr = { unreadMessagesNr }
|
||||
isFocused = { isFocused }
|
||||
label = { t('chat.tabs.chat') }
|
||||
nbUnread = { _nbUnreadMessages } />
|
||||
)
|
||||
});
|
||||
|
||||
return () => props.dispatch(closeChat());
|
||||
}, [ nrUnreadMessages ]);
|
||||
return () => isFocused && dispatch(closeChat());
|
||||
}, [ isFocused, _nbUnreadMessages ]);
|
||||
|
||||
return (
|
||||
<Chat
|
||||
{ ...props }
|
||||
isChatScreenFocused = { isChatScreenFocused } />
|
||||
<Chat { ...props } />
|
||||
);
|
||||
}));
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<Props> {
|
|||
* @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<Props> {
|
|||
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<Props> {
|
|||
// 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<Props> {
|
|||
<View style = { messageBubbleStyle }>
|
||||
<View style = { styles.textWrapper } >
|
||||
{ this._renderDisplayName() }
|
||||
{isGifMessage(messageText)
|
||||
{ isGifMessage(messageText)
|
||||
? <GifMessage message = { messageText } />
|
||||
: (
|
||||
<Linkify linkStyle = { styles.chatLink }>
|
||||
{messageText}
|
||||
<Linkify
|
||||
linkStyle = { styles.chatLink }
|
||||
style = { styles.chatMessage }>
|
||||
{ messageText }
|
||||
</Linkify>
|
||||
)}
|
||||
{ this._renderPrivateNotice() }
|
||||
|
@ -134,14 +125,14 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
* @returns {React$Element<*> | null}
|
||||
*/
|
||||
_renderDisplayName() {
|
||||
const { _styles, message, showDisplayName } = this.props;
|
||||
const { message, showDisplayName } = this.props;
|
||||
|
||||
if (!showDisplayName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style = { _styles.displayName }>
|
||||
<Text style = { styles.senderDisplayName }>
|
||||
{ message.displayName }
|
||||
</Text>
|
||||
);
|
||||
|
@ -153,14 +144,14 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
* @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 (
|
||||
<Text style = { message.lobbyChat ? _styles.lobbyMsgNotice : _styles.privateNotice }>
|
||||
<Text style = { message.lobbyChat ? styles.lobbyMsgNotice : styles.privateNotice }>
|
||||
{ this._getPrivateNoticeMessage() }
|
||||
</Text>
|
||||
);
|
||||
|
@ -172,7 +163,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
* @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<Props> {
|
|||
}
|
||||
|
||||
return (
|
||||
<View style = { _styles.replyContainer }>
|
||||
<View style = { styles.replyContainer }>
|
||||
<PrivateMessageButton
|
||||
isLobbyMessage = { lobbyChat }
|
||||
participantID = { message.id }
|
||||
reply = { true }
|
||||
showLabel = { false }
|
||||
toggledStyles = { _styles.replyStyles } />
|
||||
toggledStyles = { styles.replyStyles } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -217,7 +208,6 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_styles: ColorSchemeRegistry.get(state, 'Chat'),
|
||||
knocking: state['features/lobby'].knocking
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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<Props> {
|
|||
return `key_${index}`;
|
||||
}
|
||||
|
||||
_renderMessage: Object => React$Element<*>;
|
||||
_renderMessage: Object => ReactElement;
|
||||
|
||||
/**
|
||||
* Renders a single chat message.
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { ConfirmDialog } from '../../../base/dialog';
|
||||
|
|
|
@ -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<Props> {
|
|||
return `key_${index}`;
|
||||
}
|
||||
|
||||
_renderListEmptyComponent: () => React$Element<any>;
|
||||
_renderListEmptyComponent: () => ReactElement;
|
||||
|
||||
/**
|
||||
* 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>}
|
||||
*/
|
||||
_renderListEmptyComponent() {
|
||||
const { _styles, t } = this.props;
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<View style = { styles.emptyComponentWrapper }>
|
||||
<Text style = { _styles.emptyComponentText }>
|
||||
<Text style = { styles.emptyComponentText }>
|
||||
{ t('chat.noMessagesMessage') }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderMessageGroup: Object => React$Element<any>;
|
||||
_renderMessageGroup: Object => ReactElement;
|
||||
|
||||
/**
|
||||
* Renders a single chat message.
|
||||
|
@ -114,16 +105,4 @@ class MessageContainer extends AbstractMessageContainer<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
|
|
|
@ -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<Props> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _styles, privateMessageRecipient, t,
|
||||
isLobbyChatActive, lobbyMessageRecipient } = this.props;
|
||||
|
||||
const {
|
||||
isLobbyChatActive,
|
||||
lobbyMessageRecipient,
|
||||
privateMessageRecipient,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
if (isLobbyChatActive) {
|
||||
return (
|
||||
<View style = { _styles.lobbyMessageRecipientContainer }>
|
||||
<Text style = { _styles.messageRecipientText }>
|
||||
<View style = { styles.lobbyMessageRecipientContainer }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.lobbyChatMessageTo', {
|
||||
recipient: lobbyMessageRecipient.name
|
||||
}) }
|
||||
|
@ -114,8 +110,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
|||
<TouchableHighlight
|
||||
onPress = { this._onResetLobbyMessageRecipient }>
|
||||
<Icon
|
||||
src = { IconCloseCircle }
|
||||
style = { _styles.messageRecipientCancelIcon } />
|
||||
src = { IconCloseLarge }
|
||||
style = { styles.messageRecipientCancelIcon } />
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
|
@ -126,8 +122,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
|||
}
|
||||
|
||||
return (
|
||||
<View style = { _styles.messageRecipientContainer }>
|
||||
<Text style = { _styles.messageRecipientText }>
|
||||
<View style = { styles.messageRecipientContainer }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
{ t('chat.messageTo', {
|
||||
recipient: privateMessageRecipient.name
|
||||
}) }
|
||||
|
@ -136,8 +132,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
|
|||
onPress = { this._onResetPrivateMessageRecipient }
|
||||
underlayColor = { 'transparent' }>
|
||||
<Icon
|
||||
src = { IconCloseCircle }
|
||||
style = { _styles.messageRecipientCancelIcon } />
|
||||
src = { IconCloseLarge }
|
||||
style = { styles.messageRecipientCancelIcon } />
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
);
|
||||
|
@ -154,7 +150,6 @@ function _mapStateToProps(state) {
|
|||
const { lobbyMessageRecipient, isLobbyChatActive } = state['features/chat'];
|
||||
|
||||
return {
|
||||
_styles: ColorSchemeRegistry.get(state, 'Chat'),
|
||||
isLobbyChatActive,
|
||||
lobbyMessageRecipient
|
||||
};
|
||||
|
|
|
@ -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<Props, any> {
|
|||
* @param {Props} ownProps - The own props of the component.
|
||||
* @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 { disablePolls } = state['features/base/config'];
|
||||
const { visible = enabled, isLobbyMessage, participantID } = ownProps;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// @flow
|
||||
|
||||
import clsx from 'clsx';
|
||||
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 */
|
||||
|
||||
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 }>
|
||||
<ChatTab.Screen
|
||||
component = { Chat }
|
||||
listeners = {{
|
||||
tabPress: () => {
|
||||
dispatch(setIsPollsTabFocused(false));
|
||||
}
|
||||
}}
|
||||
name = { screen.conference.chatandpolls.tab.chat } />
|
||||
<ChatTab.Screen
|
||||
component = { PollsPane }
|
||||
listeners = {{
|
||||
tabPress: () => {
|
||||
dispatch(setIsPollsTabFocused(true));
|
||||
dispatch(resetNbUnreadPollsMessages);
|
||||
}
|
||||
}}
|
||||
name = { screen.conference.chatandpolls.tab.polls } />
|
||||
</ChatTab.Navigator>
|
||||
);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 = () => {
|
|||
<Icon
|
||||
color = { BaseTheme.palette.icon03 }
|
||||
size = { 160 }
|
||||
src = { IconChatUnread } />
|
||||
src = { IconMessage } />
|
||||
<Text style = { chatStyles.noPollText } >
|
||||
{
|
||||
t('polls.results.empty')
|
||||
|
|
|
@ -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: () => (
|
||||
<TabBarLabelCounter
|
||||
activeUnreadNr = { activeUnreadPollsNr }
|
||||
isFocused = { isPollsTabFocused }
|
||||
label = { t('chat.tabs.polls') }
|
||||
nbUnread = { nbUnreadPolls } />
|
||||
)
|
||||
});
|
||||
}, [ nrUnreadPolls ]);
|
||||
|
||||
}, [ isPollsTabFocused, nbUnreadPolls ]);
|
||||
|
||||
return (
|
||||
<JitsiScreen
|
||||
|
|
|
@ -110,10 +110,12 @@ export const chatStyles = createStyleSheet({
|
|||
|
||||
noPollContent: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
paddingTop: '4%'
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: '25%'
|
||||
},
|
||||
|
||||
noPollText: {
|
||||
|
@ -149,7 +151,7 @@ export const chatStyles = createStyleSheet({
|
|||
|
||||
pollCreateButton: {
|
||||
flex: 1,
|
||||
marginHorizontal: BaseTheme.spacing[2]
|
||||
marginHorizontal: BaseTheme.spacing[1]
|
||||
},
|
||||
|
||||
pollSendLabel: {
|
||||
|
@ -191,7 +193,8 @@ export const chatStyles = createStyleSheet({
|
|||
},
|
||||
|
||||
pollCreateAddButton: {
|
||||
margin: BaseTheme.spacing[2]
|
||||
marginHorizontal: BaseTheme.spacing[1],
|
||||
marginVertical: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
toggleText: {
|
||||
|
@ -200,8 +203,8 @@ export const chatStyles = createStyleSheet({
|
|||
},
|
||||
|
||||
createPollButton: {
|
||||
marginHorizontal: BaseTheme.spacing[4],
|
||||
marginVertical: '8%'
|
||||
marginHorizontal: BaseTheme.spacing[3],
|
||||
marginVertical: 34
|
||||
},
|
||||
|
||||
pollPane: {
|
||||
|
@ -218,5 +221,28 @@ export const chatStyles = createStyleSheet({
|
|||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
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 */
|
||||
// @ts-ignore
|
||||
import React from 'react';
|
||||
|
||||
import Button from '../../../base/ui/components/web/Button';
|
||||
|
|
Loading…
Reference in New Issue