2017-06-08_509bba0/509bba0_unpacked_with_node_modules/discord_app/components/MessageGroup.js
2022-07-26 10:06:20 -07:00

939 lines
28 KiB
JavaScript
Executable file

import React from 'react';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import ChannelTextArea from './ChannelTextArea';
import {copy, SUPPORTS_COPY} from '../utils/ClipboardUtils';
import {toReactionEmoji} from '../utils/ReactionUtils';
import Popout from './common/Popout';
import Avatar from './common/Avatar';
import BotTag from './common/BotTag';
import Attachment from './Attachment';
import Embed from './Embed';
import InviteButton from './InviteButton';
import Tooltip from './common/Tooltip';
import SystemMessage from './SystemMessage';
import MessageReactions from './MessageReactions';
import EditMessageStore from '../stores/EditMessageStore';
import ReadStateActionCreators from '../actions/ReadStateActionCreators';
import MessageActionCreators from '../actions/MessageActionCreators';
import ChannelPinActionCreators from '../actions/ChannelPinActionCreators';
import {addReaction} from '../actions/ReactionActionCreators';
import ChatRestrictionMixin from '../mixins/web/ChatRestrictionMixin';
import MessageUtils from '../utils/MessageUtils';
import ReportUtils from '../utils/ReportUtils';
import UserStore from '../stores/UserStore';
import i18n from '../i18n';
import NativeUtils from '../utils/NativeUtils';
import ContextMenu from './common/ContextMenu';
import UserContextMenu from './contextmenus/UserContextMenu';
import MessageContextMenu from './contextmenus/MessageContextMenu';
import MessageAlerts from './MessageAlerts';
import PermissionMixin from '../mixins/PermissionMixin';
import Flux from '../lib/flux';
import shallowEqual from '../../discord_common/js/lib/shallowEqual';
import BackgroundFlash from './common/animated/BackgroundFlash';
import ModalActionCreators from '../actions/ModalActionCreators';
import MessageReactionsModal from './MessageReactionsModal';
import ReportModal from './ReportModal';
import {hasAnimatedAvatar} from '../utils/AvatarUtils';
import {EmojiPicker} from './EmojiPicker';
import {
MessageTypesDeletable,
TextareaTypes,
MessageStates,
ContextMenuTypes,
MessageTypes,
ComponentActions,
} from '../Constants';
import UserPopout from './UserPopout';
import '../styles/confirm_message_modal.styl';
const EDIT_MESSAGE_REF = 'EDIT_MESSAGE_REF';
const OptionPopout = React.createClass({
mixins: [PureRenderMixin, PermissionMixin],
propTypes: {
channel: React.PropTypes.object.isRequired,
message: React.PropTypes.object.isRequired,
canPin: React.PropTypes.bool,
canEdit: React.PropTypes.bool,
developerMode: React.PropTypes.bool,
canDelete: React.PropTypes.bool,
},
handleCopyId() {
copy(this.props.message.id);
this.props.onClose();
},
handleEdit() {
MessageActionCreators.startEditMessage(this.props.channel.id, this.props.message.id, this.props.message.content);
},
handleRetry() {
const {channel, message} = this.props;
MessageActionCreators.retrySendMessage(channel, message.id, message.content, message.tts);
},
handleDelete() {
if (this.props.message.state === MessageStates.SEND_FAILED) {
MessageActionCreators.deleteMessage(this.props.channel.id, this.props.message.id, true);
} else {
MessageAlerts.confirmDelete(this.props.channel, this.props.message);
}
},
handleReactions() {
ModalActionCreators.push(MessageReactionsModal, {message: this.props.message});
},
handlePin() {
MessageAlerts.confirmPin(this.props.channel, this.props.message);
this.props.onClose();
},
handleReport() {
ModalActionCreators.push(ReportModal, {
message: this.props.message,
});
this.props.onClose();
},
handleUnpin(e) {
if (e.shiftKey) {
ChannelPinActionCreators.unpinMessage(this.props.channel, this.props.message.id);
} else {
MessageAlerts.confirmUnpin(this.props.channel, this.props.message);
}
this.props.onClose();
},
render() {
const {message} = this.props;
let retryButton;
let editButton;
let pinButton;
let copyIdButton;
let deleteButton;
let reactionsButton;
let reportButton;
if (message.state === MessageStates.SEND_FAILED) {
retryButton = <div className="btn-item" onClick={this.handleRetry}>{i18n.Messages.RETRY}</div>;
} else {
const currentUser = UserStore.getCurrentUser();
if (this.props.canEdit && message.author.id === currentUser.id) {
editButton = <div className="btn-item" onClick={this.handleEdit}>{i18n.Messages.EDIT}</div>;
}
if (this.props.canPin) {
if (message.pinned === false) {
pinButton = <div className="btn-item" onClick={this.handlePin}>{i18n.Messages.PIN}</div>;
} else {
pinButton = <div className="btn-item" onClick={this.handleUnpin}>{i18n.Messages.UNPIN}</div>;
}
}
}
if (SUPPORTS_COPY && this.props.developerMode) {
copyIdButton = <div className="btn-item" onClick={this.handleCopyId}>{i18n.Messages.COPY_ID}</div>;
}
if (this.props.canDelete) {
deleteButton = (
<div className="btn-item" onClick={this.handleDelete}>
{i18n.Messages.DELETE}
</div>
);
}
if (message.reactions.length) {
reactionsButton = (
<div className="btn-item" onClick={this.handleReactions}>
{i18n.Messages.REACTIONS}
</div>
);
}
if (this.props.canReport) {
reportButton = (
<div className="btn-item" onClick={this.handleReport}>
{i18n.Messages.REPORT}
</div>
);
}
return (
<div className="option-popout small-popout-box">
{pinButton}
{editButton}
{retryButton}
{copyIdButton}
{deleteButton}
{reactionsButton}
{reportButton}
</div>
);
},
});
const Message = React.createClass({
mixins: [Flux.StoreListenerMixin(EditMessageStore), PureRenderMixin, ChatRestrictionMixin],
propTypes: {
first: React.PropTypes.bool.isRequired,
compact: React.PropTypes.bool.isRequired,
canDelete: React.PropTypes.bool,
canPin: React.PropTypes.bool,
canAddReactions: React.PropTypes.bool,
canAddNewReactions: React.PropTypes.bool,
canFlash: React.PropTypes.bool,
canReport: React.PropTypes.bool,
channel: React.PropTypes.object.isRequired,
message: React.PropTypes.object.isRequired,
onUpdate: React.PropTypes.func,
onEdit: React.PropTypes.func,
onToggleEditing: React.PropTypes.func,
inlineAttachmentMedia: React.PropTypes.bool.isRequired,
inlineEmbedMedia: React.PropTypes.bool.isRequired,
renderEmbeds: React.PropTypes.bool.isRequired,
renderReactions: React.PropTypes.bool.isRequired,
avatarSize: React.PropTypes.oneOf(Avatar.SIZES),
popoutPosition: React.PropTypes.oneOf(Popout.POSITIONS),
option: React.PropTypes.bool,
canEdit: React.PropTypes.bool,
jumpSequenceId: React.PropTypes.number,
developerMode: React.PropTypes.bool,
renderMentioned: React.PropTypes.bool,
},
getStateFromStores() {
return {
isEditing: this.props.canEdit && EditMessageStore.isEditing(this.props.channel.id, this.props.message.id),
};
},
componentWillMount() {
this._mounted = false;
},
componentDidMount() {
this._mounted = true;
},
componentDidUpdate(prevProps, prevState) {
const {message} = this.props;
if (message.embeds !== prevProps.message.embeds || message.reactions !== prevProps.message.reactions) {
this.props.onUpdate && this.props.onUpdate(ReactDOM.findDOMNode(this));
}
if (this.state.isEditing !== prevState.isEditing) {
this.props.onToggleEditing && this.props.onToggleEditing(this.state.isEditing);
if (this.state.isEditing) {
this.props.onEdit && this.props.onEdit(ReactDOM.findDOMNode(this));
}
}
},
handleCancelEditing() {
MessageActionCreators.endEditMessage();
},
handleSaveClick() {
this.handleSaveEditing(ReactDOM.findDOMNode(this.refs[EDIT_MESSAGE_REF].refs['textarea']).value);
},
handleSaveEditing(value) {
if (value.length == 0) {
MessageAlerts.confirmDelete(this.props.channel, this.props.message);
} else if (this.applyChatRestrictions(value)) {
// don't cancel editing just bail
return;
} else if (value !== EditMessageStore.getEditingContent()) {
const result = MessageUtils.parse(this.props.channel, value, true);
MessageActionCreators.editMessage(this.props.channel.id, this.props.message.id, result);
}
this.handleCancelEditing();
},
handleKeyDown(e) {
if (e.which === 27 /* ESCAPE */ && !e.shiftKey) {
e.preventDefault();
this.handleCancelEditing();
}
},
handleTextareaResize() {
this.props.onEdit && this.props.onEdit(ReactDOM.findDOMNode(this));
},
handleManualAck(e) {
if (e.altKey) {
e.preventDefault();
ReadStateActionCreators.manualAck(this.props.channel.id, this.props.message.id);
}
},
renderOptionPopout(props) {
return (
<OptionPopout
{...props}
channel={this.props.channel}
message={this.props.message}
developerMode={this.props.developerMode}
canEdit={this.props.canEdit}
canPin={this.props.canPin}
canDelete={this.props.canDelete}
canReport={this.props.canReport}
/>
);
},
renderUserPopout(props) {
const {channel, message} = this.props;
return <UserPopout {...props} user={message.author} guildId={channel.getGuildId()} channelId={channel.id} />;
},
renderUsername(message) {
const style = message.colorString ? {color: message.colorString} : null;
if (this.props.compact) {
return (
<Popout closeOnScroll={false} render={this.renderUserPopout} position={this.props.popoutPosition}>
<span className="username-wrapper" onContextMenu={this.props.onContextMenu}>
{message.author.bot ? <BotTag /> : null}
<strong className="user-name" style={style}>
{message.nick || message.author.toString()}
</strong>
<i className="highlight-separator right-pad">: </i>
</span>
</Popout>
);
} else {
return (
<Popout closeOnScroll={false} render={this.renderUserPopout} position={this.props.popoutPosition}>
<span className="username-wrapper" onContextMenu={this.props.onContextMenu}>
<strong className="user-name" style={style}>
{message.nick || message.author.toString()}
</strong>
{message.author.bot ? <BotTag /> : null}
</span>
</Popout>
);
}
},
renderAttachments(message) {
if (message.attachments.length) {
return message.attachments.map(attachment => {
return (
<Attachment
key={attachment.id}
attachment={attachment}
inlineMedia={this.props.inlineAttachmentMedia}
onContextMenu={e => this.handleMessageContextMenu(e, {attachment})}
/>
);
});
}
return null;
},
renderEmbeds(message) {
const {renderEmbeds, inlineEmbedMedia, channel} = this.props;
if (message.embeds.length && renderEmbeds) {
return message.embeds.map((embed, i) =>
<Embed key={i} {...embed} message={message} inlineMedia={inlineEmbedMedia} channelId={channel.id} />
);
}
return null;
},
renderOptionButton(message, isEditing, isSending) {
if (this.props.option === false) {
return null;
}
// We cannot actually modify local bot messages, so skip rendering the button
if (message.author.isLocalBot()) return;
const currentUser = UserStore.getCurrentUser();
const showOptionsPopout =
!isEditing &&
!isSending &&
(message.author.id === currentUser.id ||
this.props.canDelete ||
this.props.canPin ||
this.props.canReport ||
(SUPPORTS_COPY && this.props.developerMode));
if (showOptionsPopout) {
return (
<Popout closeOnScroll={false} render={this.renderOptionPopout} position={Popout.BOTTOM} offsetY={-6}>
<div className="btn-option" />
</Popout>
);
}
return null;
},
renderReactionPopout(props) {
const {channel} = this.props;
return <EmojiPicker {...props} channel={channel} onSelectEmoji={this.handleAddReaction} />;
},
handleAddReaction(emoji) {
// Emoji equaling null means that the emoji picker was closed, and we shouldn't do anything.
if (emoji == null) return;
const {channel, message} = this.props;
addReaction(channel.id, message.id, toReactionEmoji(emoji));
},
renderReactionButton(message, left = true) {
if (
this.props.option === false ||
this.state.isEditing ||
message.state === MessageStates.SENDING ||
!this.props.canAddNewReactions ||
!this.props.renderReactions ||
message.author.isLocalBot()
)
return;
return (
<Popout
closeOnScroll={false}
render={this.renderReactionPopout}
position={left ? Popout.LEFT : Popout.RIGHT}
offsetX={left ? -10 : 0}
tooltip={i18n.Messages.ADD_REACTION}
subscribeTo={left ? ComponentActions.TOGGLE_REACTION_POPOUT(message.id) : null}>
<div className="btn-reaction" />
</Popout>
);
},
renderInvites(message) {
if (message.invites.length) {
return message.invites.map(code => <InviteButton key={code} code={code} />);
}
return null;
},
renderReactions(message) {
if (this.props.renderReactions && message.reactions.length) {
return (
<MessageReactions message={message} transitionAppear={this._mounted} readOnly={!this.props.canAddReactions}>
{this.renderReactionButton(message, false)}
</MessageReactions>
);
}
return null;
},
renderContent(message) {
let edited;
if (message.isEdited()) {
edited = (
<Tooltip text={message.editedTimestamp.calendar()}>
<span className="edited">({i18n.Messages.MESSAGE_EDITED})</span>
</Tooltip>
);
}
if (this.props.compact) {
return (
<div className="markup">
{this.renderTimestamp(message)}
{this.renderUsername(message)}
<span className="message-content">
{message.contentParsed}
{edited}
</span>
</div>
);
}
return (
<div className="markup">
{message.contentParsed}
{edited}
</div>
);
},
renderTimestamp(message) {
if (this.props.compact) {
return (
<span className="timestamp">
<i className="highlight-separator left-pad">[</i>
{message.timestamp.format('LT')}
<i className="highlight-separator right-pad">] </i>
</span>
);
}
return <span className="timestamp">{message.timestamp.calendar()}</span>;
},
renderAccessories(message) {
return (
<div className="accessory">
{this.renderInvites(message)}
{this.renderAttachments(message)}
{this.renderEmbeds(message)}
{this.renderReactions(message)}
</div>
);
},
renderHeader(message, isEditing) {
if (!this.props.compact && (this.props.first || isEditing)) {
return (
<h2>
{this.renderUsername(message)}
<span className="highlight-separator"> - </span>
{this.renderTimestamp(message)}
</h2>
);
}
return null;
},
handleSystemMessageContextMenu(event) {
if (!NativeUtils.embedded) return;
const {target} = event;
ContextMenu.openContextMenu(event, props =>
<MessageContextMenu
{...props}
type={ContextMenuTypes.MESSAGE_SYSTEM}
message={this.props.message}
channel={this.props.channel}
target={target}
/>
);
},
handleMessageContextMenu(event, extraProps) {
if (!NativeUtils.embedded) return;
// event is of type SyntheticEvent and is pooled, so we can't use event.target later in the callback
// see https://fb.me/react-event-pooling for more info.
const {target} = event;
ContextMenu.openContextMenu(event, props =>
<MessageContextMenu
{...props}
{...extraProps}
type={ContextMenuTypes.MESSAGE_MAIN}
message={this.props.message}
channel={this.props.channel}
target={target}
/>
);
},
render() {
const {message} = this.props;
const isEditing = this.state.isEditing;
const isSending = message.state === MessageStates.SENDING;
const header = this.renderHeader(message, isEditing);
const option = this.renderOptionButton(message, isEditing, isSending);
const reactionButton = this.renderReactionButton(message);
if (message.type > MessageTypes.DEFAULT) {
return (
<div
className={classNames('message', classes)}
onContextMenu={MessageTypesDeletable[message.type] && this.handleSystemMessageContextMenu}>
<SystemMessage message={message} option={option} />
</div>
);
}
let classes;
if (isEditing) {
const defaultValue = EditMessageStore.getEditingContent();
const editTextarea = (
<ChannelTextArea
ref={EDIT_MESSAGE_REF}
key={message.id}
channel={this.props.channel}
type={TextareaTypes.EDIT}
onSubmit={this.handleSaveEditing}
onKeyDown={this.handleKeyDown}
onResize={this.handleTextareaResize}
defaultValue={defaultValue}
/>
);
const editOperation = (
<div className="edit-operation">
{i18n.Messages.EDIT_TEXTAREA_HELP.format({
onCancel: this.handleCancelEditing,
onSave: this.handleSaveClick,
})}
</div>
);
classes = {
'edit-message': true,
'edit-first-message': this.props.first,
};
if (this.props.compact) {
return (
<div>
<div className={classNames(classes)}>
<div className="edit-container-outer">
{this.renderTimestamp(message)}
<div className="edit-container-inner">
{editTextarea}
{editOperation}
</div>
</div>
</div>
{this.renderAccessories(message)}
</div>
);
} else {
return (
<div>
<div className={classNames(classes)}>
<div className="edit-container-outer">
<Avatar user={message.author} size={this.props.avatarSize} animate />
<div className="edit-container-inner">
{header}
{editTextarea}
{editOperation}
</div>
</div>
</div>
{this.renderAccessories(message)}
</div>
);
}
}
const content = this.renderContent(message);
classes = {
'message-sending': isSending,
'message-send-failed': message.state === MessageStates.SEND_FAILED,
mentioned: this.props.renderMentioned && message.mentioned,
first: this.props.first,
};
let body = null;
if (this.props.compact) {
body = (
<div className={classNames('message', classes)} onClick={this.handleManualAck}>
<div className="message-text" onContextMenu={this.handleMessageContextMenu}>
{option}
{reactionButton}
{content}
</div>
{this.renderAccessories(message)}
</div>
);
} else {
body = (
<div className={classNames('message', classes)} onClick={this.handleManualAck}>
<div className="body">
{header}
<div className="message-text" onContextMenu={this.handleMessageContextMenu}>
{option}
{reactionButton}
{content}
</div>
</div>
{this.renderAccessories(message)}
</div>
);
}
if (this.props.jumpSequenceId && this.props.canFlash) {
return <BackgroundFlash sequenceId={this.props.jumpSequenceId}>{body}</BackgroundFlash>;
}
return body;
},
});
const MessageGroup = React.createClass({
propTypes: {
hasDivider: React.PropTypes.bool,
canManageMessages: React.PropTypes.bool,
channel: React.PropTypes.object.isRequired,
messages: React.PropTypes.array.isRequired,
onUpdate: React.PropTypes.func,
onEdit: React.PropTypes.func,
inlineAttachmentMedia: React.PropTypes.bool,
inlineEmbedMedia: React.PropTypes.bool,
renderEmbeds: React.PropTypes.bool,
renderReactions: React.PropTypes.bool,
avatarSize: React.PropTypes.oneOf(Avatar.SIZES),
popoutPosition: React.PropTypes.oneOf(Popout.POSITIONS),
compact: React.PropTypes.bool,
groupOption: React.PropTypes.func,
messageOption: React.PropTypes.bool,
jumpTargetIndex: React.PropTypes.number,
jumpSequenceId: React.PropTypes.number,
canFlash: React.PropTypes.bool,
enableInteraction: React.PropTypes.bool,
developerMode: React.PropTypes.bool,
renderMentioned: React.PropTypes.bool,
canAddReactions: React.PropTypes.bool,
canChat: React.PropTypes.bool,
canReport: React.PropTypes.bool,
},
getDefaultProps() {
return {
canManageMessages: false,
hasDivider: false,
popoutPosition: Popout.RIGHT,
avatarSize: 'large',
compact: false,
messageOption: true,
canEdit: true,
jumpTargetIndex: -1,
enableInteraction: true,
developerMode: false,
canFlash: true,
renderMentioned: true,
inlineAttachmentMedia: false,
inlineEmbedMedia: false,
renderEmbeds: false,
renderReactions: true,
canAddReactions: false,
canChat: false,
canReport: false,
};
},
getInitialState() {
const {messages: [message]} = this.props;
return {
animatedAvatar: hasAnimatedAvatar(message.author),
animate: false,
isEditing: false,
};
},
componentWillReceiveProps(nextProps) {
const {messages: [message]} = nextProps;
const animatedAvatar = hasAnimatedAvatar(message.author);
if (animatedAvatar !== this.state.animatedAvatar) {
this.setState({animatedAvatar});
}
},
shouldComponentUpdate(nextProps, nextState) {
if (!shallowEqual(this.props, nextProps, ['messages']) || !shallowEqual(this.state, nextState)) {
return true;
}
const {messages} = this.props;
if (messages.length !== nextProps.messages.length) {
return true;
}
for (let i = 0; i < messages.length; i++) {
if (messages[i] !== nextProps.messages[i]) {
return true;
}
}
return false;
},
getMessage(index) {
return this.refs[index];
},
onToggleEditing(isEditing) {
this.setState({isEditing});
},
renderUserPopout(props) {
const {channel, messages: [message]} = this.props;
return <UserPopout {...props} user={message.author} guildId={channel.getGuildId()} channelId={channel.id} />;
},
render() {
const {channel, compact} = this.props;
const {animatedAvatar, animate} = this.state;
const author = this.props.messages[0].author;
const isLocalBot = author.isLocalBot();
const className = classNames({
'message-group': true,
'has-divider': this.props.hasDivider,
'hide-overflow': !this.state.isEditing,
'is-local-bot-message': isLocalBot,
compact,
});
const isSystemMessage = this.props.messages[0].type > MessageTypes.DEFAULT;
const canDelete = this.props.canManageMessages || author.id === UserStore.getCurrentUser().id;
const canPin = !isSystemMessage && (this.props.canManageMessages || channel.isPrivate());
const canEdit = !isSystemMessage && this.props.canEdit;
const canAddNewReactions = !isSystemMessage && (this.props.canAddNewReactions || channel.isPrivate());
const canAddReactions = !isSystemMessage && (this.props.canChat || channel.isPrivate());
const canReport = this.props.canReport && ReportUtils.canReportUser(author);
const messages = this.props.messages.map((message, i) => {
return (
<Message
key={message.id}
ref={i}
first={i === 0}
renderMentioned={this.props.renderMentioned}
jumpSequenceId={this.props.jumpTargetIndex === i ? this.props.jumpSequenceId : null}
canFlash={this.props.canFlash}
compact={compact}
channel={channel}
message={message}
inlineAttachmentMedia={this.props.inlineAttachmentMedia}
inlineEmbedMedia={this.props.inlineEmbedMedia}
renderEmbeds={this.props.renderEmbeds}
renderReactions={this.props.renderReactions}
canPin={canPin}
canDelete={canDelete}
onEdit={this.props.onEdit}
onToggleEditing={this.onToggleEditing}
onUpdate={this.props.onUpdate}
avatarSize={this.props.avatarSize}
popoutPosition={this.props.popoutPosition}
onContextMenu={this.handleContextMenu}
option={this.props.messageOption}
developerMode={this.props.developerMode}
canAddReactions={canAddReactions}
canAddNewReactions={canAddNewReactions}
canEdit={canEdit}
canReport={canReport}
/>
);
});
let avatar;
if (!compact) {
avatar = (
<Popout closeOnScroll={false} render={this.renderUserPopout} position={Popout.RIGHT}>
<Avatar user={author} onContextMenu={this.handleContextMenu} size={this.props.avatarSize} animate={animate} />
</Popout>
);
}
let clickWrapper;
if (this.props.onClickAnywhere || !this.props.enableInteraction) {
clickWrapper = (
<div
className={classNames('sink-interactions', {clickable: this.props.onClickAnywhere})}
onClick={this.props.onClickAnywhere}
/>
);
}
if (isSystemMessage) {
return (
<div className={className}>
<div className="comment">{messages}</div>
{clickWrapper}
</div>
);
}
let localBotMessage;
if (isLocalBot) {
localBotMessage = (
<div className="local-bot-message">
{i18n.Messages.ONLY_YOU_CAN_SEE_AND_DELETE_THESE.format({
count: messages.length,
handleDelete: this.handleDeleteMessageGroup,
})}
</div>
);
}
return (
<div
className={className}
onMouseEnter={animatedAvatar && this.onMouseEnter}
onMouseLeave={animatedAvatar && this.onMouseLeave}>
{avatar}
<div className="comment">{messages}{localBotMessage}</div>
{clickWrapper}
{this.props.groupOption && this.props.groupOption(this.props.messages)}
</div>
);
},
onMouseEnter() {
this.setState({animate: true});
},
onMouseLeave() {
this.setState({animate: false});
},
handleContextMenu(event) {
const {channel, messages: [nearestMessage]} = this.props;
const {author: user} = nearestMessage;
if (channel.isGroupDM()) {
ContextMenu.openContextMenu(event, props =>
<UserContextMenu
{...props}
type={ContextMenuTypes.USER_GROUP_DM}
user={user}
nearestMessage={nearestMessage}
channelId={channel.id}
/>
);
} else if (channel.isDM()) {
ContextMenu.openContextMenu(event, props =>
<UserContextMenu
{...props}
type={ContextMenuTypes.USER_PRIVATE_CHANNELS_MESSAGE}
user={user}
channelId={channel.id}
nearestMessage={nearestMessage}
selected={true}
/>
);
} else {
ContextMenu.openContextMenu(event, props =>
<UserContextMenu
{...props}
type={ContextMenuTypes.USER_CHANNEL_MESSAGE}
user={user}
channelId={channel.id}
nearestMessage={nearestMessage}
guildId={channel['guild_id']}
/>
);
}
},
handleDeleteMessageGroup() {
const {messages, channel} = this.props;
messages.forEach(message => MessageActionCreators.deleteMessage(channel.id, message.id, true));
},
});
export default MessageGroup;
// WEBPACK FOOTER //
// ./discord_app/components/MessageGroup.js