feat: add chat color scheming
This commit is contained in:
parent
8be02f9ca1
commit
a35099f949
|
@ -153,6 +153,8 @@ class ColorSchemeRegistry {
|
||||||
const colorScheme = toState(stateful)['features/base/color-scheme'];
|
const colorScheme = toState(stateful)['features/base/color-scheme'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
...defaultScheme._defaultTheme,
|
||||||
|
...colorScheme._defaultTheme,
|
||||||
...defaultScheme[componentName],
|
...defaultScheme[componentName],
|
||||||
...colorScheme[componentName]
|
...colorScheme[componentName]
|
||||||
}[colorDefinition];
|
}[colorDefinition];
|
||||||
|
|
|
@ -6,18 +6,26 @@ import { ColorPalette, getRGBAFormat } from '../styles';
|
||||||
* The default color scheme of the application.
|
* The default color scheme of the application.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
'BottomSheet': {
|
'_defaultTheme': {
|
||||||
|
// Generic app theme colors that are used accross the entire app.
|
||||||
|
// All scheme definitions below inherit these values.
|
||||||
background: 'rgb(255, 255, 255)',
|
background: 'rgb(255, 255, 255)',
|
||||||
icon: '#1c2025',
|
icon: 'rgb(28, 32, 37)',
|
||||||
label: '#1c2025'
|
text: 'rgb(28, 32, 37)'
|
||||||
|
},
|
||||||
|
'Chat': {
|
||||||
|
displayName: 'rgb(94, 109, 121)',
|
||||||
|
localMsgBackground: 'rgb(215, 230, 249)',
|
||||||
|
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)'
|
||||||
},
|
},
|
||||||
'Dialog': {
|
'Dialog': {
|
||||||
background: 'rgb(255, 255, 255)',
|
|
||||||
border: 'rgba(0, 3, 6, 0.6)',
|
border: 'rgba(0, 3, 6, 0.6)',
|
||||||
buttonBackground: ColorPalette.blue,
|
buttonBackground: ColorPalette.blue,
|
||||||
buttonLabel: ColorPalette.white,
|
buttonLabel: ColorPalette.white
|
||||||
icon: '#1c2025',
|
|
||||||
text: '#1c2025'
|
|
||||||
},
|
},
|
||||||
'Header': {
|
'Header': {
|
||||||
background: ColorPalette.blue,
|
background: ColorPalette.blue,
|
||||||
|
@ -30,8 +38,7 @@ export default {
|
||||||
background: 'rgb(42, 58, 75)'
|
background: 'rgb(42, 58, 75)'
|
||||||
},
|
},
|
||||||
'LoadConfigOverlay': {
|
'LoadConfigOverlay': {
|
||||||
background: 'rgb(249, 249, 249)',
|
background: 'rgb(249, 249, 249)'
|
||||||
text: 'rgb(28, 32, 37)'
|
|
||||||
},
|
},
|
||||||
'Thumbnail': {
|
'Thumbnail': {
|
||||||
activeParticipantHighlight: 'rgb(81, 214, 170)',
|
activeParticipantHighlight: 'rgb(81, 214, 170)',
|
||||||
|
|
|
@ -147,7 +147,7 @@ ColorSchemeRegistry.register('BottomSheet', {
|
||||||
* Style for the label in a generic item rendered in the menu.
|
* Style for the label in a generic item rendered in the menu.
|
||||||
*/
|
*/
|
||||||
labelStyle: {
|
labelStyle: {
|
||||||
color: schemeColor('label'),
|
color: schemeColor('text'),
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
fontSize: MD_FONT_SIZE,
|
fontSize: MD_FONT_SIZE,
|
||||||
marginLeft: 32,
|
marginLeft: 32,
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { getLocalizedDateFormatter } from '../../base/i18n';
|
import { getLocalizedDateFormatter } from '../../base/i18n';
|
||||||
|
|
||||||
|
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formatter string to display the message timestamp.
|
* Formatter string to display the message timestamp.
|
||||||
*/
|
*/
|
||||||
|
@ -65,7 +67,7 @@ export default class AbstractChatMessage<P: Props> extends PureComponent<P> {
|
||||||
_getMessageText() {
|
_getMessageText() {
|
||||||
const { message } = this.props;
|
const { message } = this.props;
|
||||||
|
|
||||||
return message.messageType === 'error'
|
return message.messageType === MESSAGE_TYPE_ERROR
|
||||||
? this.props.t('chat.error', {
|
? this.props.t('chat.error', {
|
||||||
error: message.message
|
error: message.message
|
||||||
})
|
})
|
||||||
|
@ -81,7 +83,7 @@ export default class AbstractChatMessage<P: Props> extends PureComponent<P> {
|
||||||
const { message, t } = this.props;
|
const { message, t } = this.props;
|
||||||
|
|
||||||
return t('chat.privateNotice', {
|
return t('chat.privateNotice', {
|
||||||
recipient: message.messageType === 'local' ? message.recipient : t('chat.you')
|
recipient: message.messageType === MESSAGE_TYPE_LOCAL ? message.recipient : t('chat.you')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { getParticipantDisplayName } from '../../base/participants';
|
||||||
|
|
||||||
import { setPrivateMessageRecipient } from '../actions';
|
import { setPrivateMessageRecipient } from '../actions';
|
||||||
|
|
||||||
type Props = {
|
export type Props = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function used to translate i18n labels.
|
* Function used to translate i18n labels.
|
||||||
|
@ -27,7 +27,7 @@ type Props = {
|
||||||
/**
|
/**
|
||||||
* Abstract class for the {@code MessageRecipient} component.
|
* Abstract class for the {@code MessageRecipient} component.
|
||||||
*/
|
*/
|
||||||
export default class AbstractMessageRecipient extends PureComponent<Props> {
|
export default class AbstractMessageRecipient<P: Props> extends PureComponent<P> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
|
import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
|
||||||
|
|
||||||
|
import { ColorSchemeRegistry } from '../../../base/color-scheme';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
|
|
||||||
import { HeaderWithNavigation, SlidingView } from '../../../base/react';
|
import { HeaderWithNavigation, SlidingView } from '../../../base/react';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
import { StyleType } from '../../../base/styles';
|
||||||
|
|
||||||
import AbstractChat, {
|
import AbstractChat, {
|
||||||
_mapDispatchToProps,
|
_mapDispatchToProps,
|
||||||
_mapStateToProps,
|
_mapStateToProps as _abstractMapStateToProps,
|
||||||
type Props
|
type Props as AbstractProps
|
||||||
} from '../AbstractChat';
|
} from '../AbstractChat';
|
||||||
|
|
||||||
import ChatInputBar from './ChatInputBar';
|
import ChatInputBar from './ChatInputBar';
|
||||||
|
@ -19,6 +20,14 @@ import MessageContainer from './MessageContainer';
|
||||||
import MessageRecipient from './MessageRecipient';
|
import MessageRecipient from './MessageRecipient';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
type Props = AbstractProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color-schemed stylesheet of the feature.
|
||||||
|
*/
|
||||||
|
_styles: StyleType
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React native component that renders the chat window (modal) of
|
* Implements a React native component that renders the chat window (modal) of
|
||||||
* the mobile client.
|
* the mobile client.
|
||||||
|
@ -41,6 +50,8 @@ class Chat extends AbstractChat<Props> {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
const { _styles } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SlidingView
|
<SlidingView
|
||||||
onHide = { this._onClose }
|
onHide = { this._onClose }
|
||||||
|
@ -52,7 +63,7 @@ class Chat extends AbstractChat<Props> {
|
||||||
<HeaderWithNavigation
|
<HeaderWithNavigation
|
||||||
headerLabelKey = 'chat.title'
|
headerLabelKey = 'chat.title'
|
||||||
onPressBack = { this._onClose } />
|
onPressBack = { this._onClose } />
|
||||||
<SafeAreaView style = { styles.backdrop }>
|
<SafeAreaView style = { _styles.backdrop }>
|
||||||
<MessageContainer messages = { this.props._messages } />
|
<MessageContainer messages = { this.props._messages } />
|
||||||
<MessageRecipient />
|
<MessageRecipient />
|
||||||
<ChatInputBar onSend = { this.props._onSendMessage } />
|
<ChatInputBar onSend = { this.props._onSendMessage } />
|
||||||
|
@ -80,4 +91,17 @@ class Chat extends AbstractChat<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 {
|
||||||
|
..._abstractMapStateToProps(state),
|
||||||
|
_styles: ColorSchemeRegistry.get(state, 'Chat')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));
|
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));
|
||||||
|
|
|
@ -4,16 +4,28 @@ 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 { type StyleType } from '../../../base/styles';
|
||||||
|
|
||||||
|
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||||
import { replaceNonUnicodeEmojis } from '../../functions';
|
import { replaceNonUnicodeEmojis } from '../../functions';
|
||||||
|
|
||||||
import AbstractChatMessage, { type Props } from '../AbstractChatMessage';
|
import AbstractChatMessage, { type Props as AbstractProps } from '../AbstractChatMessage';
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
|
@ -24,55 +36,58 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { message } = this.props;
|
const { _styles, message } = this.props;
|
||||||
const localMessage = message.messageType === 'local';
|
const localMessage = message.messageType === MESSAGE_TYPE_LOCAL;
|
||||||
|
const { privateMessage } = message;
|
||||||
|
|
||||||
// Style arrays that need to be updated in various scenarios, such as
|
// Style arrays that need to be updated in various scenarios, such as
|
||||||
// error messages or others.
|
// error messages or others.
|
||||||
const detailsWrapperStyle = [
|
const detailsWrapperStyle = [
|
||||||
styles.detailsWrapper
|
styles.detailsWrapper
|
||||||
];
|
];
|
||||||
const textWrapperStyle = [
|
const messageBubbleStyle = [
|
||||||
styles.textWrapper
|
styles.messageBubble
|
||||||
];
|
];
|
||||||
|
|
||||||
if (localMessage) {
|
if (localMessage) {
|
||||||
|
// This is a message sent by the local participant.
|
||||||
|
|
||||||
// The wrapper needs to be aligned to the right.
|
// The wrapper needs to be aligned to the right.
|
||||||
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper);
|
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper);
|
||||||
|
|
||||||
// The bubble needs to be differently styled.
|
// The bubble needs some additional styling
|
||||||
textWrapperStyle.push(styles.ownTextWrapper);
|
messageBubbleStyle.push(_styles.localMessageBubble);
|
||||||
} else if (message.messageType === 'error') {
|
} else if (message.messageType === MESSAGE_TYPE_ERROR) {
|
||||||
// The bubble needs to be differently styled.
|
// This is a system message.
|
||||||
textWrapperStyle.push(styles.systemTextWrapper);
|
|
||||||
|
// The bubble needs some additional styling
|
||||||
|
messageBubbleStyle.push(styles.systemMessageBubble);
|
||||||
|
} else {
|
||||||
|
// This is a remote message sent by a remote participant.
|
||||||
|
|
||||||
|
// The bubble needs some additional styling
|
||||||
|
messageBubbleStyle.push(_styles.remoteMessageBubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (privateMessage) {
|
||||||
|
messageBubbleStyle.push(_styles.privateMessageBubble);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style = { styles.messageWrapper } >
|
<View style = { styles.messageWrapper } >
|
||||||
{ this._renderAvatar() }
|
{ this._renderAvatar() }
|
||||||
<View style = { detailsWrapperStyle }>
|
<View style = { detailsWrapperStyle }>
|
||||||
<View style = { styles.replyWrapper }>
|
<View style = { messageBubbleStyle }>
|
||||||
<View style = { textWrapperStyle } >
|
<View style = { styles.textWrapper } >
|
||||||
{
|
{ this._renderDisplayName() }
|
||||||
this.props.showDisplayName
|
|
||||||
&& this._renderDisplayName()
|
|
||||||
}
|
|
||||||
<Linkify linkStyle = { styles.chatLink }>
|
<Linkify linkStyle = { styles.chatLink }>
|
||||||
{ replaceNonUnicodeEmojis(this._getMessageText()) }
|
{ replaceNonUnicodeEmojis(this._getMessageText()) }
|
||||||
</Linkify>
|
</Linkify>
|
||||||
{
|
{ this._renderPrivateNotice() }
|
||||||
message.privateMessage
|
|
||||||
&& this._renderPrivateNotice()
|
|
||||||
}
|
|
||||||
</View>
|
</View>
|
||||||
{ message.privateMessage && !localMessage
|
{ this._renderPrivateReplyButton() }
|
||||||
&& <PrivateMessageButton
|
|
||||||
participantID = { message.id }
|
|
||||||
reply = { true }
|
|
||||||
showLabel = { false }
|
|
||||||
toggledStyles = { styles.replyStyles } /> }
|
|
||||||
</View>
|
</View>
|
||||||
{ this.props.showTimestamp && this._renderTimestamp() }
|
{ this._renderTimestamp() }
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -104,37 +119,77 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the display name of the sender.
|
* Renders the display name of the sender if necessary.
|
||||||
*
|
*
|
||||||
* @returns {React$Element<*>}
|
* @returns {React$Element<*> | null}
|
||||||
*/
|
*/
|
||||||
_renderDisplayName() {
|
_renderDisplayName() {
|
||||||
|
const { _styles, message, showDisplayName } = this.props;
|
||||||
|
|
||||||
|
if (!showDisplayName) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style = { styles.displayName }>
|
<Text style = { _styles.displayName }>
|
||||||
{ this.props.message.displayName }
|
{ message.displayName }
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the message privacy notice.
|
* Renders the message privacy notice, if necessary.
|
||||||
*
|
*
|
||||||
* @returns {React$Element<*>}
|
* @returns {React$Element<*> | null}
|
||||||
*/
|
*/
|
||||||
_renderPrivateNotice() {
|
_renderPrivateNotice() {
|
||||||
|
const { _styles, message } = this.props;
|
||||||
|
|
||||||
|
if (!message.privateMessage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style = { styles.privateNotice }>
|
<Text style = { _styles.privateNotice }>
|
||||||
{ this._getPrivateNoticeMessage() }
|
{ this._getPrivateNoticeMessage() }
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the time at which the message was sent.
|
* Renders the private reply button, if necessary.
|
||||||
*
|
*
|
||||||
* @returns {React$Element<*>}
|
* @returns {React$Element<*> | null}
|
||||||
|
*/
|
||||||
|
_renderPrivateReplyButton() {
|
||||||
|
const { _styles, message } = this.props;
|
||||||
|
const { messageType, privateMessage } = message;
|
||||||
|
|
||||||
|
if (!privateMessage || messageType === MESSAGE_TYPE_LOCAL) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style = { _styles.replyContainer }>
|
||||||
|
<PrivateMessageButton
|
||||||
|
participantID = { message.id }
|
||||||
|
reply = { true }
|
||||||
|
showLabel = { false }
|
||||||
|
toggledStyles = { _styles.replyStyles } />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the time at which the message was sent, if necessary.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<*> | null}
|
||||||
*/
|
*/
|
||||||
_renderTimestamp() {
|
_renderTimestamp() {
|
||||||
|
if (!this.props.showTimestamp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style = { styles.timeText }>
|
<Text style = { styles.timeText }>
|
||||||
{ this._getFormattedTimestamp() }
|
{ this._getFormattedTimestamp() }
|
||||||
|
@ -143,4 +198,16 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translate(ChatMessage);
|
/**
|
||||||
|
* 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)(ChatMessage));
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
import React, { Component } 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 ChatMessage from './ChatMessage';
|
import ChatMessage from './ChatMessage';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
|
@ -73,11 +75,11 @@ export default class ChatMessageGroup extends Component<Props> {
|
||||||
<ChatMessage
|
<ChatMessage
|
||||||
message = { message }
|
message = { message }
|
||||||
showAvatar = {
|
showAvatar = {
|
||||||
this.props.messages[0].messageType !== 'local'
|
this.props.messages[0].messageType !== MESSAGE_TYPE_LOCAL
|
||||||
&& index === this.props.messages.length - 1
|
&& index === this.props.messages.length - 1
|
||||||
}
|
}
|
||||||
showDisplayName = {
|
showDisplayName = {
|
||||||
this.props.messages[0].messageType === 'remote'
|
this.props.messages[0].messageType === MESSAGE_TYPE_REMOTE
|
||||||
&& index === this.props.messages.length - 1
|
&& index === this.props.messages.length - 1
|
||||||
}
|
}
|
||||||
showTimestamp = { index === 0 } />
|
showTimestamp = { index === 0 } />
|
||||||
|
|
|
@ -3,28 +3,37 @@
|
||||||
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, IconCancelSelection } from '../../../base/icons';
|
import { Icon, IconCancelSelection } from '../../../base/icons';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../base/redux';
|
||||||
|
import { type StyleType } from '../../../base/styles';
|
||||||
|
|
||||||
import AbstractMessageRecipient, {
|
import AbstractMessageRecipient, {
|
||||||
_mapDispatchToProps,
|
_mapDispatchToProps,
|
||||||
_mapStateToProps
|
_mapStateToProps as _abstractMapStateToProps,
|
||||||
|
type Props as AbstractProps
|
||||||
} from '../AbstractMessageRecipient';
|
} from '../AbstractMessageRecipient';
|
||||||
|
|
||||||
import styles from './styles';
|
type Props = AbstractProps & {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color-schemed stylesheet of the feature.
|
||||||
|
*/
|
||||||
|
_styles: StyleType
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to implement the displaying of the recipient of the next message.
|
* Class to implement the displaying of the recipient of the next message.
|
||||||
*/
|
*/
|
||||||
class MessageRecipient extends AbstractMessageRecipient {
|
class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||||
/**
|
/**
|
||||||
* Implements {@code PureComponent#render}.
|
* Implements {@code PureComponent#render}.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { _privateMessageRecipient } = this.props;
|
const { _privateMessageRecipient, _styles } = this.props;
|
||||||
|
|
||||||
if (!_privateMessageRecipient) {
|
if (!_privateMessageRecipient) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -33,8 +42,8 @@ class MessageRecipient extends AbstractMessageRecipient {
|
||||||
const { t } = this.props;
|
const { t } = this.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
|
recipient: _privateMessageRecipient
|
||||||
}) }
|
}) }
|
||||||
|
@ -42,11 +51,24 @@ class MessageRecipient extends AbstractMessageRecipient {
|
||||||
<TouchableHighlight onPress = { this.props._onRemovePrivateMessageRecipient }>
|
<TouchableHighlight onPress = { this.props._onRemovePrivateMessageRecipient }>
|
||||||
<Icon
|
<Icon
|
||||||
src = { IconCancelSelection }
|
src = { IconCancelSelection }
|
||||||
style = { styles.messageRecipientCancelIcon } />
|
style = { _styles.messageRecipientCancelIcon } />
|
||||||
</TouchableHighlight>
|
</TouchableHighlight>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps part of the redux state to the props of this component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @returns {Props}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
..._abstractMapStateToProps(state),
|
||||||
|
_styles: ColorSchemeRegistry.get(state, 'Chat')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(MessageRecipient));
|
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(MessageRecipient));
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
|
||||||
import { BoxModel, ColorPalette } from '../../../base/styles';
|
import { BoxModel, ColorPalette } from '../../../base/styles';
|
||||||
|
|
||||||
|
const BUBBLE_RADIUS = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styles of the feature chat.
|
* The styles of the feature chat.
|
||||||
*
|
*
|
||||||
|
@ -20,14 +23,6 @@ export default {
|
||||||
width: 32
|
width: 32
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Background of the chat screen.
|
|
||||||
*/
|
|
||||||
backdrop: {
|
|
||||||
backgroundColor: ColorPalette.white,
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
|
|
||||||
chatContainer: {
|
chatContainer: {
|
||||||
alignItems: 'stretch',
|
alignItems: 'stretch',
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
@ -47,14 +42,6 @@ export default {
|
||||||
flexDirection: 'column'
|
flexDirection: 'column'
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* The text node for the display name.
|
|
||||||
*/
|
|
||||||
displayName: {
|
|
||||||
color: 'rgb(118, 136, 152)',
|
|
||||||
fontSize: 13
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
|
* A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
|
||||||
*/
|
*/
|
||||||
|
@ -76,35 +63,16 @@ export default {
|
||||||
height: 48
|
height: 48
|
||||||
},
|
},
|
||||||
|
|
||||||
|
messageBubble: {
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: BUBBLE_RADIUS,
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
|
|
||||||
messageContainer: {
|
messageContainer: {
|
||||||
flex: 1
|
flex: 1
|
||||||
},
|
},
|
||||||
|
|
||||||
messageRecipientCancelIcon: {
|
|
||||||
color: ColorPalette.white,
|
|
||||||
fontSize: 18
|
|
||||||
},
|
|
||||||
|
|
||||||
messageRecipientContainer: {
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: ColorPalette.warning,
|
|
||||||
flexDirection: 'row',
|
|
||||||
padding: BoxModel.padding
|
|
||||||
},
|
|
||||||
|
|
||||||
messageRecipientText: {
|
|
||||||
color: ColorPalette.white,
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The message text itself.
|
|
||||||
*/
|
|
||||||
messageText: {
|
|
||||||
color: 'rgb(28, 32, 37)',
|
|
||||||
fontSize: 15
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper View for the entire block.
|
* Wrapper View for the entire block.
|
||||||
*/
|
*/
|
||||||
|
@ -123,34 +91,11 @@ export default {
|
||||||
alignItems: 'flex-end'
|
alignItems: 'flex-end'
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Style modifier for the {@code textWrapper} for own messages.
|
|
||||||
*/
|
|
||||||
ownTextWrapper: {
|
|
||||||
backgroundColor: 'rgb(210, 231, 249)',
|
|
||||||
borderTopLeftRadius: 8,
|
|
||||||
borderTopRightRadius: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
replyWrapper: {
|
replyWrapper: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
|
|
||||||
replyStyles: {
|
|
||||||
iconStyle: {
|
|
||||||
color: 'rgb(118, 136, 152)',
|
|
||||||
fontSize: 22,
|
|
||||||
margin: BoxModel.margin / 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
privateNotice: {
|
|
||||||
color: ColorPalette.warning,
|
|
||||||
fontSize: 13,
|
|
||||||
fontStyle: 'italic'
|
|
||||||
},
|
|
||||||
|
|
||||||
sendButtonIcon: {
|
sendButtonIcon: {
|
||||||
color: ColorPalette.darkGrey,
|
color: ColorPalette.darkGrey,
|
||||||
fontSize: 22
|
fontSize: 22
|
||||||
|
@ -159,7 +104,7 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Style modifier for system (error) messages.
|
* Style modifier for system (error) messages.
|
||||||
*/
|
*/
|
||||||
systemTextWrapper: {
|
systemMessageBubble: {
|
||||||
backgroundColor: 'rgb(247, 215, 215)'
|
backgroundColor: 'rgb(247, 215, 215)'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -168,9 +113,6 @@ export default {
|
||||||
*/
|
*/
|
||||||
textWrapper: {
|
textWrapper: {
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
backgroundColor: 'rgb(240, 243, 247)',
|
|
||||||
borderRadius: 8,
|
|
||||||
borderTopLeftRadius: 0,
|
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: 9
|
padding: 9
|
||||||
},
|
},
|
||||||
|
@ -183,3 +125,73 @@ export default {
|
||||||
fontSize: 13
|
fontSize: 13
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
|
||||||
|
localMessageBubble: {
|
||||||
|
backgroundColor: schemeColor('localMsgBackground'),
|
||||||
|
borderTopRightRadius: 0
|
||||||
|
},
|
||||||
|
|
||||||
|
messageRecipientCancelIcon: {
|
||||||
|
color: schemeColor('icon'),
|
||||||
|
fontSize: 18
|
||||||
|
},
|
||||||
|
|
||||||
|
messageRecipientContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: schemeColor('privateMsgBackground'),
|
||||||
|
flexDirection: 'row',
|
||||||
|
padding: BoxModel.padding
|
||||||
|
},
|
||||||
|
|
||||||
|
messageRecipientText: {
|
||||||
|
color: schemeColor('text'),
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { toArray } from 'react-emoji-render';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { Linkify } from '../../../base/react';
|
import { Linkify } from '../../../base/react';
|
||||||
|
|
||||||
|
import { MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||||
|
|
||||||
import AbstractChatMessage, {
|
import AbstractChatMessage, {
|
||||||
type Props
|
type Props
|
||||||
} from '../AbstractChatMessage';
|
} from '../AbstractChatMessage';
|
||||||
|
@ -47,7 +49,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
||||||
</div>
|
</div>
|
||||||
{ message.privateMessage && this._renderPrivateNotice() }
|
{ message.privateMessage && this._renderPrivateNotice() }
|
||||||
</div>
|
</div>
|
||||||
{ message.privateMessage && message.messageType !== 'local'
|
{ message.privateMessage && message.messageType !== MESSAGE_TYPE_LOCAL
|
||||||
&& <PrivateMessageButton
|
&& <PrivateMessageButton
|
||||||
participantID = { message.id }
|
participantID = { message.id }
|
||||||
reply = { true }
|
reply = { true }
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { MESSAGE_TYPE_REMOTE } from '../../constants';
|
||||||
|
|
||||||
import AbstractMessageContainer, { type Props }
|
import AbstractMessageContainer, { type Props }
|
||||||
from '../AbstractMessageContainer';
|
from '../AbstractMessageContainer';
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ export default class MessageContainer extends AbstractMessageContainer {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatMessageGroup
|
<ChatMessageGroup
|
||||||
className = { messageType || 'remote' }
|
className = { messageType || MESSAGE_TYPE_REMOTE }
|
||||||
key = { index }
|
key = { index }
|
||||||
messages = { group } />
|
messages = { group } />
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,13 +8,14 @@ import { connect } from '../../../base/redux';
|
||||||
|
|
||||||
import AbstractMessageRecipient, {
|
import AbstractMessageRecipient, {
|
||||||
_mapDispatchToProps,
|
_mapDispatchToProps,
|
||||||
_mapStateToProps
|
_mapStateToProps,
|
||||||
|
type Props
|
||||||
} from '../AbstractMessageRecipient';
|
} from '../AbstractMessageRecipient';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to implement the displaying of the recipient of the next message.
|
* Class to implement the displaying of the recipient of the next message.
|
||||||
*/
|
*/
|
||||||
class MessageRecipient extends AbstractMessageRecipient {
|
class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||||
/**
|
/**
|
||||||
* Implements {@code PureComponent#render}.
|
* Implements {@code PureComponent#render}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The audio ID of the audio element for which the {@link playAudio} action is
|
* The audio ID of the audio element for which the {@link playAudio} action is
|
||||||
* triggered when new chat message is received.
|
* triggered when new chat message is received.
|
||||||
|
@ -5,3 +7,18 @@
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
export const INCOMING_MSG_SOUND_ID = 'INCOMING_MSG_SOUND';
|
export const INCOMING_MSG_SOUND_ID = 'INCOMING_MSG_SOUND';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code messageType} of error (system) messages.
|
||||||
|
*/
|
||||||
|
export const MESSAGE_TYPE_ERROR = 'error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code messageType} of local messages.
|
||||||
|
*/
|
||||||
|
export const MESSAGE_TYPE_LOCAL = 'local';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code messageType} of remote messages.
|
||||||
|
*/
|
||||||
|
export const MESSAGE_TYPE_REMOTE = 'remote';
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { isButtonEnabled, showToolbox } from '../toolbox';
|
||||||
import { SEND_MESSAGE, SET_PRIVATE_MESSAGE_RECIPIENT } from './actionTypes';
|
import { SEND_MESSAGE, SET_PRIVATE_MESSAGE_RECIPIENT } from './actionTypes';
|
||||||
import { addMessage, clearMessages, toggleChat } from './actions';
|
import { addMessage, clearMessages, toggleChat } from './actions';
|
||||||
import { ChatPrivacyDialog } from './components';
|
import { ChatPrivacyDialog } from './components';
|
||||||
import { INCOMING_MSG_SOUND_ID } from './constants';
|
import { INCOMING_MSG_SOUND_ID, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from './constants';
|
||||||
import { INCOMING_MSG_SOUND_FILE } from './sounds';
|
import { INCOMING_MSG_SOUND_FILE } from './sounds';
|
||||||
|
|
||||||
declare var APP: Object;
|
declare var APP: Object;
|
||||||
|
@ -194,7 +194,7 @@ function _addChatMsgListener(conference, store) {
|
||||||
function _handleChatError({ dispatch }, error) {
|
function _handleChatError({ dispatch }, error) {
|
||||||
dispatch(addMessage({
|
dispatch(addMessage({
|
||||||
hasRead: true,
|
hasRead: true,
|
||||||
messageType: 'error',
|
messageType: MESSAGE_TYPE_ERROR,
|
||||||
message: error,
|
message: error,
|
||||||
privateMessage: false,
|
privateMessage: false,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
|
@ -231,7 +231,7 @@ function _handleReceivedMessage({ dispatch, getState }, { id, message, nick, pri
|
||||||
displayName,
|
displayName,
|
||||||
hasRead,
|
hasRead,
|
||||||
id,
|
id,
|
||||||
messageType: participant.local ? 'local' : 'remote',
|
messageType: participant.local ? MESSAGE_TYPE_LOCAL : MESSAGE_TYPE_REMOTE,
|
||||||
message,
|
message,
|
||||||
privateMessage,
|
privateMessage,
|
||||||
recipient: getParticipantDisplayName(state, localParticipant.id),
|
recipient: getParticipantDisplayName(state, localParticipant.id),
|
||||||
|
@ -285,7 +285,7 @@ function _persistSentPrivateMessage({ dispatch, getState }, recipientID, message
|
||||||
displayName,
|
displayName,
|
||||||
hasRead: true,
|
hasRead: true,
|
||||||
id: localParticipant.id,
|
id: localParticipant.id,
|
||||||
messageType: 'local',
|
messageType: MESSAGE_TYPE_LOCAL,
|
||||||
message,
|
message,
|
||||||
privateMessage: true,
|
privateMessage: true,
|
||||||
recipient: getParticipantDisplayName(getState, recipientID),
|
recipient: getParticipantDisplayName(getState, recipientID),
|
||||||
|
@ -323,7 +323,7 @@ function _shouldSendPrivateMessageTo(state, action): ?string {
|
||||||
const lastMessage = navigator.product === 'ReactNative'
|
const lastMessage = navigator.product === 'ReactNative'
|
||||||
? messages[0] : messages[messages.length - 1];
|
? messages[0] : messages[messages.length - 1];
|
||||||
|
|
||||||
if (lastMessage.messageType === 'local') {
|
if (lastMessage.messageType === MESSAGE_TYPE_LOCAL) {
|
||||||
// The sender is probably aware of any private messages as already sent
|
// The sender is probably aware of any private messages as already sent
|
||||||
// a message since then. Doesn't make sense to display the notice now.
|
// a message since then. Doesn't make sense to display the notice now.
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -339,7 +339,7 @@ function _shouldSendPrivateMessageTo(state, action): ?string {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const recentPrivateMessages = messages.filter(
|
const recentPrivateMessages = messages.filter(
|
||||||
message =>
|
message =>
|
||||||
message.messageType !== 'local'
|
message.messageType !== MESSAGE_TYPE_LOCAL
|
||||||
&& message.privateMessage
|
&& message.privateMessage
|
||||||
&& message.timestamp + PRIVACY_NOTICE_TIMEOUT > now);
|
&& message.timestamp + PRIVACY_NOTICE_TIMEOUT > now);
|
||||||
const recentPrivateMessage = navigator.product === 'ReactNative'
|
const recentPrivateMessage = navigator.product === 'ReactNative'
|
||||||
|
|
Loading…
Reference in New Issue