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'];
|
||||
|
||||
return {
|
||||
...defaultScheme._defaultTheme,
|
||||
...colorScheme._defaultTheme,
|
||||
...defaultScheme[componentName],
|
||||
...colorScheme[componentName]
|
||||
}[colorDefinition];
|
||||
|
|
|
@ -6,18 +6,26 @@ import { ColorPalette, getRGBAFormat } from '../styles';
|
|||
* The default color scheme of the application.
|
||||
*/
|
||||
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)',
|
||||
icon: '#1c2025',
|
||||
label: '#1c2025'
|
||||
icon: 'rgb(28, 32, 37)',
|
||||
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': {
|
||||
background: 'rgb(255, 255, 255)',
|
||||
border: 'rgba(0, 3, 6, 0.6)',
|
||||
buttonBackground: ColorPalette.blue,
|
||||
buttonLabel: ColorPalette.white,
|
||||
icon: '#1c2025',
|
||||
text: '#1c2025'
|
||||
buttonLabel: ColorPalette.white
|
||||
},
|
||||
'Header': {
|
||||
background: ColorPalette.blue,
|
||||
|
@ -30,8 +38,7 @@ export default {
|
|||
background: 'rgb(42, 58, 75)'
|
||||
},
|
||||
'LoadConfigOverlay': {
|
||||
background: 'rgb(249, 249, 249)',
|
||||
text: 'rgb(28, 32, 37)'
|
||||
background: 'rgb(249, 249, 249)'
|
||||
},
|
||||
'Thumbnail': {
|
||||
activeParticipantHighlight: 'rgb(81, 214, 170)',
|
||||
|
|
|
@ -147,7 +147,7 @@ ColorSchemeRegistry.register('BottomSheet', {
|
|||
* Style for the label in a generic item rendered in the menu.
|
||||
*/
|
||||
labelStyle: {
|
||||
color: schemeColor('label'),
|
||||
color: schemeColor('text'),
|
||||
flexShrink: 1,
|
||||
fontSize: MD_FONT_SIZE,
|
||||
marginLeft: 32,
|
||||
|
|
|
@ -4,6 +4,8 @@ import { PureComponent } from 'react';
|
|||
|
||||
import { getLocalizedDateFormatter } from '../../base/i18n';
|
||||
|
||||
import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../constants';
|
||||
|
||||
/**
|
||||
* Formatter string to display the message timestamp.
|
||||
*/
|
||||
|
@ -65,7 +67,7 @@ export default class AbstractChatMessage<P: Props> extends PureComponent<P> {
|
|||
_getMessageText() {
|
||||
const { message } = this.props;
|
||||
|
||||
return message.messageType === 'error'
|
||||
return message.messageType === MESSAGE_TYPE_ERROR
|
||||
? this.props.t('chat.error', {
|
||||
error: message.message
|
||||
})
|
||||
|
@ -81,7 +83,7 @@ export default class AbstractChatMessage<P: Props> extends PureComponent<P> {
|
|||
const { message, t } = this.props;
|
||||
|
||||
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';
|
||||
|
||||
type Props = {
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* Function used to translate i18n labels.
|
||||
|
@ -27,7 +27,7 @@ type Props = {
|
|||
/**
|
||||
* 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 { KeyboardAvoidingView, SafeAreaView } from 'react-native';
|
||||
|
||||
import { ColorSchemeRegistry } from '../../../base/color-scheme';
|
||||
import { translate } from '../../../base/i18n';
|
||||
|
||||
import { HeaderWithNavigation, SlidingView } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { StyleType } from '../../../base/styles';
|
||||
|
||||
import AbstractChat, {
|
||||
_mapDispatchToProps,
|
||||
_mapStateToProps,
|
||||
type Props
|
||||
_mapStateToProps as _abstractMapStateToProps,
|
||||
type Props as AbstractProps
|
||||
} from '../AbstractChat';
|
||||
|
||||
import ChatInputBar from './ChatInputBar';
|
||||
|
@ -19,6 +20,14 @@ import MessageContainer from './MessageContainer';
|
|||
import MessageRecipient from './MessageRecipient';
|
||||
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
|
||||
* the mobile client.
|
||||
|
@ -41,6 +50,8 @@ class Chat extends AbstractChat<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _styles } = this.props;
|
||||
|
||||
return (
|
||||
<SlidingView
|
||||
onHide = { this._onClose }
|
||||
|
@ -52,7 +63,7 @@ class Chat extends AbstractChat<Props> {
|
|||
<HeaderWithNavigation
|
||||
headerLabelKey = 'chat.title'
|
||||
onPressBack = { this._onClose } />
|
||||
<SafeAreaView style = { styles.backdrop }>
|
||||
<SafeAreaView style = { _styles.backdrop }>
|
||||
<MessageContainer messages = { this.props._messages } />
|
||||
<MessageRecipient />
|
||||
<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));
|
||||
|
|
|
@ -4,16 +4,28 @@ 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 { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
import { replaceNonUnicodeEmojis } from '../../functions';
|
||||
|
||||
import AbstractChatMessage, { type Props } from '../AbstractChatMessage';
|
||||
import AbstractChatMessage, { type Props as AbstractProps } from '../AbstractChatMessage';
|
||||
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.
|
||||
*/
|
||||
|
@ -24,55 +36,58 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { message } = this.props;
|
||||
const localMessage = message.messageType === 'local';
|
||||
const { _styles, message } = this.props;
|
||||
const localMessage = message.messageType === MESSAGE_TYPE_LOCAL;
|
||||
const { privateMessage } = message;
|
||||
|
||||
// Style arrays that need to be updated in various scenarios, such as
|
||||
// error messages or others.
|
||||
const detailsWrapperStyle = [
|
||||
styles.detailsWrapper
|
||||
];
|
||||
const textWrapperStyle = [
|
||||
styles.textWrapper
|
||||
const messageBubbleStyle = [
|
||||
styles.messageBubble
|
||||
];
|
||||
|
||||
if (localMessage) {
|
||||
// This is a message sent by the local participant.
|
||||
|
||||
// The wrapper needs to be aligned to the right.
|
||||
detailsWrapperStyle.push(styles.ownMessageDetailsWrapper);
|
||||
|
||||
// The bubble needs to be differently styled.
|
||||
textWrapperStyle.push(styles.ownTextWrapper);
|
||||
} else if (message.messageType === 'error') {
|
||||
// The bubble needs to be differently styled.
|
||||
textWrapperStyle.push(styles.systemTextWrapper);
|
||||
// The bubble needs some additional styling
|
||||
messageBubbleStyle.push(_styles.localMessageBubble);
|
||||
} else if (message.messageType === MESSAGE_TYPE_ERROR) {
|
||||
// This is a system message.
|
||||
|
||||
// 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 (
|
||||
<View style = { styles.messageWrapper } >
|
||||
{ this._renderAvatar() }
|
||||
<View style = { detailsWrapperStyle }>
|
||||
<View style = { styles.replyWrapper }>
|
||||
<View style = { textWrapperStyle } >
|
||||
{
|
||||
this.props.showDisplayName
|
||||
&& this._renderDisplayName()
|
||||
}
|
||||
<View style = { messageBubbleStyle }>
|
||||
<View style = { styles.textWrapper } >
|
||||
{ this._renderDisplayName() }
|
||||
<Linkify linkStyle = { styles.chatLink }>
|
||||
{ replaceNonUnicodeEmojis(this._getMessageText()) }
|
||||
</Linkify>
|
||||
{
|
||||
message.privateMessage
|
||||
&& this._renderPrivateNotice()
|
||||
}
|
||||
{ this._renderPrivateNotice() }
|
||||
</View>
|
||||
{ message.privateMessage && !localMessage
|
||||
&& <PrivateMessageButton
|
||||
participantID = { message.id }
|
||||
reply = { true }
|
||||
showLabel = { false }
|
||||
toggledStyles = { styles.replyStyles } /> }
|
||||
{ this._renderPrivateReplyButton() }
|
||||
</View>
|
||||
{ this.props.showTimestamp && this._renderTimestamp() }
|
||||
{ this._renderTimestamp() }
|
||||
</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() {
|
||||
const { _styles, message, showDisplayName } = this.props;
|
||||
|
||||
if (!showDisplayName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style = { styles.displayName }>
|
||||
{ this.props.message.displayName }
|
||||
<Text style = { _styles.displayName }>
|
||||
{ message.displayName }
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the message privacy notice.
|
||||
* Renders the message privacy notice, if necessary.
|
||||
*
|
||||
* @returns {React$Element<*>}
|
||||
* @returns {React$Element<*> | null}
|
||||
*/
|
||||
_renderPrivateNotice() {
|
||||
const { _styles, message } = this.props;
|
||||
|
||||
if (!message.privateMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style = { styles.privateNotice }>
|
||||
<Text style = { _styles.privateNotice }>
|
||||
{ this._getPrivateNoticeMessage() }
|
||||
</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() {
|
||||
if (!this.props.showTimestamp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Text style = { styles.timeText }>
|
||||
{ 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 { FlatList } from 'react-native';
|
||||
|
||||
import { MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from '../../constants';
|
||||
|
||||
import ChatMessage from './ChatMessage';
|
||||
import styles from './styles';
|
||||
|
||||
|
@ -73,11 +75,11 @@ export default class ChatMessageGroup extends Component<Props> {
|
|||
<ChatMessage
|
||||
message = { message }
|
||||
showAvatar = {
|
||||
this.props.messages[0].messageType !== 'local'
|
||||
this.props.messages[0].messageType !== MESSAGE_TYPE_LOCAL
|
||||
&& index === this.props.messages.length - 1
|
||||
}
|
||||
showDisplayName = {
|
||||
this.props.messages[0].messageType === 'remote'
|
||||
this.props.messages[0].messageType === MESSAGE_TYPE_REMOTE
|
||||
&& index === this.props.messages.length - 1
|
||||
}
|
||||
showTimestamp = { index === 0 } />
|
||||
|
|
|
@ -3,28 +3,37 @@
|
|||
import React from 'react';
|
||||
import { Text, TouchableHighlight, View } from 'react-native';
|
||||
|
||||
import { ColorSchemeRegistry } from '../../../base/color-scheme';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconCancelSelection } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { type StyleType } from '../../../base/styles';
|
||||
|
||||
import AbstractMessageRecipient, {
|
||||
_mapDispatchToProps,
|
||||
_mapStateToProps
|
||||
_mapStateToProps as _abstractMapStateToProps,
|
||||
type Props as AbstractProps
|
||||
} 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 MessageRecipient extends AbstractMessageRecipient {
|
||||
class MessageRecipient extends AbstractMessageRecipient<Props> {
|
||||
/**
|
||||
* Implements {@code PureComponent#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _privateMessageRecipient } = this.props;
|
||||
const { _privateMessageRecipient, _styles } = this.props;
|
||||
|
||||
if (!_privateMessageRecipient) {
|
||||
return null;
|
||||
|
@ -33,8 +42,8 @@ class MessageRecipient extends AbstractMessageRecipient {
|
|||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<View style = { styles.messageRecipientContainer }>
|
||||
<Text style = { styles.messageRecipientText }>
|
||||
<View style = { _styles.messageRecipientContainer }>
|
||||
<Text style = { _styles.messageRecipientText }>
|
||||
{ t('chat.messageTo', {
|
||||
recipient: _privateMessageRecipient
|
||||
}) }
|
||||
|
@ -42,11 +51,24 @@ class MessageRecipient extends AbstractMessageRecipient {
|
|||
<TouchableHighlight onPress = { this.props._onRemovePrivateMessageRecipient }>
|
||||
<Icon
|
||||
src = { IconCancelSelection }
|
||||
style = { styles.messageRecipientCancelIcon } />
|
||||
style = { _styles.messageRecipientCancelIcon } />
|
||||
</TouchableHighlight>
|
||||
</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));
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
// @flow
|
||||
|
||||
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
|
||||
import { BoxModel, ColorPalette } from '../../../base/styles';
|
||||
|
||||
const BUBBLE_RADIUS = 8;
|
||||
|
||||
/**
|
||||
* The styles of the feature chat.
|
||||
*
|
||||
|
@ -20,14 +23,6 @@ export default {
|
|||
width: 32
|
||||
},
|
||||
|
||||
/**
|
||||
* Background of the chat screen.
|
||||
*/
|
||||
backdrop: {
|
||||
backgroundColor: ColorPalette.white,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
chatContainer: {
|
||||
alignItems: 'stretch',
|
||||
flex: 1,
|
||||
|
@ -47,14 +42,6 @@ export default {
|
|||
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).
|
||||
*/
|
||||
|
@ -76,35 +63,16 @@ export default {
|
|||
height: 48
|
||||
},
|
||||
|
||||
messageBubble: {
|
||||
alignItems: 'center',
|
||||
borderRadius: BUBBLE_RADIUS,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
messageContainer: {
|
||||
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.
|
||||
*/
|
||||
|
@ -123,34 +91,11 @@ export default {
|
|||
alignItems: 'flex-end'
|
||||
},
|
||||
|
||||
/**
|
||||
* Style modifier for the {@code textWrapper} for own messages.
|
||||
*/
|
||||
ownTextWrapper: {
|
||||
backgroundColor: 'rgb(210, 231, 249)',
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 0
|
||||
},
|
||||
|
||||
replyWrapper: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
replyStyles: {
|
||||
iconStyle: {
|
||||
color: 'rgb(118, 136, 152)',
|
||||
fontSize: 22,
|
||||
margin: BoxModel.margin / 2
|
||||
}
|
||||
},
|
||||
|
||||
privateNotice: {
|
||||
color: ColorPalette.warning,
|
||||
fontSize: 13,
|
||||
fontStyle: 'italic'
|
||||
},
|
||||
|
||||
sendButtonIcon: {
|
||||
color: ColorPalette.darkGrey,
|
||||
fontSize: 22
|
||||
|
@ -159,7 +104,7 @@ export default {
|
|||
/**
|
||||
* Style modifier for system (error) messages.
|
||||
*/
|
||||
systemTextWrapper: {
|
||||
systemMessageBubble: {
|
||||
backgroundColor: 'rgb(247, 215, 215)'
|
||||
},
|
||||
|
||||
|
@ -168,9 +113,6 @@ export default {
|
|||
*/
|
||||
textWrapper: {
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: 'rgb(240, 243, 247)',
|
||||
borderRadius: 8,
|
||||
borderTopLeftRadius: 0,
|
||||
flexDirection: 'column',
|
||||
padding: 9
|
||||
},
|
||||
|
@ -183,3 +125,73 @@ export default {
|
|||
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 { Linkify } from '../../../base/react';
|
||||
|
||||
import { MESSAGE_TYPE_LOCAL } from '../../constants';
|
||||
|
||||
import AbstractChatMessage, {
|
||||
type Props
|
||||
} from '../AbstractChatMessage';
|
||||
|
@ -47,7 +49,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
|
|||
</div>
|
||||
{ message.privateMessage && this._renderPrivateNotice() }
|
||||
</div>
|
||||
{ message.privateMessage && message.messageType !== 'local'
|
||||
{ message.privateMessage && message.messageType !== MESSAGE_TYPE_LOCAL
|
||||
&& <PrivateMessageButton
|
||||
participantID = { message.id }
|
||||
reply = { true }
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { MESSAGE_TYPE_REMOTE } from '../../constants';
|
||||
|
||||
import AbstractMessageContainer, { type Props }
|
||||
from '../AbstractMessageContainer';
|
||||
|
||||
|
@ -61,7 +63,7 @@ export default class MessageContainer extends AbstractMessageContainer {
|
|||
|
||||
return (
|
||||
<ChatMessageGroup
|
||||
className = { messageType || 'remote' }
|
||||
className = { messageType || MESSAGE_TYPE_REMOTE }
|
||||
key = { index }
|
||||
messages = { group } />
|
||||
);
|
||||
|
|
|
@ -8,13 +8,14 @@ import { connect } from '../../../base/redux';
|
|||
|
||||
import AbstractMessageRecipient, {
|
||||
_mapDispatchToProps,
|
||||
_mapStateToProps
|
||||
_mapStateToProps,
|
||||
type Props
|
||||
} from '../AbstractMessageRecipient';
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* The audio ID of the audio element for which the {@link playAudio} action is
|
||||
* triggered when new chat message is received.
|
||||
|
@ -5,3 +7,18 @@
|
|||
* @type {string}
|
||||
*/
|
||||
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 { addMessage, clearMessages, toggleChat } from './actions';
|
||||
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';
|
||||
|
||||
declare var APP: Object;
|
||||
|
@ -194,7 +194,7 @@ function _addChatMsgListener(conference, store) {
|
|||
function _handleChatError({ dispatch }, error) {
|
||||
dispatch(addMessage({
|
||||
hasRead: true,
|
||||
messageType: 'error',
|
||||
messageType: MESSAGE_TYPE_ERROR,
|
||||
message: error,
|
||||
privateMessage: false,
|
||||
timestamp: Date.now()
|
||||
|
@ -231,7 +231,7 @@ function _handleReceivedMessage({ dispatch, getState }, { id, message, nick, pri
|
|||
displayName,
|
||||
hasRead,
|
||||
id,
|
||||
messageType: participant.local ? 'local' : 'remote',
|
||||
messageType: participant.local ? MESSAGE_TYPE_LOCAL : MESSAGE_TYPE_REMOTE,
|
||||
message,
|
||||
privateMessage,
|
||||
recipient: getParticipantDisplayName(state, localParticipant.id),
|
||||
|
@ -285,7 +285,7 @@ function _persistSentPrivateMessage({ dispatch, getState }, recipientID, message
|
|||
displayName,
|
||||
hasRead: true,
|
||||
id: localParticipant.id,
|
||||
messageType: 'local',
|
||||
messageType: MESSAGE_TYPE_LOCAL,
|
||||
message,
|
||||
privateMessage: true,
|
||||
recipient: getParticipantDisplayName(getState, recipientID),
|
||||
|
@ -323,7 +323,7 @@ function _shouldSendPrivateMessageTo(state, action): ?string {
|
|||
const lastMessage = navigator.product === 'ReactNative'
|
||||
? 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
|
||||
// a message since then. Doesn't make sense to display the notice now.
|
||||
return undefined;
|
||||
|
@ -339,7 +339,7 @@ function _shouldSendPrivateMessageTo(state, action): ?string {
|
|||
const now = Date.now();
|
||||
const recentPrivateMessages = messages.filter(
|
||||
message =>
|
||||
message.messageType !== 'local'
|
||||
message.messageType !== MESSAGE_TYPE_LOCAL
|
||||
&& message.privateMessage
|
||||
&& message.timestamp + PRIVACY_NOTICE_TIMEOUT > now);
|
||||
const recentPrivateMessage = navigator.product === 'ReactNative'
|
||||
|
|
Loading…
Reference in New Issue