mirror of https://github.com/jitsi/jitsi-meet
Move some styles from SCSS to JSS Convert some files to TS Implement redesignpull/12823/head
parent
e3166e6faa
commit
8e1d96cc48
@ -1,21 +1,19 @@ |
|||||||
// @flow
|
|
||||||
|
|
||||||
import punycode from 'punycode'; |
import punycode from 'punycode'; |
||||||
import React, { Component } from 'react'; |
import React, { Component, ReactNode } from 'react'; |
||||||
import ReactLinkify from 'react-linkify'; |
import ReactLinkify from 'react-linkify'; |
||||||
|
|
||||||
type Props = { |
interface IProps { |
||||||
|
|
||||||
/** |
/** |
||||||
* The children of the component. |
* The children of the component. |
||||||
*/ |
*/ |
||||||
children: React$Node |
children: ReactNode; |
||||||
}; |
} |
||||||
|
|
||||||
/** |
/** |
||||||
* Implements a react wrapper for the react-linkify component. |
* Implements a react wrapper for the react-linkify component. |
||||||
*/ |
*/ |
||||||
export default class Linkify extends Component<Props> { |
export default class Linkify extends Component<IProps> { |
||||||
/** |
/** |
||||||
* Implements {@Component#render}. |
* Implements {@Component#render}. |
||||||
* |
* |
@ -1,131 +0,0 @@ |
|||||||
// @flow
|
|
||||||
|
|
||||||
import React from 'react'; |
|
||||||
|
|
||||||
import { translate } from '../../../base/i18n'; |
|
||||||
import Message from '../../../base/react/components/web/Message'; |
|
||||||
import { connect } from '../../../base/redux'; |
|
||||||
import { MESSAGE_TYPE_LOCAL } from '../../constants'; |
|
||||||
import AbstractChatMessage, { type Props } from '../AbstractChatMessage'; |
|
||||||
|
|
||||||
import PrivateMessageButton from './PrivateMessageButton'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Renders a single chat message. |
|
||||||
*/ |
|
||||||
class ChatMessage extends AbstractChatMessage<Props> { |
|
||||||
/** |
|
||||||
* Implements React's {@link Component#render()}. |
|
||||||
* |
|
||||||
* @inheritdoc |
|
||||||
* @returns {ReactElement} |
|
||||||
*/ |
|
||||||
render() { |
|
||||||
const { message, t, knocking } = this.props; |
|
||||||
|
|
||||||
return ( |
|
||||||
<div |
|
||||||
className = 'chatmessage-wrapper' |
|
||||||
id = { this.props.message.messageId } |
|
||||||
tabIndex = { -1 }> |
|
||||||
<div |
|
||||||
className = { `chatmessage ${message.privateMessage ? 'privatemessage' : ''} ${ |
|
||||||
message.lobbyChat && !knocking ? 'lobbymessage' : ''}` }>
|
|
||||||
<div className = 'replywrapper'> |
|
||||||
<div className = 'messagecontent'> |
|
||||||
{ this.props.showDisplayName && this._renderDisplayName() } |
|
||||||
<div className = 'usermessage'> |
|
||||||
<span className = 'sr-only'> |
|
||||||
{ this.props.message.displayName === this.props.message.recipient |
|
||||||
? t('chat.messageAccessibleTitleMe') |
|
||||||
: t('chat.messageAccessibleTitle', |
|
||||||
{ user: this.props.message.displayName }) } |
|
||||||
</span> |
|
||||||
<Message text = { this._getMessageText() } /> |
|
||||||
</div> |
|
||||||
{ (message.privateMessage || (message.lobbyChat && !knocking)) |
|
||||||
&& this._renderPrivateNotice() } |
|
||||||
</div> |
|
||||||
{ (message.privateMessage || (message.lobbyChat && !knocking)) |
|
||||||
&& message.messageType !== MESSAGE_TYPE_LOCAL |
|
||||||
&& ( |
|
||||||
<div |
|
||||||
className = { `messageactions ${ |
|
||||||
message.lobbyChat ? 'lobbychatmessageactions' : ''}` }>
|
|
||||||
<PrivateMessageButton |
|
||||||
isLobbyMessage = { message.lobbyChat } |
|
||||||
participantID = { message.id } |
|
||||||
reply = { true } |
|
||||||
showLabel = { false } /> |
|
||||||
</div> |
|
||||||
) } |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
{ this.props.showTimestamp && this._renderTimestamp() } |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
_getFormattedTimestamp: () => string; |
|
||||||
|
|
||||||
_getMessageText: () => string; |
|
||||||
|
|
||||||
_getPrivateNoticeMessage: () => string; |
|
||||||
|
|
||||||
/** |
|
||||||
* Renders the display name of the sender. |
|
||||||
* |
|
||||||
* @returns {React$Element<*>} |
|
||||||
*/ |
|
||||||
_renderDisplayName() { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
aria-hidden = { true } |
|
||||||
className = 'display-name'> |
|
||||||
{ this.props.message.displayName } |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Renders the message privacy notice. |
|
||||||
* |
|
||||||
* @returns {React$Element<*>} |
|
||||||
*/ |
|
||||||
_renderPrivateNotice() { |
|
||||||
return ( |
|
||||||
<div className = 'privatemessagenotice'> |
|
||||||
{ this._getPrivateNoticeMessage() } |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Renders the time at which the message was sent. |
|
||||||
* |
|
||||||
* @returns {React$Element<*>} |
|
||||||
*/ |
|
||||||
_renderTimestamp() { |
|
||||||
return ( |
|
||||||
<div className = 'timestamp'> |
|
||||||
{ this._getFormattedTimestamp() } |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Maps part of the Redux store to the props of this component. |
|
||||||
* |
|
||||||
* @param {Object} state - The Redux state. |
|
||||||
* @returns {Props} |
|
||||||
*/ |
|
||||||
function _mapStateToProps(state: Object): $Shape<Props> { |
|
||||||
const { knocking } = state['features/lobby']; |
|
||||||
|
|
||||||
return { |
|
||||||
knocking |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(ChatMessage)); |
|
@ -0,0 +1,223 @@ |
|||||||
|
import { Theme } from '@mui/material'; |
||||||
|
import { withStyles } from '@mui/styles'; |
||||||
|
import clsx from 'clsx'; |
||||||
|
import React from 'react'; |
||||||
|
import { connect } from 'react-redux'; |
||||||
|
|
||||||
|
import { IReduxState } from '../../../app/types'; |
||||||
|
import { translate } from '../../../base/i18n/functions'; |
||||||
|
import Message from '../../../base/react/components/web/Message'; |
||||||
|
import { withPixelLineHeight } from '../../../base/styles/functions.web'; |
||||||
|
import { MESSAGE_TYPE_LOCAL } from '../../constants'; |
||||||
|
import AbstractChatMessage, { IProps as AbstractProps } from '../AbstractChatMessage'; |
||||||
|
|
||||||
|
import PrivateMessageButton from './PrivateMessageButton'; |
||||||
|
|
||||||
|
interface IProps extends AbstractProps { |
||||||
|
|
||||||
|
classes: any; |
||||||
|
|
||||||
|
type: string; |
||||||
|
} |
||||||
|
|
||||||
|
const styles = (theme: Theme) => { |
||||||
|
return { |
||||||
|
chatMessageWrapper: { |
||||||
|
maxWidth: '100%' |
||||||
|
}, |
||||||
|
|
||||||
|
chatMessage: { |
||||||
|
display: 'inline-flex', |
||||||
|
padding: '12px', |
||||||
|
backgroundColor: theme.palette.ui02, |
||||||
|
borderRadius: '4px 12px 12px 12px', |
||||||
|
boxSizing: 'border-box' as const, |
||||||
|
maxWidth: '100%', |
||||||
|
marginTop: '4px', |
||||||
|
|
||||||
|
'&.privatemessage': { |
||||||
|
backgroundColor: theme.palette.support05 |
||||||
|
}, |
||||||
|
|
||||||
|
'&.local': { |
||||||
|
backgroundColor: theme.palette.ui04, |
||||||
|
borderRadius: '12px 4px 12px 12px', |
||||||
|
|
||||||
|
'&.privatemessage': { |
||||||
|
backgroundColor: theme.palette.support05 |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
'&.error': { |
||||||
|
backgroundColor: 'rgb(215, 121, 118)', |
||||||
|
borderRadius: 0, |
||||||
|
fontWeight: 100 |
||||||
|
}, |
||||||
|
|
||||||
|
'&.lobbymessage': { |
||||||
|
backgroundColor: theme.palette.support05 |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
replyWrapper: { |
||||||
|
display: 'flex', |
||||||
|
flexDirection: 'row' as const, |
||||||
|
alignItems: 'center' |
||||||
|
}, |
||||||
|
|
||||||
|
messageContent: { |
||||||
|
maxWidth: '100%', |
||||||
|
overflow: 'hidden', |
||||||
|
flex: 1 |
||||||
|
}, |
||||||
|
|
||||||
|
replyButtonContainer: { |
||||||
|
display: 'flex', |
||||||
|
alignItems: 'flex-start', |
||||||
|
height: '100%' |
||||||
|
}, |
||||||
|
|
||||||
|
replyButton: { |
||||||
|
padding: '2px' |
||||||
|
}, |
||||||
|
|
||||||
|
displayName: { |
||||||
|
...withPixelLineHeight(theme.typography.labelBold), |
||||||
|
color: theme.palette.text02, |
||||||
|
whiteSpace: 'nowrap', |
||||||
|
textOverflow: 'ellipsis', |
||||||
|
overflow: 'hidden', |
||||||
|
marginBottom: theme.spacing(1) |
||||||
|
}, |
||||||
|
|
||||||
|
userMessage: { |
||||||
|
...withPixelLineHeight(theme.typography.bodyShortRegular), |
||||||
|
color: theme.palette.text01, |
||||||
|
whiteSpace: 'pre-wrap' |
||||||
|
}, |
||||||
|
|
||||||
|
privateMessageNotice: { |
||||||
|
...withPixelLineHeight(theme.typography.labelRegular), |
||||||
|
color: theme.palette.text02, |
||||||
|
marginTop: theme.spacing(1) |
||||||
|
}, |
||||||
|
|
||||||
|
timestamp: { |
||||||
|
...withPixelLineHeight(theme.typography.labelRegular), |
||||||
|
color: theme.palette.text03, |
||||||
|
marginTop: theme.spacing(1) |
||||||
|
} |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders a single chat message. |
||||||
|
*/ |
||||||
|
class ChatMessage extends AbstractChatMessage<IProps> { |
||||||
|
/** |
||||||
|
* Implements React's {@link Component#render()}. |
||||||
|
* |
||||||
|
* @inheritdoc |
||||||
|
* @returns {ReactElement} |
||||||
|
*/ |
||||||
|
render() { |
||||||
|
const { message, t, knocking, classes, type } = this.props; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div |
||||||
|
className = { classes.chatMessageWrapper } |
||||||
|
id = { this.props.message.messageId } |
||||||
|
tabIndex = { -1 }> |
||||||
|
<div |
||||||
|
className = { clsx('chatmessage', classes.chatMessage, type, |
||||||
|
message.privateMessage && 'privatemessage', |
||||||
|
message.lobbyChat && !knocking && 'lobbymessage') }> |
||||||
|
<div className = { classes.replyWrapper }> |
||||||
|
<div className = { clsx('messagecontent', classes.messageContent) }> |
||||||
|
{ this.props.showDisplayName && this._renderDisplayName() } |
||||||
|
<div className = { clsx('usermessage', classes.userMessage) }> |
||||||
|
<span className = 'sr-only'> |
||||||
|
{ this.props.message.displayName === this.props.message.recipient |
||||||
|
? t('chat.messageAccessibleTitleMe') |
||||||
|
: t('chat.messageAccessibleTitle', |
||||||
|
{ user: this.props.message.displayName }) } |
||||||
|
</span> |
||||||
|
<Message text = { this._getMessageText() } /> |
||||||
|
</div> |
||||||
|
{ (message.privateMessage || (message.lobbyChat && !knocking)) |
||||||
|
&& this._renderPrivateNotice() } |
||||||
|
</div> |
||||||
|
{ (message.privateMessage || (message.lobbyChat && !knocking)) |
||||||
|
&& message.messageType !== MESSAGE_TYPE_LOCAL |
||||||
|
&& ( |
||||||
|
<div |
||||||
|
className = { classes.replyButtonContainer }> |
||||||
|
<PrivateMessageButton |
||||||
|
isLobbyMessage = { message.lobbyChat } |
||||||
|
participantID = { message.id } /> |
||||||
|
</div> |
||||||
|
) } |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{ this.props.showTimestamp && this._renderTimestamp() } |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the display name of the sender. |
||||||
|
* |
||||||
|
* @returns {React$Element<*>} |
||||||
|
*/ |
||||||
|
_renderDisplayName() { |
||||||
|
return ( |
||||||
|
<div |
||||||
|
aria-hidden = { true } |
||||||
|
className = { clsx('display-name', this.props.classes.displayName) }> |
||||||
|
{ this.props.message.displayName } |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the message privacy notice. |
||||||
|
* |
||||||
|
* @returns {React$Element<*>} |
||||||
|
*/ |
||||||
|
_renderPrivateNotice() { |
||||||
|
return ( |
||||||
|
<div className = { this.props.classes.privateMessageNotice }> |
||||||
|
{ this._getPrivateNoticeMessage() } |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the time at which the message was sent. |
||||||
|
* |
||||||
|
* @returns {React$Element<*>} |
||||||
|
*/ |
||||||
|
_renderTimestamp() { |
||||||
|
return ( |
||||||
|
<div className = { clsx('timestamp', this.props.classes.timestamp) }> |
||||||
|
{ this._getFormattedTimestamp() } |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Maps part of the Redux store to the props of this component. |
||||||
|
* |
||||||
|
* @param {Object} state - The Redux state. |
||||||
|
* @returns {IProps} |
||||||
|
*/ |
||||||
|
function _mapStateToProps(state: IReduxState) { |
||||||
|
const { knocking } = state['features/lobby']; |
||||||
|
|
||||||
|
return { |
||||||
|
knocking |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(withStyles(styles)(ChatMessage))); |
@ -1,59 +0,0 @@ |
|||||||
// @flow
|
|
||||||
|
|
||||||
import React, { Component } from 'react'; |
|
||||||
|
|
||||||
import ChatMessage from './ChatMessage'; |
|
||||||
|
|
||||||
type Props = { |
|
||||||
|
|
||||||
/** |
|
||||||
* Additional CSS classes to apply to the root element. |
|
||||||
*/ |
|
||||||
className: string, |
|
||||||
|
|
||||||
/** |
|
||||||
* The messages to display as a group. |
|
||||||
*/ |
|
||||||
messages: Array<Object>, |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* Displays a list of chat messages. Will show only the display name for the |
|
||||||
* first chat message and the timestamp for the last chat message. |
|
||||||
* |
|
||||||
* @augments React.Component |
|
||||||
*/ |
|
||||||
class ChatMessageGroup extends Component<Props> { |
|
||||||
static defaultProps = { |
|
||||||
className: '' |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* Implements React's {@link Component#render()}. |
|
||||||
* |
|
||||||
* @inheritdoc |
|
||||||
*/ |
|
||||||
render() { |
|
||||||
const { className, messages } = this.props; |
|
||||||
|
|
||||||
const messagesLength = messages.length; |
|
||||||
|
|
||||||
if (!messagesLength) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className = { `chat-message-group ${className}` }> |
|
||||||
{ messages.map((message, i) => ( |
|
||||||
<ChatMessage |
|
||||||
key = { i } |
|
||||||
message = { message } |
|
||||||
showDisplayName = { i === 0 } |
|
||||||
showTimestamp = { i === messages.length - 1 } /> |
|
||||||
))} |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default ChatMessageGroup; |
|
@ -0,0 +1,81 @@ |
|||||||
|
import clsx from 'clsx'; |
||||||
|
import React from 'react'; |
||||||
|
import { makeStyles } from 'tss-react/mui'; |
||||||
|
|
||||||
|
// eslint-disable-next-line lines-around-comment
|
||||||
|
// @ts-ignore
|
||||||
|
import Avatar from '../../../base/avatar/components/Avatar'; |
||||||
|
import { IMessage } from '../../reducer'; |
||||||
|
|
||||||
|
import ChatMessage from './ChatMessage'; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
|
||||||
|
/** |
||||||
|
* Additional CSS classes to apply to the root element. |
||||||
|
*/ |
||||||
|
className: string; |
||||||
|
|
||||||
|
/** |
||||||
|
* The messages to display as a group. |
||||||
|
*/ |
||||||
|
messages: Array<IMessage>; |
||||||
|
} |
||||||
|
|
||||||
|
const useStyles = makeStyles()(theme => { |
||||||
|
return { |
||||||
|
messageGroup: { |
||||||
|
display: 'flex', |
||||||
|
flexDirection: 'column' |
||||||
|
}, |
||||||
|
|
||||||
|
groupContainer: { |
||||||
|
display: 'flex', |
||||||
|
|
||||||
|
'&.local': { |
||||||
|
justifyContent: 'flex-end', |
||||||
|
|
||||||
|
'& .avatar': { |
||||||
|
display: 'none' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
avatar: { |
||||||
|
margin: `${theme.spacing(1)} ${theme.spacing(2)} ${theme.spacing(3)} 0`, |
||||||
|
position: 'sticky', |
||||||
|
top: 0 |
||||||
|
} |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
const ChatMessageGroup = ({ className = '', messages }: IProps) => { |
||||||
|
const { classes } = useStyles(); |
||||||
|
const messagesLength = messages.length; |
||||||
|
|
||||||
|
if (!messagesLength) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className = { clsx(classes.groupContainer, className) }> |
||||||
|
<Avatar |
||||||
|
className = { clsx(classes.avatar, 'avatar') } |
||||||
|
participantId = { messages[0].id } |
||||||
|
size = { 32 } /> |
||||||
|
<div className = { `${classes.messageGroup} chat-message-group ${className}` }> |
||||||
|
{messages.map((message, i) => ( |
||||||
|
<ChatMessage |
||||||
|
key = { i } |
||||||
|
message = { message } |
||||||
|
showDisplayName = { i === 0 } |
||||||
|
showTimestamp = { i === messages.length - 1 } |
||||||
|
type = { className } /> |
||||||
|
))} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default ChatMessageGroup; |
@ -1,99 +0,0 @@ |
|||||||
// @flow
|
|
||||||
|
|
||||||
import { CHAT_ENABLED, getFeatureFlag } from '../../../base/flags'; |
|
||||||
import { translate } from '../../../base/i18n'; |
|
||||||
import { IconMessage, IconReply } from '../../../base/icons'; |
|
||||||
import { getParticipantById } from '../../../base/participants'; |
|
||||||
import { connect } from '../../../base/redux'; |
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components'; |
|
||||||
import { handleLobbyChatInitialized, openChat } from '../../actions'; |
|
||||||
|
|
||||||
export type Props = AbstractButtonProps & { |
|
||||||
|
|
||||||
/** |
|
||||||
* True if the message is a lobby chat message. |
|
||||||
*/ |
|
||||||
isLobbyMessage: boolean, |
|
||||||
|
|
||||||
/** |
|
||||||
* The ID of the participant that the message is to be sent. |
|
||||||
*/ |
|
||||||
participantID: string, |
|
||||||
|
|
||||||
/** |
|
||||||
* True if the button is rendered as a reply button. |
|
||||||
*/ |
|
||||||
reply: boolean, |
|
||||||
|
|
||||||
/** |
|
||||||
* Function to be used to translate i18n labels. |
|
||||||
*/ |
|
||||||
t: Function, |
|
||||||
|
|
||||||
/** |
|
||||||
* The Redux dispatch function. |
|
||||||
*/ |
|
||||||
dispatch: Function, |
|
||||||
|
|
||||||
/** |
|
||||||
* The participant object retrieved from Redux. |
|
||||||
*/ |
|
||||||
_participant: Object, |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* Class to render a button that initiates the sending of a private message through chet. |
|
||||||
*/ |
|
||||||
class PrivateMessageButton extends AbstractButton<Props, any> { |
|
||||||
accessibilityLabel = 'toolbar.accessibilityLabel.privateMessage'; |
|
||||||
icon = IconMessage; |
|
||||||
label = 'toolbar.privateMessage'; |
|
||||||
toggledIcon = IconReply; |
|
||||||
|
|
||||||
/** |
|
||||||
* Handles clicking / pressing the button, and kicks the participant. |
|
||||||
* |
|
||||||
* @private |
|
||||||
* @returns {void} |
|
||||||
*/ |
|
||||||
_handleClick() { |
|
||||||
const { _participant, participantID, dispatch, isLobbyMessage } = this.props; |
|
||||||
|
|
||||||
if (isLobbyMessage) { |
|
||||||
dispatch(handleLobbyChatInitialized(participantID)); |
|
||||||
} else { |
|
||||||
dispatch(openChat(_participant)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Helper function to be implemented by subclasses, which must return a |
|
||||||
* {@code boolean} value indicating if this button is toggled or not. |
|
||||||
* |
|
||||||
* @protected |
|
||||||
* @returns {boolean} |
|
||||||
*/ |
|
||||||
_isToggled() { |
|
||||||
return this.props.reply; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Maps part of the Redux store to the props of this component. |
|
||||||
* |
|
||||||
* @param {Object} state - The Redux state. |
|
||||||
* @param {Props} ownProps - The own props of the component. |
|
||||||
* @returns {Props} |
|
||||||
*/ |
|
||||||
export function _mapStateToProps(state: Object, ownProps: Props): $Shape<Props> { |
|
||||||
const enabled = getFeatureFlag(state, CHAT_ENABLED, true); |
|
||||||
const { visible = enabled } = ownProps; |
|
||||||
|
|
||||||
return { |
|
||||||
_participant: getParticipantById(state, ownProps.participantID), |
|
||||||
visible |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(PrivateMessageButton)); |
|
@ -0,0 +1,72 @@ |
|||||||
|
import React, { useCallback } from 'react'; |
||||||
|
import { useDispatch, useSelector } from 'react-redux'; |
||||||
|
import { makeStyles } from 'tss-react/mui'; |
||||||
|
|
||||||
|
import { IReduxState } from '../../../app/types'; |
||||||
|
import { CHAT_ENABLED } from '../../../base/flags/constants'; |
||||||
|
import { getFeatureFlag } from '../../../base/flags/functions'; |
||||||
|
import { IconReply } from '../../../base/icons/svg'; |
||||||
|
import { getParticipantById } from '../../../base/participants/functions'; |
||||||
|
import Button from '../../../base/ui/components/web/Button'; |
||||||
|
import { BUTTON_TYPES } from '../../../base/ui/constants.any'; |
||||||
|
import { handleLobbyChatInitialized, openChat } from '../../actions.web'; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
|
||||||
|
/** |
||||||
|
* True if the message is a lobby chat message. |
||||||
|
*/ |
||||||
|
isLobbyMessage: boolean; |
||||||
|
|
||||||
|
/** |
||||||
|
* The ID of the participant that the message is to be sent. |
||||||
|
*/ |
||||||
|
participantID: string; |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether the button should be visible or not. |
||||||
|
*/ |
||||||
|
visible?: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
const useStyles = makeStyles()(theme => { |
||||||
|
return { |
||||||
|
replyButton: { |
||||||
|
padding: '2px', |
||||||
|
|
||||||
|
'&:hover': { |
||||||
|
backgroundColor: theme.palette.action03 |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
const PrivateMessageButton = ({ participantID, isLobbyMessage, visible }: IProps) => { |
||||||
|
const { classes } = useStyles(); |
||||||
|
const dispatch = useDispatch(); |
||||||
|
const participant = useSelector((state: IReduxState) => getParticipantById(state, participantID)); |
||||||
|
const isVisible = useSelector((state: IReduxState) => getFeatureFlag(state, CHAT_ENABLED, true)) ?? visible; |
||||||
|
|
||||||
|
const handleClick = useCallback(() => { |
||||||
|
if (isLobbyMessage) { |
||||||
|
dispatch(handleLobbyChatInitialized(participantID)); |
||||||
|
} else { |
||||||
|
dispatch(openChat(participant)); |
||||||
|
} |
||||||
|
}, []); |
||||||
|
|
||||||
|
if (!isVisible) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<Button |
||||||
|
accessibilityLabel = 'toolbar.accessibilityLabel.privateMessage' |
||||||
|
className = { classes.replyButton } |
||||||
|
icon = { IconReply } |
||||||
|
onClick = { handleClick } |
||||||
|
type = { BUTTON_TYPES.TERTIARY } /> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default PrivateMessageButton; |
Loading…
Reference in new issue