diff --git a/lang/main.json b/lang/main.json index 84f11c5bc..e4806df70 100644 --- a/lang/main.json +++ b/lang/main.json @@ -47,8 +47,10 @@ }, "chat": { "error": "Error: your message was not sent. Reason: {{error}}", + "fieldPlaceHolder": "Type your message here", "messagebox": "Type a message", "messageTo": "Private message to {{recipient}}", + "noMessagesMessage": "There are no messages in the meeting yet. Start a conversation here!", "nickname": { "popover": "Choose a nickname", "title": "Enter a nickname to use chat" diff --git a/react/features/chat/components/AbstractMessageContainer.js b/react/features/chat/components/AbstractMessageContainer.js index 766d73e32..91843075e 100644 --- a/react/features/chat/components/AbstractMessageContainer.js +++ b/react/features/chat/components/AbstractMessageContainer.js @@ -15,7 +15,7 @@ export type Props = { * * @extends PureComponent */ -export default class AbstractMessageContainer extends PureComponent { +export default class AbstractMessageContainer extends PureComponent

{ static defaultProps = { messages: [] }; @@ -46,7 +46,7 @@ export default class AbstractMessageContainer extends PureComponent { } } - groups.push(currentGrouping); + currentGrouping.length && groups.push(currentGrouping); return groups; } diff --git a/react/features/chat/components/native/ChatInputBar.js b/react/features/chat/components/native/ChatInputBar.js index 047fe28c0..91a6082ee 100644 --- a/react/features/chat/components/native/ChatInputBar.js +++ b/react/features/chat/components/native/ChatInputBar.js @@ -3,6 +3,7 @@ import React, { Component } from 'react'; import { TextInput, TouchableOpacity, View } from 'react-native'; +import { translate } from '../../../base/i18n'; import { Icon, IconChatSend } from '../../../base/icons'; import { Platform } from '../../../base/react'; @@ -13,7 +14,12 @@ type Props = { /** * Callback to invoke on message send. */ - onSend: Function + onSend: Function, + + /** + * Function to be used to translate i18n labels. + */ + t: Function }; type State = { @@ -37,7 +43,7 @@ type State = { /** * Implements the chat input bar with text field and action(s). */ -export default class ChatInputBar extends Component { +class ChatInputBar extends Component { /** * Instantiates a new instance of the component. * @@ -53,6 +59,7 @@ export default class ChatInputBar extends Component { }; this._onChangeText = this._onChangeText.bind(this); + this._onFieldReferenceAvailable = this._onFieldReferenceAvailable.bind(this); this._onFocused = this._onFocused.bind(this); this._onSubmit = this._onSubmit.bind(this); } @@ -76,6 +83,8 @@ export default class ChatInputBar extends Component { onChangeText = { this._onChangeText } onFocus = { this._onFocused(true) } onSubmitEditing = { this._onSubmit } + placeholder = { this.props.t('chat.fieldPlaceHolder') } + ref = { this._onFieldReferenceAvailable } returnKeyType = 'send' style = { styles.inputField } value = { this.state.message } /> @@ -105,6 +114,18 @@ export default class ChatInputBar extends Component { }); } + _onFieldReferenceAvailable: Object => void; + + /** + * Callback to be invoked when the field reference is available. + * + * @param {Object} field - The reference to the field. + * @returns {void} + */ + _onFieldReferenceAvailable(field) { + field && field.focus(); + } + _onFocused: boolean => Function; /** @@ -138,3 +159,5 @@ export default class ChatInputBar extends Component { }); } } + +export default translate(ChatInputBar); diff --git a/react/features/chat/components/native/MessageContainer.js b/react/features/chat/components/native/MessageContainer.js index a2a6c23fe..ef45a7d84 100644 --- a/react/features/chat/components/native/MessageContainer.js +++ b/react/features/chat/components/native/MessageContainer.js @@ -1,18 +1,36 @@ // @flow import React from 'react'; -import { FlatList } from 'react-native'; +import { FlatList, Text, View } from 'react-native'; -import AbstractMessageContainer, { type Props } +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'; import ChatMessageGroup from './ChatMessageGroup'; import styles from './styles'; +type Props = AbstractProps & { + + /** + * The color-schemed stylesheet of the feature. + */ + _styles: StyleType, + + /** + * Function to be used to translate i18n labels. + */ + t: Function +}; + /** * Implements a container to render all the chat messages in a conference. */ -export default class MessageContainer extends AbstractMessageContainer { +class MessageContainer extends AbstractMessageContainer { /** * Instantiates a new instance of the component. * @@ -22,6 +40,7 @@ export default class MessageContainer extends AbstractMessageContainer { super(props); this._keyExtractor = this._keyExtractor.bind(this); + this._renderListEmptyComponent = this._renderListEmptyComponent.bind(this); this._renderMessageGroup = this._renderMessageGroup.bind(this); } @@ -31,10 +50,16 @@ export default class MessageContainer extends AbstractMessageContainer { * @inheritdoc */ render() { + const data = this._getMessagesGroupedBySender(); + return ( React$Element<*>; + _renderListEmptyComponent: () => React$Element; + + /** + * Renders a message when there are no messages in the chat yet. + * + * @returns {React$Element} + */ + _renderListEmptyComponent() { + const { _styles, t } = this.props; + + return ( + + + { t('chat.noMessagesMessage') } + + + ); + } + + _renderMessageGroup: Object => React$Element; /** * Renders a single chat message. @@ -70,3 +114,17 @@ export default class MessageContainer extends AbstractMessageContainer { return ; } } + +/** + * 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)); diff --git a/react/features/chat/components/native/styles.js b/react/features/chat/components/native/styles.js index 53276706a..4a4b5eaa5 100644 --- a/react/features/chat/components/native/styles.js +++ b/react/features/chat/components/native/styles.js @@ -42,6 +42,13 @@ export default { flexDirection: 'column' }, + emptyComponentWrapper: { + alignSelf: 'center', + flex: 1, + padding: BoxModel.padding, + paddingTop: '10%' + }, + /** * A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar). */ @@ -143,6 +150,11 @@ ColorSchemeRegistry.register('Chat', { fontSize: 13 }, + emptyComponentText: { + color: schemeColor('displayName'), + textAlign: 'center' + }, + localMessageBubble: { backgroundColor: schemeColor('localMsgBackground'), borderTopRightRadius: 0 diff --git a/react/features/chat/components/web/MessageContainer.js b/react/features/chat/components/web/MessageContainer.js index 5b44de49d..8639ced87 100644 --- a/react/features/chat/components/web/MessageContainer.js +++ b/react/features/chat/components/web/MessageContainer.js @@ -14,7 +14,7 @@ import ChatMessageGroup from './ChatMessageGroup'; * * @extends AbstractMessageContainer */ -export default class MessageContainer extends AbstractMessageContainer { +export default class MessageContainer extends AbstractMessageContainer { /** * Whether or not chat has been scrolled to the bottom of the screen. Used * to determine if chat should be scrolled automatically to the bottom when